New directory
[bpt/emacs.git] / src / mac.c
1 /* Unix emulation routines for GNU Emacs on the Mac OS.
2 Copyright (C) 2000, 2001 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
20
21 /* Contributed by Andrew Choi (akochoi@mac.com). */
22
23 #include <config.h>
24
25 #include <stdio.h>
26 #include <errno.h>
27 #include <utime.h>
28 #include <dirent.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <string.h>
32 #include <pwd.h>
33 #include <sys/param.h>
34 #include <stdlib.h>
35 #include <fcntl.h>
36 #if __MWERKS__
37 #include <unistd.h>
38 #endif
39
40 #ifdef MAC_OSX
41 #undef mktime
42 #undef DEBUG
43 #undef free
44 #undef malloc
45 #undef realloc
46 #undef init_process
47 #include <Carbon/Carbon.h>
48 #undef free
49 #define free unexec_free
50 #undef malloc
51 #define malloc unexec_malloc
52 #undef realloc
53 #define realloc unexec_realloc
54 #undef init_process
55 #define init_process emacs_init_process
56 #else /* not MAC_OSX */
57 #include <Files.h>
58 #include <MacTypes.h>
59 #include <TextUtils.h>
60 #include <Folders.h>
61 #include <Resources.h>
62 #include <Aliases.h>
63 #include <FixMath.h>
64 #include <Timer.h>
65 #include <OSA.h>
66 #include <AppleScript.h>
67 #include <Scrap.h>
68 #endif /* not MAC_OSX */
69
70 #include "lisp.h"
71 #include "process.h"
72 #include "sysselect.h"
73 #include "systime.h"
74
75 Lisp_Object QCLIPBOARD;
76
77 /* An instance of the AppleScript component. */
78 static ComponentInstance as_scripting_component;
79 /* The single script context used for all script executions. */
80 static OSAID as_script_context;
81
82
83 /* When converting from Mac to Unix pathnames, /'s in folder names are
84 converted to :'s. This function, used in copying folder names,
85 performs a strncat and converts all character a to b in the copy of
86 the string s2 appended to the end of s1. */
87
88 void
89 string_cat_and_replace (char *s1, const char *s2, int n, char a, char b)
90 {
91 int l1 = strlen (s1);
92 int l2 = strlen (s2);
93 char *p = s1 + l1;
94 int i;
95
96 strncat (s1, s2, n);
97 for (i = 0; i < l2; i++)
98 {
99 if (*p == a)
100 *p = b;
101 p++;
102 }
103 }
104
105
106 /* Convert a Mac pathname to Posix form. A Mac full pathname is one
107 that does not begin with a ':' and contains at least one ':'. A Mac
108 full pathname causes a '/' to be prepended to the Posix pathname.
109 The algorithm for the rest of the pathname is as follows:
110 For each segment between two ':',
111 if it is non-null, copy as is and then add a '/' at the end,
112 otherwise, insert a "../" into the Posix pathname.
113 Returns 1 if successful; 0 if fails. */
114
115 int
116 mac_to_posix_pathname (const char *mfn, char *ufn, int ufnbuflen)
117 {
118 const char *p, *q, *pe;
119
120 strcpy (ufn, "");
121
122 if (*mfn == '\0')
123 return 1;
124
125 p = strchr (mfn, ':');
126 if (p != 0 && p != mfn) /* full pathname */
127 strcat (ufn, "/");
128
129 p = mfn;
130 if (*p == ':')
131 p++;
132
133 pe = mfn + strlen (mfn);
134 while (p < pe)
135 {
136 q = strchr (p, ':');
137 if (q)
138 {
139 if (q == p)
140 { /* two consecutive ':' */
141 if (strlen (ufn) + 3 >= ufnbuflen)
142 return 0;
143 strcat (ufn, "../");
144 }
145 else
146 {
147 if (strlen (ufn) + (q - p) + 1 >= ufnbuflen)
148 return 0;
149 string_cat_and_replace (ufn, p, q - p, '/', ':');
150 strcat (ufn, "/");
151 }
152 p = q + 1;
153 }
154 else
155 {
156 if (strlen (ufn) + (pe - p) >= ufnbuflen)
157 return 0;
158 string_cat_and_replace (ufn, p, pe - p, '/', ':');
159 /* no separator for last one */
160 p = pe;
161 }
162 }
163
164 return 1;
165 }
166
167
168 extern char *get_temp_dir_name ();
169
170
171 /* Convert a Posix pathname to Mac form. Approximately reverse of the
172 above in algorithm. */
173
174 int
175 posix_to_mac_pathname (const char *ufn, char *mfn, int mfnbuflen)
176 {
177 const char *p, *q, *pe;
178 char expanded_pathname[MAXPATHLEN+1];
179
180 strcpy (mfn, "");
181
182 if (*ufn == '\0')
183 return 1;
184
185 p = ufn;
186
187 /* Check for and handle volume names. Last comparison: strangely
188 somewhere "/.emacs" is passed. A temporary fix for now. */
189 if (*p == '/' && strchr (p+1, '/') == NULL && strcmp (p, "/.emacs") != 0)
190 {
191 if (strlen (p) + 1 > mfnbuflen)
192 return 0;
193 strcpy (mfn, p+1);
194 strcat (mfn, ":");
195 return 1;
196 }
197
198 /* expand to emacs dir found by init_emacs_passwd_dir */
199 if (strncmp (p, "~emacs/", 7) == 0)
200 {
201 struct passwd *pw = getpwnam ("emacs");
202 p += 7;
203 if (strlen (pw->pw_dir) + strlen (p) > MAXPATHLEN)
204 return 0;
205 strcpy (expanded_pathname, pw->pw_dir);
206 strcat (expanded_pathname, p);
207 p = expanded_pathname;
208 /* now p points to the pathname with emacs dir prefix */
209 }
210 else if (strncmp (p, "/tmp/", 5) == 0)
211 {
212 char *t = get_temp_dir_name ();
213 p += 5;
214 if (strlen (t) + strlen (p) > MAXPATHLEN)
215 return 0;
216 strcpy (expanded_pathname, t);
217 strcat (expanded_pathname, p);
218 p = expanded_pathname;
219 /* now p points to the pathname with emacs dir prefix */
220 }
221 else if (*p != '/') /* relative pathname */
222 strcat (mfn, ":");
223
224 if (*p == '/')
225 p++;
226
227 pe = p + strlen (p);
228 while (p < pe)
229 {
230 q = strchr (p, '/');
231 if (q)
232 {
233 if (q - p == 2 && *p == '.' && *(p+1) == '.')
234 {
235 if (strlen (mfn) + 1 >= mfnbuflen)
236 return 0;
237 strcat (mfn, ":");
238 }
239 else
240 {
241 if (strlen (mfn) + (q - p) + 1 >= mfnbuflen)
242 return 0;
243 string_cat_and_replace (mfn, p, q - p, ':', '/');
244 strcat (mfn, ":");
245 }
246 p = q + 1;
247 }
248 else
249 {
250 if (strlen (mfn) + (pe - p) >= mfnbuflen)
251 return 0;
252 string_cat_and_replace (mfn, p, pe - p, ':', '/');
253 p = pe;
254 }
255 }
256
257 return 1;
258 }
259
260 #ifndef MAC_OSX
261
262 /* The following functions with "sys_" prefix are stubs to Unix
263 functions that have already been implemented by CW or MPW. The
264 calls to them in Emacs source course are #define'd to call the sys_
265 versions by the header files s-mac.h. In these stubs pathnames are
266 converted between their Unix and Mac forms. */
267
268
269 /* Unix epoch is Jan 1, 1970 while Mac epoch is Jan 1, 1904: 66 years
270 + 17 leap days. These are for adjusting time values returned by
271 MacOS Toolbox functions. */
272
273 #define MAC_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
274
275 #ifdef __MWERKS__
276 #if __MSL__ < 0x6000
277 /* CW Pro 5 epoch is Jan 1, 1900 (aaarghhhhh!); remember, 1900 is not
278 a leap year! This is for adjusting time_t values returned by MSL
279 functions. */
280 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 70 + 17) * 24 * 60 * 60)
281 #else /* __MSL__ >= 0x6000 */
282 /* CW changes Pro 6 to follow Unix! */
283 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
284 #endif /* __MSL__ >= 0x6000 */
285 #elif __MRC__
286 /* MPW library functions follow Unix (confused?). */
287 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
288 #else /* not __MRC__ */
289 You lose!!!
290 #endif /* not __MRC__ */
291
292
293 /* Define our own stat function for both MrC and CW. The reason for
294 doing this: "stat" is both the name of a struct and function name:
295 can't use the same trick like that for sys_open, sys_close, etc. to
296 redirect Emacs's calls to our own version that converts Unix style
297 filenames to Mac style filename because all sorts of compilation
298 errors will be generated if stat is #define'd to be sys_stat. */
299
300 int
301 stat_noalias (const char *path, struct stat *buf)
302 {
303 char mac_pathname[MAXPATHLEN+1];
304 CInfoPBRec cipb;
305
306 if (posix_to_mac_pathname (path, mac_pathname, MAXPATHLEN+1) == 0)
307 return -1;
308
309 c2pstr (mac_pathname);
310 cipb.hFileInfo.ioNamePtr = mac_pathname;
311 cipb.hFileInfo.ioVRefNum = 0;
312 cipb.hFileInfo.ioDirID = 0;
313 cipb.hFileInfo.ioFDirIndex = 0;
314 /* set to 0 to get information about specific dir or file */
315
316 errno = PBGetCatInfo (&cipb, false);
317 if (errno == -43) /* -43: fnfErr defined in Errors.h */
318 errno = ENOENT;
319 if (errno != noErr)
320 return -1;
321
322 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* bit 4 = 1 for directories */
323 {
324 buf->st_mode = S_IFDIR | S_IREAD | S_IEXEC;
325
326 if (!(cipb.hFileInfo.ioFlAttrib & 0x1))
327 buf->st_mode |= S_IWRITE; /* bit 1 = 1 for locked files/directories */
328 buf->st_ino = cipb.dirInfo.ioDrDirID;
329 buf->st_dev = cipb.dirInfo.ioVRefNum;
330 buf->st_size = cipb.dirInfo.ioDrNmFls;
331 /* size of dir = number of files and dirs */
332 buf->st_atime
333 = buf->st_mtime
334 = cipb.dirInfo.ioDrMdDat - MAC_UNIX_EPOCH_DIFF;
335 buf->st_ctime = cipb.dirInfo.ioDrCrDat - MAC_UNIX_EPOCH_DIFF;
336 }
337 else
338 {
339 buf->st_mode = S_IFREG | S_IREAD;
340 if (!(cipb.hFileInfo.ioFlAttrib & 0x1))
341 buf->st_mode |= S_IWRITE; /* bit 1 = 1 for locked files/directories */
342 if (cipb.hFileInfo.ioFlFndrInfo.fdType == 'APPL')
343 buf->st_mode |= S_IEXEC;
344 buf->st_ino = cipb.hFileInfo.ioDirID;
345 buf->st_dev = cipb.hFileInfo.ioVRefNum;
346 buf->st_size = cipb.hFileInfo.ioFlLgLen;
347 buf->st_atime
348 = buf->st_mtime
349 = cipb.hFileInfo.ioFlMdDat - MAC_UNIX_EPOCH_DIFF;
350 buf->st_ctime = cipb.hFileInfo.ioFlCrDat - MAC_UNIX_EPOCH_DIFF;
351 }
352
353 if (cipb.hFileInfo.ioFlFndrInfo.fdFlags & 0x8000)
354 {
355 /* identify alias files as symlinks */
356 buf->st_mode &= ~S_IFREG;
357 buf->st_mode |= S_IFLNK;
358 }
359
360 buf->st_nlink = 1;
361 buf->st_uid = getuid ();
362 buf->st_gid = getgid ();
363 buf->st_rdev = 0;
364
365 return 0;
366 }
367
368
369 int
370 lstat (const char *path, struct stat *buf)
371 {
372 int result;
373 char true_pathname[MAXPATHLEN+1];
374
375 /* Try looking for the file without resolving aliases first. */
376 if ((result = stat_noalias (path, buf)) >= 0)
377 return result;
378
379 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
380 return -1;
381
382 return stat_noalias (true_pathname, buf);
383 }
384
385
386 int
387 stat (const char *path, struct stat *sb)
388 {
389 int result;
390 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
391 int len;
392
393 if ((result = stat_noalias (path, sb)) >= 0 &&
394 ! (sb->st_mode & S_IFLNK))
395 return result;
396
397 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
398 return -1;
399
400 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
401 if (len > -1)
402 {
403 fully_resolved_name[len] = '\0';
404 /* in fact our readlink terminates strings */
405 return lstat (fully_resolved_name, sb);
406 }
407 else
408 return lstat (true_pathname, sb);
409 }
410
411
412 #if __MRC__
413 /* CW defines fstat in stat.mac.c while MPW does not provide this
414 function. Without the information of how to get from a file
415 descriptor in MPW StdCLib to a Mac OS file spec, it should be hard
416 to implement this function. Fortunately, there is only one place
417 where this function is called in our configuration: in fileio.c,
418 where only the st_dev and st_ino fields are used to determine
419 whether two fildes point to different i-nodes to prevent copying
420 a file onto itself equal. What we have here probably needs
421 improvement. */
422
423 int
424 fstat (int fildes, struct stat *buf)
425 {
426 buf->st_dev = 0;
427 buf->st_ino = fildes;
428 buf->st_mode = S_IFREG; /* added by T.I. for the copy-file */
429 return 0; /* success */
430 }
431 #endif /* __MRC__ */
432
433
434 int
435 mkdir (const char *dirname, int mode)
436 {
437 #pragma unused(mode)
438
439 HFileParam hfpb;
440 char true_pathname[MAXPATHLEN+1], mac_pathname[MAXPATHLEN+1];
441
442 if (find_true_pathname (dirname, true_pathname, MAXPATHLEN+1) == -1)
443 return -1;
444
445 if (posix_to_mac_pathname (true_pathname, mac_pathname, MAXPATHLEN+1) == 0)
446 return -1;
447
448 c2pstr (mac_pathname);
449 hfpb.ioNamePtr = mac_pathname;
450 hfpb.ioVRefNum = 0; /* ignored unless name is invalid */
451 hfpb.ioDirID = 0; /* parent is the root */
452
453 errno = PBDirCreate ((HParmBlkPtr) &hfpb, false);
454 /* just return the Mac OSErr code for now */
455 return errno == noErr ? 0 : -1;
456 }
457
458
459 #undef rmdir
460 sys_rmdir (const char *dirname)
461 {
462 HFileParam hfpb;
463 char mac_pathname[MAXPATHLEN+1];
464
465 if (posix_to_mac_pathname (dirname, mac_pathname, MAXPATHLEN+1) == 0)
466 return -1;
467
468 c2pstr (mac_pathname);
469 hfpb.ioNamePtr = mac_pathname;
470 hfpb.ioVRefNum = 0; /* ignored unless name is invalid */
471 hfpb.ioDirID = 0; /* parent is the root */
472
473 errno = PBHDelete ((HParmBlkPtr) &hfpb, false);
474 return errno == noErr ? 0 : -1;
475 }
476
477
478 #ifdef __MRC__
479 /* No implementation yet. */
480 int
481 execvp (const char *path, ...)
482 {
483 return -1;
484 }
485 #endif /* __MRC__ */
486
487
488 int
489 utime (const char *path, const struct utimbuf *times)
490 {
491 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
492 int len;
493 char mac_pathname[MAXPATHLEN+1];
494 CInfoPBRec cipb;
495
496 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
497 return -1;
498
499 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
500 if (len > -1)
501 fully_resolved_name[len] = '\0';
502 else
503 strcpy (fully_resolved_name, true_pathname);
504
505 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
506 return -1;
507
508 c2pstr (mac_pathname);
509 cipb.hFileInfo.ioNamePtr = mac_pathname;
510 cipb.hFileInfo.ioVRefNum = 0;
511 cipb.hFileInfo.ioDirID = 0;
512 cipb.hFileInfo.ioFDirIndex = 0;
513 /* set to 0 to get information about specific dir or file */
514
515 errno = PBGetCatInfo (&cipb, false);
516 if (errno != noErr)
517 return -1;
518
519 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* bit 4 = 1 for directories */
520 {
521 if (times)
522 cipb.dirInfo.ioDrMdDat = times->modtime + MAC_UNIX_EPOCH_DIFF;
523 else
524 GetDateTime (&cipb.dirInfo.ioDrMdDat);
525 }
526 else
527 {
528 if (times)
529 cipb.hFileInfo.ioFlMdDat = times->modtime + MAC_UNIX_EPOCH_DIFF;
530 else
531 GetDateTime (&cipb.hFileInfo.ioFlMdDat);
532 }
533
534 errno = PBSetCatInfo (&cipb, false);
535 return errno == noErr ? 0 : -1;
536 }
537
538
539 #ifndef F_OK
540 #define F_OK 0
541 #endif
542 #ifndef X_OK
543 #define X_OK 1
544 #endif
545 #ifndef W_OK
546 #define W_OK 2
547 #endif
548
549 /* Like stat, but test for access mode in hfpb.ioFlAttrib */
550 int
551 access (const char *path, int mode)
552 {
553 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
554 int len;
555 char mac_pathname[MAXPATHLEN+1];
556 CInfoPBRec cipb;
557
558 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
559 return -1;
560
561 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
562 if (len > -1)
563 fully_resolved_name[len] = '\0';
564 else
565 strcpy (fully_resolved_name, true_pathname);
566
567 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
568 return -1;
569
570 c2pstr (mac_pathname);
571 cipb.hFileInfo.ioNamePtr = mac_pathname;
572 cipb.hFileInfo.ioVRefNum = 0;
573 cipb.hFileInfo.ioDirID = 0;
574 cipb.hFileInfo.ioFDirIndex = 0;
575 /* set to 0 to get information about specific dir or file */
576
577 errno = PBGetCatInfo (&cipb, false);
578 if (errno != noErr)
579 return -1;
580
581 if (mode == F_OK) /* got this far, file exists */
582 return 0;
583
584 if (mode & X_OK)
585 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* path refers to a directory */
586 return 0;
587 else
588 {
589 if (cipb.hFileInfo.ioFlFndrInfo.fdType == 'APPL')
590 return 0;
591 else
592 return -1;
593 }
594
595 if (mode & W_OK)
596 return (cipb.hFileInfo.ioFlAttrib & 0x1) ? -1 : 0;
597 /* don't allow if lock bit is on */
598
599 return -1;
600 }
601
602
603 #define DEV_NULL_FD 0x10000
604
605 #undef open
606 int
607 sys_open (const char *path, int oflag)
608 {
609 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
610 int len;
611 char mac_pathname[MAXPATHLEN+1];
612
613 if (strcmp (path, "/dev/null") == 0)
614 return DEV_NULL_FD; /* some bogus fd to be ignored in write */
615
616 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
617 return -1;
618
619 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
620 if (len > -1)
621 fully_resolved_name[len] = '\0';
622 else
623 strcpy (fully_resolved_name, true_pathname);
624
625 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
626 return -1;
627 else
628 {
629 #ifdef __MRC__
630 int res = open (mac_pathname, oflag);
631 /* if (oflag == O_WRONLY || oflag == O_RDWR) */
632 if (oflag & O_CREAT)
633 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
634 return res;
635 #else /* not __MRC__ */
636 return open (mac_pathname, oflag);
637 #endif /* not __MRC__ */
638 }
639 }
640
641
642 #undef creat
643 int
644 sys_creat (const char *path, mode_t mode)
645 {
646 char true_pathname[MAXPATHLEN+1];
647 int len;
648 char mac_pathname[MAXPATHLEN+1];
649
650 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
651 return -1;
652
653 if (!posix_to_mac_pathname (true_pathname, mac_pathname, MAXPATHLEN+1))
654 return -1;
655 else
656 {
657 #ifdef __MRC__
658 int result = creat (mac_pathname);
659 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
660 return result;
661 #else /* not __MRC__ */
662 return creat (mac_pathname, mode);
663 #endif /* not __MRC__ */
664 }
665 }
666
667
668 #undef unlink
669 int
670 sys_unlink (const char *path)
671 {
672 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
673 int len;
674 char mac_pathname[MAXPATHLEN+1];
675
676 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
677 return -1;
678
679 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
680 if (len > -1)
681 fully_resolved_name[len] = '\0';
682 else
683 strcpy (fully_resolved_name, true_pathname);
684
685 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
686 return -1;
687 else
688 return unlink (mac_pathname);
689 }
690
691
692 #undef read
693 int
694 sys_read (int fildes, char *buf, int count)
695 {
696 if (fildes == 0) /* this should not be used for console input */
697 return -1;
698 else
699 #if __MSL__ >= 0x6000
700 return _read (fildes, buf, count);
701 #else
702 return read (fildes, buf, count);
703 #endif
704 }
705
706
707 #undef write
708 int
709 sys_write (int fildes, const char *buf, int count)
710 {
711 if (fildes == DEV_NULL_FD)
712 return count;
713 else
714 #if __MSL__ >= 0x6000
715 return _write (fildes, buf, count);
716 #else
717 return write (fildes, buf, count);
718 #endif
719 }
720
721
722 #undef rename
723 int
724 sys_rename (const char * old_name, const char * new_name)
725 {
726 char true_old_pathname[MAXPATHLEN+1], true_new_pathname[MAXPATHLEN+1];
727 char fully_resolved_old_name[MAXPATHLEN+1];
728 int len;
729 char mac_old_name[MAXPATHLEN+1], mac_new_name[MAXPATHLEN+1];
730
731 if (find_true_pathname (old_name, true_old_pathname, MAXPATHLEN+1) == -1)
732 return -1;
733
734 len = readlink (true_old_pathname, fully_resolved_old_name, MAXPATHLEN);
735 if (len > -1)
736 fully_resolved_old_name[len] = '\0';
737 else
738 strcpy (fully_resolved_old_name, true_old_pathname);
739
740 if (find_true_pathname (new_name, true_new_pathname, MAXPATHLEN+1) == -1)
741 return -1;
742
743 if (strcmp (fully_resolved_old_name, true_new_pathname) == 0)
744 return 0;
745
746 if (!posix_to_mac_pathname (fully_resolved_old_name,
747 mac_old_name,
748 MAXPATHLEN+1))
749 return -1;
750
751 if (!posix_to_mac_pathname(true_new_pathname, mac_new_name, MAXPATHLEN+1))
752 return -1;
753
754 /* If a file with new_name already exists, rename deletes the old
755 file in Unix. CW version fails in these situation. So we add a
756 call to unlink here. */
757 (void) unlink (mac_new_name);
758
759 return rename (mac_old_name, mac_new_name);
760 }
761
762
763 #undef fopen
764 extern FILE *fopen (const char *name, const char *mode);
765 FILE *
766 sys_fopen (const char *name, const char *mode)
767 {
768 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
769 int len;
770 char mac_pathname[MAXPATHLEN+1];
771
772 if (find_true_pathname (name, true_pathname, MAXPATHLEN+1) == -1)
773 return 0;
774
775 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
776 if (len > -1)
777 fully_resolved_name[len] = '\0';
778 else
779 strcpy (fully_resolved_name, true_pathname);
780
781 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
782 return 0;
783 else
784 {
785 #ifdef __MRC__
786 if (mode[0] == 'w' || mode[0] == 'a')
787 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
788 #endif /* not __MRC__ */
789 return fopen (mac_pathname, mode);
790 }
791 }
792
793
794 #include <Events.h>
795
796 long target_ticks = 0;
797
798 #ifdef __MRC__
799 __sigfun alarm_signal_func = (__sigfun) 0;
800 #elif __MWERKS__
801 __signal_func_ptr alarm_signal_func = (__signal_func_ptr) 0;
802 #else /* not __MRC__ and not __MWERKS__ */
803 You lose!!!
804 #endif /* not __MRC__ and not __MWERKS__ */
805
806
807 /* These functions simulate SIG_ALRM. The stub for function signal
808 stores the signal handler function in alarm_signal_func if a
809 SIG_ALRM is encountered. check_alarm is called in XTread_socket,
810 which emacs calls periodically. A pending alarm is represented by
811 a non-zero target_ticks value. check_alarm calls the handler
812 function pointed to by alarm_signal_func if one has been set up and
813 an alarm is pending. */
814
815 void
816 check_alarm ()
817 {
818 if (target_ticks && TickCount () > target_ticks)
819 {
820 target_ticks = 0;
821 if (alarm_signal_func)
822 (*alarm_signal_func)(SIGALRM);
823 }
824 }
825
826
827 int
828 select (n, rfds, wfds, efds, timeout)
829 int n;
830 SELECT_TYPE *rfds;
831 SELECT_TYPE *wfds;
832 SELECT_TYPE *efds;
833 struct timeval *timeout;
834 {
835 #ifdef TARGET_API_MAC_CARBON
836 return 1;
837 #else /* not TARGET_API_MAC_CARBON */
838 EMACS_TIME end_time, now;
839 EventRecord e;
840
841 /* Can only handle wait for keyboard input. */
842 if (n > 1 || wfds || efds)
843 return -1;
844
845 EMACS_GET_TIME (end_time);
846 EMACS_ADD_TIME (end_time, end_time, *timeout);
847
848 do
849 {
850 /* Also return true if an event other than a keyDown has
851 occurred. This causes kbd_buffer_get_event in keyboard.c to
852 call read_avail_input which in turn calls XTread_socket to
853 poll for these events. Otherwise these never get processed
854 except but a very slow poll timer. */
855 if (FD_ISSET (0, rfds) && EventAvail (everyEvent, &e))
856 return 1;
857
858 /* Also check movement of the mouse. */
859 {
860 Point mouse_pos;
861 static Point old_mouse_pos = {-1, -1};
862
863 GetMouse (&mouse_pos);
864 if (!EqualPt (mouse_pos, old_mouse_pos))
865 {
866 old_mouse_pos = mouse_pos;
867 return 1;
868 }
869 }
870
871 WaitNextEvent (0, &e, 1UL, NULL); /* Accept no event; wait 1
872 tic. by T.I. */
873
874 EMACS_GET_TIME (now);
875 EMACS_SUB_TIME (now, end_time, now);
876 }
877 while (!EMACS_TIME_NEG_P (now));
878
879 return 0;
880 #endif /* not TARGET_API_MAC_CARBON */
881 }
882
883
884 /* Called in sys_select to wait for an alarm signal to arrive. */
885
886 int
887 pause ()
888 {
889 EventRecord e;
890 unsigned long tick;
891
892 if (!target_ticks) /* no alarm pending */
893 return -1;
894
895 if ((tick = TickCount ()) < target_ticks)
896 WaitNextEvent (0, &e, target_ticks - tick, NULL); /* Accept no event;
897 just wait. by T.I. */
898
899 target_ticks = 0;
900 if (alarm_signal_func)
901 (*alarm_signal_func)(SIGALRM);
902
903 return 0;
904 }
905
906
907 int
908 alarm (int seconds)
909 {
910 long remaining = target_ticks ? (TickCount () - target_ticks) / 60 : 0;
911
912 target_ticks = seconds ? TickCount () + 60 * seconds : 0;
913
914 return (remaining < 0) ? 0 : (unsigned int) remaining;
915 }
916
917
918 #undef signal
919 #ifdef __MRC__
920 extern __sigfun signal (int signal, __sigfun signal_func);
921 __sigfun
922 sys_signal (int signal_num, __sigfun signal_func)
923 #elif __MWERKS__
924 extern __signal_func_ptr signal (int signal, __signal_func_ptr signal_func);
925 __signal_func_ptr
926 sys_signal (int signal_num, __signal_func_ptr signal_func)
927 #else /* not __MRC__ and not __MWERKS__ */
928 You lose!!!
929 #endif /* not __MRC__ and not __MWERKS__ */
930 {
931 if (signal_num != SIGALRM)
932 return signal (signal_num, signal_func);
933 else
934 {
935 #ifdef __MRC__
936 __sigfun old_signal_func;
937 #elif __MWERKS__
938 __signal_func_ptr old_signal_func;
939 #else
940 You lose!!!
941 #endif
942 old_signal_func = alarm_signal_func;
943 alarm_signal_func = signal_func;
944 return old_signal_func;
945 }
946 }
947
948
949 /* gettimeofday should return the amount of time (in a timeval
950 structure) since midnight today. The toolbox function Microseconds
951 returns the number of microseconds (in a UnsignedWide value) since
952 the machine was booted. Also making this complicated is WideAdd,
953 WideSubtract, etc. take wide values. */
954
955 int
956 gettimeofday (tp)
957 struct timeval *tp;
958 {
959 static inited = 0;
960 static wide wall_clock_at_epoch, clicks_at_epoch;
961 UnsignedWide uw_microseconds;
962 wide w_microseconds;
963 time_t sys_time (time_t *);
964
965 /* If this function is called for the first time, record the number
966 of seconds since midnight and the number of microseconds since
967 boot at the time of this first call. */
968 if (!inited)
969 {
970 time_t systime;
971 inited = 1;
972 systime = sys_time (NULL);
973 /* Store microseconds since midnight in wall_clock_at_epoch. */
974 WideMultiply (systime, 1000000L, &wall_clock_at_epoch);
975 Microseconds (&uw_microseconds);
976 /* Store microseconds since boot in clicks_at_epoch. */
977 clicks_at_epoch.hi = uw_microseconds.hi;
978 clicks_at_epoch.lo = uw_microseconds.lo;
979 }
980
981 /* Get time since boot */
982 Microseconds (&uw_microseconds);
983
984 /* Convert to time since midnight*/
985 w_microseconds.hi = uw_microseconds.hi;
986 w_microseconds.lo = uw_microseconds.lo;
987 WideSubtract (&w_microseconds, &clicks_at_epoch);
988 WideAdd (&w_microseconds, &wall_clock_at_epoch);
989 tp->tv_sec = WideDivide (&w_microseconds, 1000000L, &tp->tv_usec);
990
991 return 0;
992 }
993
994
995 #ifdef __MRC__
996 unsigned int
997 sleep (unsigned int seconds)
998 {
999 unsigned long time_up;
1000 EventRecord e;
1001
1002 time_up = TickCount () + seconds * 60;
1003 while (TickCount () < time_up)
1004 {
1005 /* Accept no event; just wait. by T.I. */
1006 WaitNextEvent (0, &e, 30, NULL);
1007 }
1008
1009 return (0);
1010 }
1011 #endif /* __MRC__ */
1012
1013
1014 /* The time functions adjust time values according to the difference
1015 between the Unix and CW epoches. */
1016
1017 #undef gmtime
1018 extern struct tm *gmtime (const time_t *);
1019 struct tm *
1020 sys_gmtime (const time_t *timer)
1021 {
1022 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
1023
1024 return gmtime (&unix_time);
1025 }
1026
1027
1028 #undef localtime
1029 extern struct tm *localtime (const time_t *);
1030 struct tm *
1031 sys_localtime (const time_t *timer)
1032 {
1033 #if __MSL__ >= 0x6000
1034 time_t unix_time = *timer;
1035 #else
1036 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
1037 #endif
1038
1039 return localtime (&unix_time);
1040 }
1041
1042
1043 #undef ctime
1044 extern char *ctime (const time_t *);
1045 char *
1046 sys_ctime (const time_t *timer)
1047 {
1048 #if __MSL__ >= 0x6000
1049 time_t unix_time = *timer;
1050 #else
1051 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
1052 #endif
1053
1054 return ctime (&unix_time);
1055 }
1056
1057
1058 #undef time
1059 extern time_t time (time_t *);
1060 time_t
1061 sys_time (time_t *timer)
1062 {
1063 #if __MSL__ >= 0x6000
1064 time_t mac_time = time (NULL);
1065 #else
1066 time_t mac_time = time (NULL) - CW_OR_MPW_UNIX_EPOCH_DIFF;
1067 #endif
1068
1069 if (timer)
1070 *timer = mac_time;
1071
1072 return mac_time;
1073 }
1074
1075
1076 /* MPW strftime broken for "%p" format */
1077 #ifdef __MRC__
1078 #undef strftime
1079 #include <time.h>
1080 size_t
1081 sys_strftime (char * s, size_t maxsize, const char * format,
1082 const struct tm * timeptr)
1083 {
1084 if (strcmp (format, "%p") == 0)
1085 {
1086 if (maxsize < 3)
1087 return 0;
1088 if (timeptr->tm_hour < 12)
1089 {
1090 strcpy (s, "AM");
1091 return 2;
1092 }
1093 else
1094 {
1095 strcpy (s, "PM");
1096 return 2;
1097 }
1098 }
1099 else
1100 return strftime (s, maxsize, format, timeptr);
1101 }
1102 #endif /* __MRC__ */
1103
1104
1105 /* no subprocesses, empty wait */
1106
1107 int
1108 wait (int pid)
1109 {
1110 return 0;
1111 }
1112
1113
1114 void
1115 croak (char *badfunc)
1116 {
1117 printf ("%s not yet implemented\r\n", badfunc);
1118 exit (1);
1119 }
1120
1121
1122 char *
1123 index (const char * str, int chr)
1124 {
1125 return strchr (str, chr);
1126 }
1127
1128
1129 char *
1130 mktemp (char *template)
1131 {
1132 int len, k;
1133 static seqnum = 0;
1134
1135 len = strlen (template);
1136 k = len - 1;
1137 while (k >= 0 && template[k] == 'X')
1138 k--;
1139
1140 k++; /* make k index of first 'X' */
1141
1142 if (k < len)
1143 {
1144 /* Zero filled, number of digits equal to the number of X's. */
1145 sprintf (&template[k], "%0*d", len-k, seqnum++);
1146
1147 return template;
1148 }
1149 else
1150 return 0;
1151 }
1152
1153
1154 /* Emulate getpwuid, getpwnam and others. */
1155
1156 #define PASSWD_FIELD_SIZE 256
1157
1158 static char my_passwd_name[PASSWD_FIELD_SIZE];
1159 static char my_passwd_dir[MAXPATHLEN+1];
1160
1161 static struct passwd my_passwd =
1162 {
1163 my_passwd_name,
1164 my_passwd_dir,
1165 };
1166
1167
1168 /* Initialized by main () in macterm.c to pathname of emacs directory. */
1169
1170 char emacs_passwd_dir[MAXPATHLEN+1];
1171
1172 char *
1173 getwd (char *);
1174
1175 void
1176 init_emacs_passwd_dir ()
1177 {
1178 int found = false;
1179
1180 if (getwd (emacs_passwd_dir) && getwd (my_passwd_dir))
1181 {
1182 /* Need pathname of first ancestor that begins with "emacs"
1183 since Mac emacs application is somewhere in the emacs-*
1184 tree. */
1185 int len = strlen (emacs_passwd_dir);
1186 int j = len - 1;
1187 /* j points to the "/" following the directory name being
1188 compared. */
1189 int i = j - 1;
1190 while (i >= 0 && !found)
1191 {
1192 while (i >= 0 && emacs_passwd_dir[i] != '/')
1193 i--;
1194 if (emacs_passwd_dir[i] == '/' && i+5 < len)
1195 found = (strncmp (&(emacs_passwd_dir[i+1]), "emacs", 5) == 0);
1196 if (found)
1197 emacs_passwd_dir[j+1] = '\0';
1198 else
1199 {
1200 j = i;
1201 i = j - 1;
1202 }
1203 }
1204 }
1205
1206 if (!found)
1207 {
1208 /* Setting to "/" probably won't work but set it to something
1209 anyway. */
1210 strcpy (emacs_passwd_dir, "/");
1211 strcpy (my_passwd_dir, "/");
1212 }
1213 }
1214
1215
1216 static struct passwd emacs_passwd =
1217 {
1218 "emacs",
1219 emacs_passwd_dir,
1220 };
1221
1222 static int my_passwd_inited = 0;
1223
1224
1225 static void
1226 init_my_passwd ()
1227 {
1228 char **owner_name;
1229
1230 /* Note: my_passwd_dir initialized in int_emacs_passwd_dir to
1231 directory where Emacs was started. */
1232
1233 owner_name = (char **) GetResource ('STR ',-16096);
1234 if (owner_name)
1235 {
1236 HLock (owner_name);
1237 BlockMove ((unsigned char *) *owner_name,
1238 (unsigned char *) my_passwd_name,
1239 *owner_name[0]+1);
1240 HUnlock (owner_name);
1241 p2cstr ((unsigned char *) my_passwd_name);
1242 }
1243 else
1244 my_passwd_name[0] = 0;
1245 }
1246
1247
1248 struct passwd *
1249 getpwuid (uid_t uid)
1250 {
1251 if (!my_passwd_inited)
1252 {
1253 init_my_passwd ();
1254 my_passwd_inited = 1;
1255 }
1256
1257 return &my_passwd;
1258 }
1259
1260
1261 struct passwd *
1262 getpwnam (const char *name)
1263 {
1264 if (strcmp (name, "emacs") == 0)
1265 return &emacs_passwd;
1266
1267 if (!my_passwd_inited)
1268 {
1269 init_my_passwd ();
1270 my_passwd_inited = 1;
1271 }
1272
1273 return &my_passwd;
1274 }
1275
1276
1277 /* The functions fork, kill, sigsetmask, sigblock, request_sigio,
1278 setpgrp, setpriority, and unrequest_sigio are defined to be empty
1279 as in msdos.c. */
1280
1281
1282 int
1283 fork ()
1284 {
1285 return -1;
1286 }
1287
1288
1289 int
1290 kill (int x, int y)
1291 {
1292 return -1;
1293 }
1294
1295
1296 void
1297 sys_subshell ()
1298 {
1299 error ("Can't spawn subshell");
1300 }
1301
1302
1303 int
1304 sigsetmask (int x)
1305 {
1306 return 0;
1307 }
1308
1309
1310 int
1311 sigblock (int mask)
1312 {
1313 return 0;
1314 }
1315
1316
1317 void
1318 request_sigio (void)
1319 {
1320 }
1321
1322
1323 void
1324 unrequest_sigio (void)
1325 {
1326 }
1327
1328
1329 int
1330 setpgrp ()
1331 {
1332 return 0;
1333 }
1334
1335
1336 /* No pipes yet. */
1337
1338 int
1339 pipe (int _fildes[2])
1340 {
1341 errno = EACCES;
1342 return -1;
1343 }
1344
1345
1346 /* Hard and symbolic links. */
1347
1348 int
1349 symlink (const char *name1, const char *name2)
1350 {
1351 errno = ENOENT;
1352 return -1;
1353 }
1354
1355
1356 int
1357 link (const char *name1, const char *name2)
1358 {
1359 errno = ENOENT;
1360 return -1;
1361 }
1362
1363 #endif /* ! MAC_OSX */
1364
1365 /* Determine the path name of the file specified by VREFNUM, DIRID,
1366 and NAME and place that in the buffer PATH of length
1367 MAXPATHLEN. */
1368 int
1369 path_from_vol_dir_name (char *path, int man_path_len, short vol_ref_num,
1370 long dir_id, ConstStr255Param name)
1371 {
1372 Str255 dir_name;
1373 CInfoPBRec cipb;
1374 OSErr err;
1375
1376 if (strlen (name) > man_path_len)
1377 return 0;
1378
1379 memcpy (dir_name, name, name[0]+1);
1380 memcpy (path, name, name[0]+1);
1381 p2cstr (path);
1382
1383 cipb.dirInfo.ioDrParID = dir_id;
1384 cipb.dirInfo.ioNamePtr = dir_name;
1385
1386 do
1387 {
1388 cipb.dirInfo.ioVRefNum = vol_ref_num;
1389 cipb.dirInfo.ioFDirIndex = -1;
1390 cipb.dirInfo.ioDrDirID = cipb.dirInfo.ioDrParID;
1391 /* go up to parent each time */
1392
1393 err = PBGetCatInfo (&cipb, false);
1394 if (err != noErr)
1395 return 0;
1396
1397 p2cstr (dir_name);
1398 if (strlen (dir_name) + strlen (path) + 1 >= man_path_len)
1399 return 0;
1400
1401 strcat (dir_name, ":");
1402 strcat (dir_name, path);
1403 /* attach to front since we're going up directory tree */
1404 strcpy (path, dir_name);
1405 }
1406 while (cipb.dirInfo.ioDrDirID != fsRtDirID);
1407 /* stop when we see the volume's root directory */
1408
1409 return 1; /* success */
1410 }
1411
1412 #ifndef MAC_OSX
1413
1414 int
1415 readlink (const char *path, char *buf, int bufsiz)
1416 {
1417 char mac_sym_link_name[MAXPATHLEN+1];
1418 OSErr err;
1419 FSSpec fsspec;
1420 Boolean target_is_folder, was_aliased;
1421 Str255 directory_name, mac_pathname;
1422 CInfoPBRec cipb;
1423
1424 if (posix_to_mac_pathname (path, mac_sym_link_name, MAXPATHLEN+1) == 0)
1425 return -1;
1426
1427 c2pstr (mac_sym_link_name);
1428 err = FSMakeFSSpec (0, 0, mac_sym_link_name, &fsspec);
1429 if (err != noErr)
1430 {
1431 errno = ENOENT;
1432 return -1;
1433 }
1434
1435 err = ResolveAliasFile (&fsspec, true, &target_is_folder, &was_aliased);
1436 if (err != noErr || !was_aliased)
1437 {
1438 errno = ENOENT;
1439 return -1;
1440 }
1441
1442 if (path_from_vol_dir_name (mac_pathname, 255, fsspec.vRefNum, fsspec.parID,
1443 fsspec.name) == 0)
1444 {
1445 errno = ENOENT;
1446 return -1;
1447 }
1448
1449 if (mac_to_posix_pathname (mac_pathname, buf, bufsiz) == 0)
1450 {
1451 errno = ENOENT;
1452 return -1;
1453 }
1454
1455 return strlen (buf);
1456 }
1457
1458
1459 /* Convert a path to one with aliases fully expanded. */
1460
1461 static int
1462 find_true_pathname (const char *path, char *buf, int bufsiz)
1463 {
1464 char *q, temp[MAXPATHLEN+1];
1465 const char *p;
1466 int len;
1467
1468 if (bufsiz <= 0 || path == 0 || path[0] == '\0')
1469 return -1;
1470
1471 buf[0] = '\0';
1472
1473 p = path;
1474 if (*p == '/')
1475 q = strchr (p + 1, '/');
1476 else
1477 q = strchr (p, '/');
1478 len = 0; /* loop may not be entered, e.g., for "/" */
1479
1480 while (q)
1481 {
1482 strcpy (temp, buf);
1483 strncat (temp, p, q - p);
1484 len = readlink (temp, buf, bufsiz);
1485 if (len <= -1)
1486 {
1487 if (strlen (temp) + 1 > bufsiz)
1488 return -1;
1489 strcpy (buf, temp);
1490 }
1491 strcat (buf, "/");
1492 len++;
1493 p = q + 1;
1494 q = strchr(p, '/');
1495 }
1496
1497 if (len + strlen (p) + 1 >= bufsiz)
1498 return -1;
1499
1500 strcat (buf, p);
1501 return len + strlen (p);
1502 }
1503
1504
1505 mode_t
1506 umask (mode_t numask)
1507 {
1508 static mode_t mask = 022;
1509 mode_t oldmask = mask;
1510 mask = numask;
1511 return oldmask;
1512 }
1513
1514
1515 int
1516 chmod (const char *path, mode_t mode)
1517 {
1518 /* say it always succeed for now */
1519 return 0;
1520 }
1521
1522
1523 int
1524 dup (int oldd)
1525 {
1526 #ifdef __MRC__
1527 return fcntl (oldd, F_DUPFD, 0);
1528 #elif __MWERKS__
1529 /* current implementation of fcntl in fcntl.mac.c simply returns old
1530 descriptor */
1531 return fcntl (oldd, F_DUPFD);
1532 #else
1533 You lose!!!
1534 #endif
1535 }
1536
1537
1538 /* This is from the original sysdep.c. Emulate BSD dup2. First close
1539 newd if it already exists. Then, attempt to dup oldd. If not
1540 successful, call dup2 recursively until we are, then close the
1541 unsuccessful ones. */
1542
1543 int
1544 dup2 (int oldd, int newd)
1545 {
1546 int fd, ret;
1547
1548 close (newd);
1549
1550 fd = dup (oldd);
1551 if (fd == -1)
1552 return -1;
1553 if (fd == newd)
1554 return newd;
1555 ret = dup2 (oldd, newd);
1556 close (fd);
1557 return ret;
1558 }
1559
1560
1561 /* let it fail for now */
1562
1563 char *
1564 sbrk (int incr)
1565 {
1566 return (char *) -1;
1567 }
1568
1569
1570 int
1571 fsync (int fd)
1572 {
1573 return 0;
1574 }
1575
1576
1577 int
1578 ioctl (int d, int request, void *argp)
1579 {
1580 return -1;
1581 }
1582
1583
1584 #ifdef __MRC__
1585 int
1586 isatty (int fildes)
1587 {
1588 if (fildes >=0 && fildes <= 2)
1589 return 1;
1590 else
1591 return 0;
1592 }
1593
1594
1595 int
1596 getgid ()
1597 {
1598 return 100;
1599 }
1600
1601
1602 int
1603 getegid ()
1604 {
1605 return 100;
1606 }
1607
1608
1609 int
1610 getuid ()
1611 {
1612 return 200;
1613 }
1614
1615
1616 int
1617 geteuid ()
1618 {
1619 return 200;
1620 }
1621 #endif /* __MRC__ */
1622
1623
1624 #ifdef __MWERKS__
1625 #if __MSL__ < 0x6000
1626 #undef getpid
1627 int
1628 getpid ()
1629 {
1630 return 9999;
1631 }
1632 #endif
1633 #endif /* __MWERKS__ */
1634
1635 #endif /* ! MAC_OSX */
1636
1637
1638 /* Return the path to the directory in which Emacs can create
1639 temporary files. The MacOS "temporary items" directory cannot be
1640 used because it removes the file written by a process when it
1641 exits. In that sense it's more like "/dev/null" than "/tmp" (but
1642 again not exactly). And of course Emacs needs to read back the
1643 files written by its subprocesses. So here we write the files to a
1644 directory "Emacs" in the Preferences Folder. This directory is
1645 created if it does not exist. */
1646
1647 char *
1648 get_temp_dir_name ()
1649 {
1650 static char *temp_dir_name = NULL;
1651 short vol_ref_num;
1652 long dir_id;
1653 OSErr err;
1654 Str255 dir_name, full_path;
1655 CInfoPBRec cpb;
1656 char unix_dir_name[MAXPATHLEN+1];
1657 DIR *dir;
1658
1659 /* Cache directory name with pointer temp_dir_name.
1660 Look for it only the first time. */
1661 if (!temp_dir_name)
1662 {
1663 err = FindFolder (kOnSystemDisk, kPreferencesFolderType, kCreateFolder,
1664 &vol_ref_num, &dir_id);
1665 if (err != noErr)
1666 return NULL;
1667
1668 if (!path_from_vol_dir_name (full_path, 255, vol_ref_num, dir_id, "\p"))
1669 return NULL;
1670
1671 if (strlen (full_path) + 6 <= MAXPATHLEN)
1672 strcat (full_path, "Emacs:");
1673 else
1674 return NULL;
1675
1676 if (!mac_to_posix_pathname (full_path, unix_dir_name, MAXPATHLEN+1))
1677 return NULL;
1678
1679 dir = opendir (unix_dir_name); /* check whether temp directory exists */
1680 if (dir)
1681 closedir (dir);
1682 else if (mkdir (unix_dir_name, 0700) != 0) /* create it if not */
1683 return NULL;
1684
1685 temp_dir_name = (char *) malloc (strlen (unix_dir_name) + 1);
1686 strcpy (temp_dir_name, unix_dir_name);
1687 }
1688
1689 return temp_dir_name;
1690 }
1691
1692 #ifndef MAC_OSX
1693
1694 /* Allocate and construct an array of pointers to strings from a list
1695 of strings stored in a 'STR#' resource. The returned pointer array
1696 is stored in the style of argv and environ: if the 'STR#' resource
1697 contains numString strings, a pointer array with numString+1
1698 elements is returned in which the last entry contains a null
1699 pointer. The pointer to the pointer array is passed by pointer in
1700 parameter t. The resource ID of the 'STR#' resource is passed in
1701 parameter StringListID.
1702 */
1703
1704 void
1705 get_string_list (char ***t, short string_list_id)
1706 {
1707 Handle h;
1708 Ptr p;
1709 int i, num_strings;
1710
1711 h = GetResource ('STR#', string_list_id);
1712 if (h)
1713 {
1714 HLock (h);
1715 p = *h;
1716 num_strings = * (short *) p;
1717 p += sizeof(short);
1718 *t = (char **) malloc (sizeof (char *) * (num_strings + 1));
1719 for (i = 0; i < num_strings; i++)
1720 {
1721 short length = *p++;
1722 (*t)[i] = (char *) malloc (length + 1);
1723 strncpy ((*t)[i], p, length);
1724 (*t)[i][length] = '\0';
1725 p += length;
1726 }
1727 (*t)[num_strings] = 0;
1728 HUnlock (h);
1729 }
1730 else
1731 {
1732 /* Return no string in case GetResource fails. Bug fixed by
1733 Ikegami Tsutomu. Caused MPW build to crash without sym -on
1734 option (no sym -on implies -opt local). */
1735 *t = (char **) malloc (sizeof (char *));
1736 (*t)[0] = 0;
1737 }
1738 }
1739
1740
1741 static char *
1742 get_path_to_system_folder ()
1743 {
1744 short vol_ref_num;
1745 long dir_id;
1746 OSErr err;
1747 Str255 dir_name, full_path;
1748 CInfoPBRec cpb;
1749 static char system_folder_unix_name[MAXPATHLEN+1];
1750 DIR *dir;
1751
1752 err = FindFolder (kOnSystemDisk, kSystemFolderType, kDontCreateFolder,
1753 &vol_ref_num, &dir_id);
1754 if (err != noErr)
1755 return NULL;
1756
1757 if (!path_from_vol_dir_name (full_path, 255, vol_ref_num, dir_id, "\p"))
1758 return NULL;
1759
1760 if (!mac_to_posix_pathname (full_path, system_folder_unix_name,
1761 MAXPATHLEN+1))
1762 return NULL;
1763
1764 return system_folder_unix_name;
1765 }
1766
1767
1768 char **environ;
1769
1770 #define ENVIRON_STRING_LIST_ID 128
1771
1772 /* Get environment variable definitions from STR# resource. */
1773
1774 void
1775 init_environ ()
1776 {
1777 int i;
1778
1779 get_string_list (&environ, ENVIRON_STRING_LIST_ID);
1780
1781 i = 0;
1782 while (environ[i])
1783 i++;
1784
1785 /* Make HOME directory the one Emacs starts up in if not specified
1786 by resource. */
1787 if (getenv ("HOME") == NULL)
1788 {
1789 environ = (char **) realloc (environ, sizeof (char *) * (i + 2));
1790 if (environ)
1791 {
1792 environ[i] = (char *) malloc (strlen (my_passwd_dir) + 6);
1793 if (environ[i])
1794 {
1795 strcpy (environ[i], "HOME=");
1796 strcat (environ[i], my_passwd_dir);
1797 }
1798 environ[i+1] = 0;
1799 i++;
1800 }
1801 }
1802
1803 /* Make HOME directory the one Emacs starts up in if not specified
1804 by resource. */
1805 if (getenv ("MAIL") == NULL)
1806 {
1807 environ = (char **) realloc (environ, sizeof (char *) * (i + 2));
1808 if (environ)
1809 {
1810 char * path_to_system_folder = get_path_to_system_folder ();
1811 environ[i] = (char *) malloc (strlen (path_to_system_folder) + 22);
1812 if (environ[i])
1813 {
1814 strcpy (environ[i], "MAIL=");
1815 strcat (environ[i], path_to_system_folder);
1816 strcat (environ[i], "Eudora Folder/In");
1817 }
1818 environ[i+1] = 0;
1819 }
1820 }
1821 }
1822
1823
1824 /* Return the value of the environment variable NAME. */
1825
1826 char *
1827 getenv (const char *name)
1828 {
1829 int length = strlen(name);
1830 char **e;
1831
1832 for (e = environ; *e != 0; e++)
1833 if (strncmp(*e, name, length) == 0 && (*e)[length] == '=')
1834 return &(*e)[length + 1];
1835
1836 if (strcmp (name, "TMPDIR") == 0)
1837 return get_temp_dir_name ();
1838
1839 return 0;
1840 }
1841
1842
1843 #ifdef __MRC__
1844 /* see Interfaces&Libraries:Interfaces:CIncludes:signal.h */
1845 char *sys_siglist[] =
1846 {
1847 "Zero is not a signal!!!",
1848 "Abort", /* 1 */
1849 "Interactive user interrupt", /* 2 */ "?",
1850 "Floating point exception", /* 4 */ "?", "?", "?",
1851 "Illegal instruction", /* 8 */ "?", "?", "?", "?", "?", "?", "?",
1852 "Segment violation", /* 16 */ "?", "?", "?", "?", "?", "?", "?",
1853 "?", "?", "?", "?", "?", "?", "?", "?",
1854 "Terminal" /* 32 */
1855 };
1856 #elif __MWERKS__
1857 char *sys_siglist[] =
1858 {
1859 "Zero is not a signal!!!",
1860 "Abort",
1861 "Floating point exception",
1862 "Illegal instruction",
1863 "Interactive user interrupt",
1864 "Segment violation",
1865 "Terminal"
1866 };
1867 #else /* not __MRC__ and not __MWERKS__ */
1868 You lose!!!
1869 #endif /* not __MRC__ and not __MWERKS__ */
1870
1871
1872 #include <utsname.h>
1873
1874 int
1875 uname (struct utsname *name)
1876 {
1877 char **system_name;
1878 system_name = GetString (-16413); /* IM - Resource Manager Reference */
1879 if (system_name)
1880 {
1881 BlockMove (*system_name, name->nodename, (*system_name)[0]+1);
1882 p2cstr (name->nodename);
1883 return 0;
1884 }
1885 else
1886 return -1;
1887 }
1888
1889
1890 #include <Processes.h>
1891 #include <EPPC.h>
1892
1893 /* Event class of HLE sent to subprocess. */
1894 const OSType kEmacsSubprocessSend = 'ESND';
1895
1896 /* Event class of HLE sent back from subprocess. */
1897 const OSType kEmacsSubprocessReply = 'ERPY';
1898
1899
1900 char *
1901 mystrchr (char *s, char c)
1902 {
1903 while (*s && *s != c)
1904 {
1905 if (*s == '\\')
1906 s++;
1907 s++;
1908 }
1909
1910 if (*s)
1911 {
1912 *s = '\0';
1913 return s;
1914 }
1915 else
1916 return NULL;
1917 }
1918
1919
1920 char *
1921 mystrtok (char *s)
1922 {
1923 while (*s)
1924 s++;
1925
1926 return s + 1;
1927 }
1928
1929
1930 void
1931 mystrcpy (char *to, char *from)
1932 {
1933 while (*from)
1934 {
1935 if (*from == '\\')
1936 from++;
1937 *to++ = *from++;
1938 }
1939 *to = '\0';
1940 }
1941
1942
1943 /* Start a Mac subprocess. Arguments for it is passed in argv (null
1944 terminated). The process should run with the default directory
1945 "workdir", read input from "infn", and write output and error to
1946 "outfn" and "errfn", resp. The Process Manager call
1947 LaunchApplication is used to start the subprocess. We use high
1948 level events as the mechanism to pass arguments to the subprocess
1949 and to make Emacs wait for the subprocess to terminate and pass
1950 back a result code. The bulk of the code here packs the arguments
1951 into one message to be passed together with the high level event.
1952 Emacs also sometimes starts a subprocess using a shell to perform
1953 wildcard filename expansion. Since we don't really have a shell on
1954 the Mac, this case is detected and the starting of the shell is
1955 by-passed. We really need to add code here to do filename
1956 expansion to support such functionality. */
1957
1958 int
1959 run_mac_command (argv, workdir, infn, outfn, errfn)
1960 unsigned char **argv;
1961 const char *workdir;
1962 const char *infn, *outfn, *errfn;
1963 {
1964 #ifdef TARGET_API_MAC_CARBON
1965 return -1;
1966 #else /* not TARGET_API_MAC_CARBON */
1967 char macappname[MAXPATHLEN+1], macworkdir[MAXPATHLEN+1];
1968 char macinfn[MAXPATHLEN+1], macoutfn[MAXPATHLEN+1], macerrfn[MAXPATHLEN+1];
1969 int paramlen, argc, newargc, j, retries;
1970 char **newargv, *param, *p;
1971 OSErr iErr;
1972 FSSpec spec;
1973 LaunchParamBlockRec lpbr;
1974 EventRecord send_event, reply_event;
1975 RgnHandle cursor_region_handle;
1976 TargetID targ;
1977 unsigned long ref_con, len;
1978
1979 if (posix_to_mac_pathname (workdir, macworkdir, MAXPATHLEN+1) == 0)
1980 return -1;
1981 if (posix_to_mac_pathname (infn, macinfn, MAXPATHLEN+1) == 0)
1982 return -1;
1983 if (posix_to_mac_pathname (outfn, macoutfn, MAXPATHLEN+1) == 0)
1984 return -1;
1985 if (posix_to_mac_pathname (errfn, macerrfn, MAXPATHLEN+1) == 0)
1986 return -1;
1987
1988 paramlen = strlen (macworkdir) + strlen (macinfn) + strlen (macoutfn)
1989 + strlen (macerrfn) + 4; /* count nulls at end of strings */
1990
1991 argc = 0;
1992 while (argv[argc])
1993 argc++;
1994
1995 if (argc == 0)
1996 return -1;
1997
1998 /* If a subprocess is invoked with a shell, we receive 3 arguments
1999 of the form: "<path to emacs bins>/sh" "-c" "<path to emacs
2000 bins>/<command> <command args>" */
2001 j = strlen (argv[0]);
2002 if (j >= 3 && strcmp (argv[0]+j-3, "/sh") == 0
2003 && argc == 3 && strcmp (argv[1], "-c") == 0)
2004 {
2005 char *command, *t, tempmacpathname[MAXPATHLEN+1];
2006
2007 /* The arguments for the command in argv[2] are separated by
2008 spaces. Count them and put the count in newargc. */
2009 command = (char *) alloca (strlen (argv[2])+2);
2010 strcpy (command, argv[2]);
2011 if (command[strlen (command) - 1] != ' ')
2012 strcat (command, " ");
2013
2014 t = command;
2015 newargc = 0;
2016 t = mystrchr (t, ' ');
2017 while (t)
2018 {
2019 newargc++;
2020 t = mystrchr (t+1, ' ');
2021 }
2022
2023 newargv = (char **) alloca (sizeof (char *) * newargc);
2024
2025 t = command;
2026 for (j = 0; j < newargc; j++)
2027 {
2028 newargv[j] = (char *) alloca (strlen (t) + 1);
2029 mystrcpy (newargv[j], t);
2030
2031 t = mystrtok (t);
2032 paramlen += strlen (newargv[j]) + 1;
2033 }
2034
2035 if (strncmp (newargv[0], "~emacs/", 7) == 0)
2036 {
2037 if (posix_to_mac_pathname (newargv[0], tempmacpathname, MAXPATHLEN+1)
2038 == 0)
2039 return -1;
2040 }
2041 else
2042 { /* sometimes Emacs call "sh" without a path for the command */
2043 #if 0
2044 char *t = (char *) alloca (strlen (newargv[0]) + 7 + 1);
2045 strcpy (t, "~emacs/");
2046 strcat (t, newargv[0]);
2047 #endif /* 0 */
2048 Lisp_Object path;
2049 openp (Vexec_path, build_string (newargv[0]), EXEC_SUFFIXES, &path,
2050 make_number (X_OK));
2051
2052 if (NILP (path))
2053 return -1;
2054 if (posix_to_mac_pathname (SDATA (path), tempmacpathname,
2055 MAXPATHLEN+1) == 0)
2056 return -1;
2057 }
2058 strcpy (macappname, tempmacpathname);
2059 }
2060 else
2061 {
2062 if (posix_to_mac_pathname (argv[0], macappname, MAXPATHLEN+1) == 0)
2063 return -1;
2064
2065 newargv = (char **) alloca (sizeof (char *) * argc);
2066 newargc = argc;
2067 for (j = 1; j < argc; j++)
2068 {
2069 if (strncmp (argv[j], "~emacs/", 7) == 0)
2070 {
2071 char *t = strchr (argv[j], ' ');
2072 if (t)
2073 {
2074 char tempcmdname[MAXPATHLEN+1], tempmaccmdname[MAXPATHLEN+1];
2075 strncpy (tempcmdname, argv[j], t-argv[j]);
2076 tempcmdname[t-argv[j]] = '\0';
2077 if (posix_to_mac_pathname (tempcmdname, tempmaccmdname,
2078 MAXPATHLEN+1) == 0)
2079 return -1;
2080 newargv[j] = (char *) alloca (strlen (tempmaccmdname)
2081 + strlen (t) + 1);
2082 strcpy (newargv[j], tempmaccmdname);
2083 strcat (newargv[j], t);
2084 }
2085 else
2086 {
2087 char tempmaccmdname[MAXPATHLEN+1];
2088 if (posix_to_mac_pathname (argv[j], tempmaccmdname,
2089 MAXPATHLEN+1) == 0)
2090 return -1;
2091 newargv[j] = (char *) alloca (strlen (tempmaccmdname)+1);
2092 strcpy (newargv[j], tempmaccmdname);
2093 }
2094 }
2095 else
2096 newargv[j] = argv[j];
2097 paramlen += strlen (newargv[j]) + 1;
2098 }
2099 }
2100
2101 /* After expanding all the arguments, we now know the length of the
2102 parameter block to be sent to the subprocess as a message
2103 attached to the HLE. */
2104 param = (char *) malloc (paramlen + 1);
2105 if (!param)
2106 return -1;
2107
2108 p = param;
2109 *p++ = newargc;
2110 /* first byte of message contains number of arguments for command */
2111 strcpy (p, macworkdir);
2112 p += strlen (macworkdir);
2113 *p++ = '\0';
2114 /* null terminate strings sent so it's possible to use strcpy over there */
2115 strcpy (p, macinfn);
2116 p += strlen (macinfn);
2117 *p++ = '\0';
2118 strcpy (p, macoutfn);
2119 p += strlen (macoutfn);
2120 *p++ = '\0';
2121 strcpy (p, macerrfn);
2122 p += strlen (macerrfn);
2123 *p++ = '\0';
2124 for (j = 1; j < newargc; j++)
2125 {
2126 strcpy (p, newargv[j]);
2127 p += strlen (newargv[j]);
2128 *p++ = '\0';
2129 }
2130
2131 c2pstr (macappname);
2132
2133 iErr = FSMakeFSSpec (0, 0, macappname, &spec);
2134
2135 if (iErr != noErr)
2136 {
2137 free (param);
2138 return -1;
2139 }
2140
2141 lpbr.launchBlockID = extendedBlock;
2142 lpbr.launchEPBLength = extendedBlockLen;
2143 lpbr.launchControlFlags = launchContinue + launchNoFileFlags;
2144 lpbr.launchAppSpec = &spec;
2145 lpbr.launchAppParameters = NULL;
2146
2147 iErr = LaunchApplication (&lpbr); /* call the subprocess */
2148 if (iErr != noErr)
2149 {
2150 free (param);
2151 return -1;
2152 }
2153
2154 send_event.what = kHighLevelEvent;
2155 send_event.message = kEmacsSubprocessSend;
2156 /* Event ID stored in "where" unused */
2157
2158 retries = 3;
2159 /* OS may think current subprocess has terminated if previous one
2160 terminated recently. */
2161 do
2162 {
2163 iErr = PostHighLevelEvent (&send_event, &lpbr.launchProcessSN, 0, param,
2164 paramlen + 1, receiverIDisPSN);
2165 }
2166 while (iErr == sessClosedErr && retries-- > 0);
2167
2168 if (iErr != noErr)
2169 {
2170 free (param);
2171 return -1;
2172 }
2173
2174 cursor_region_handle = NewRgn ();
2175
2176 /* Wait for the subprocess to finish, when it will send us a ERPY
2177 high level event. */
2178 while (1)
2179 if (WaitNextEvent (highLevelEventMask, &reply_event, 180,
2180 cursor_region_handle)
2181 && reply_event.message == kEmacsSubprocessReply)
2182 break;
2183
2184 /* The return code is sent through the refCon */
2185 iErr = AcceptHighLevelEvent (&targ, &ref_con, NULL, &len);
2186 if (iErr != noErr)
2187 {
2188 DisposeHandle ((Handle) cursor_region_handle);
2189 free (param);
2190 return -1;
2191 }
2192
2193 DisposeHandle ((Handle) cursor_region_handle);
2194 free (param);
2195
2196 return ref_con;
2197 #endif /* not TARGET_API_MAC_CARBON */
2198 }
2199
2200
2201 DIR *
2202 opendir (const char *dirname)
2203 {
2204 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
2205 char mac_pathname[MAXPATHLEN+1], vol_name[MAXPATHLEN+1];
2206 DIR *dirp;
2207 CInfoPBRec cipb;
2208 HVolumeParam vpb;
2209 int len, vol_name_len;
2210
2211 if (find_true_pathname (dirname, true_pathname, MAXPATHLEN+1) == -1)
2212 return 0;
2213
2214 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
2215 if (len > -1)
2216 fully_resolved_name[len] = '\0';
2217 else
2218 strcpy (fully_resolved_name, true_pathname);
2219
2220 dirp = (DIR *) malloc (sizeof(DIR));
2221 if (!dirp)
2222 return 0;
2223
2224 /* Handle special case when dirname is "/": sets up for readir to
2225 get all mount volumes. */
2226 if (strcmp (fully_resolved_name, "/") == 0)
2227 {
2228 dirp->getting_volumes = 1; /* special all mounted volumes DIR struct */
2229 dirp->current_index = 1; /* index for first volume */
2230 return dirp;
2231 }
2232
2233 /* Handle typical cases: not accessing all mounted volumes. */
2234 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
2235 return 0;
2236
2237 /* Emacs calls opendir without the trailing '/', Mac needs trailing ':' */
2238 len = strlen (mac_pathname);
2239 if (mac_pathname[len - 1] != ':' && len < MAXPATHLEN)
2240 strcat (mac_pathname, ":");
2241
2242 /* Extract volume name */
2243 vol_name_len = strchr (mac_pathname, ':') - mac_pathname;
2244 strncpy (vol_name, mac_pathname, vol_name_len);
2245 vol_name[vol_name_len] = '\0';
2246 strcat (vol_name, ":");
2247
2248 c2pstr (mac_pathname);
2249 cipb.hFileInfo.ioNamePtr = mac_pathname;
2250 /* using full pathname so vRefNum and DirID ignored */
2251 cipb.hFileInfo.ioVRefNum = 0;
2252 cipb.hFileInfo.ioDirID = 0;
2253 cipb.hFileInfo.ioFDirIndex = 0;
2254 /* set to 0 to get information about specific dir or file */
2255
2256 errno = PBGetCatInfo (&cipb, false);
2257 if (errno != noErr)
2258 {
2259 errno = ENOENT;
2260 return 0;
2261 }
2262
2263 if (!(cipb.hFileInfo.ioFlAttrib & 0x10)) /* bit 4 = 1 for directories */
2264 return 0; /* not a directory */
2265
2266 dirp->dir_id = cipb.dirInfo.ioDrDirID; /* used later in readdir */
2267 dirp->getting_volumes = 0;
2268 dirp->current_index = 1; /* index for first file/directory */
2269
2270 c2pstr (vol_name);
2271 vpb.ioNamePtr = vol_name;
2272 /* using full pathname so vRefNum and DirID ignored */
2273 vpb.ioVRefNum = 0;
2274 vpb.ioVolIndex = -1;
2275 errno = PBHGetVInfo ((union HParamBlockRec *) &vpb, false);
2276 if (errno != noErr)
2277 {
2278 errno = ENOENT;
2279 return 0;
2280 }
2281
2282 dirp->vol_ref_num = vpb.ioVRefNum;
2283
2284 return dirp;
2285 }
2286
2287 int
2288 closedir (DIR *dp)
2289 {
2290 free (dp);
2291
2292 return 0;
2293 }
2294
2295
2296 struct dirent *
2297 readdir (DIR *dp)
2298 {
2299 HParamBlockRec hpblock;
2300 CInfoPBRec cipb;
2301 static struct dirent s_dirent;
2302 static Str255 s_name;
2303 int done;
2304 char *p;
2305
2306 /* Handle the root directory containing the mounted volumes. Call
2307 PBHGetVInfo specifying an index to obtain the info for a volume.
2308 PBHGetVInfo returns an error when it receives an index beyond the
2309 last volume, at which time we should return a nil dirent struct
2310 pointer. */
2311 if (dp->getting_volumes)
2312 {
2313 hpblock.volumeParam.ioNamePtr = s_name;
2314 hpblock.volumeParam.ioVRefNum = 0;
2315 hpblock.volumeParam.ioVolIndex = dp->current_index;
2316
2317 errno = PBHGetVInfo (&hpblock, false);
2318 if (errno != noErr)
2319 {
2320 errno = ENOENT;
2321 return 0;
2322 }
2323
2324 p2cstr (s_name);
2325 strcat (s_name, "/"); /* need "/" for stat to work correctly */
2326
2327 dp->current_index++;
2328
2329 s_dirent.d_ino = hpblock.volumeParam.ioVRefNum;
2330 s_dirent.d_name = s_name;
2331
2332 return &s_dirent;
2333 }
2334 else
2335 {
2336 cipb.hFileInfo.ioVRefNum = dp->vol_ref_num;
2337 cipb.hFileInfo.ioNamePtr = s_name;
2338 /* location to receive filename returned */
2339
2340 /* return only visible files */
2341 done = false;
2342 while (!done)
2343 {
2344 cipb.hFileInfo.ioDirID = dp->dir_id;
2345 /* directory ID found by opendir */
2346 cipb.hFileInfo.ioFDirIndex = dp->current_index;
2347
2348 errno = PBGetCatInfo (&cipb, false);
2349 if (errno != noErr)
2350 {
2351 errno = ENOENT;
2352 return 0;
2353 }
2354
2355 /* insist on a visible entry */
2356 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* directory? */
2357 done = !(cipb.dirInfo.ioDrUsrWds.frFlags & fInvisible);
2358 else
2359 done = !(cipb.hFileInfo.ioFlFndrInfo.fdFlags & fInvisible);
2360
2361 dp->current_index++;
2362 }
2363
2364 p2cstr (s_name);
2365
2366 p = s_name;
2367 while (*p)
2368 {
2369 if (*p == '/')
2370 *p = ':';
2371 p++;
2372 }
2373
2374 s_dirent.d_ino = cipb.dirInfo.ioDrDirID;
2375 /* value unimportant: non-zero for valid file */
2376 s_dirent.d_name = s_name;
2377
2378 return &s_dirent;
2379 }
2380 }
2381
2382
2383 char *
2384 getwd (char *path)
2385 {
2386 char mac_pathname[MAXPATHLEN+1];
2387 Str255 directory_name;
2388 OSErr errno;
2389 CInfoPBRec cipb;
2390
2391 if (path_from_vol_dir_name (mac_pathname, 255, 0, 0, "\p") == 0)
2392 return NULL;
2393
2394 if (mac_to_posix_pathname (mac_pathname, path, MAXPATHLEN+1) == 0)
2395 return 0;
2396 else
2397 return path;
2398 }
2399
2400 #endif /* ! MAC_OSX */
2401
2402
2403 void
2404 initialize_applescript ()
2405 {
2406 AEDesc null_desc;
2407 OSAError osaerror;
2408
2409 /* if open fails, as_scripting_component is set to NULL. Its
2410 subsequent use in OSA calls will fail with badComponentInstance
2411 error. */
2412 as_scripting_component = OpenDefaultComponent (kOSAComponentType,
2413 kAppleScriptSubtype);
2414
2415 null_desc.descriptorType = typeNull;
2416 null_desc.dataHandle = 0;
2417 osaerror = OSAMakeContext (as_scripting_component, &null_desc,
2418 kOSANullScript, &as_script_context);
2419 if (osaerror)
2420 as_script_context = kOSANullScript;
2421 /* use default context if create fails */
2422 }
2423
2424
2425 void terminate_applescript()
2426 {
2427 OSADispose (as_scripting_component, as_script_context);
2428 CloseComponent (as_scripting_component);
2429 }
2430
2431
2432 /* Compile and execute the AppleScript SCRIPT and return the error
2433 status as function value. A zero is returned if compilation and
2434 execution is successful, in which case RESULT returns a pointer to
2435 a string containing the resulting script value. Otherwise, the Mac
2436 error code is returned and RESULT returns a pointer to an error
2437 string. In both cases the caller should deallocate the storage
2438 used by the string pointed to by RESULT if it is non-NULL. For
2439 documentation on the MacOS scripting architecture, see Inside
2440 Macintosh - Interapplication Communications: Scripting Components. */
2441
2442 static long
2443 do_applescript (char *script, char **result)
2444 {
2445 AEDesc script_desc, result_desc, error_desc;
2446 OSErr error;
2447 OSAError osaerror;
2448 long length;
2449
2450 *result = 0;
2451
2452 if (!as_scripting_component)
2453 initialize_applescript();
2454
2455 error = AECreateDesc (typeChar, script, strlen(script), &script_desc);
2456 if (error)
2457 return error;
2458
2459 osaerror = OSADoScript (as_scripting_component, &script_desc, kOSANullScript,
2460 typeChar, kOSAModeNull, &result_desc);
2461
2462 if (osaerror == errOSAScriptError)
2463 {
2464 /* error executing AppleScript: retrieve error message */
2465 if (!OSAScriptError (as_scripting_component, kOSAErrorMessage, typeChar,
2466 &error_desc))
2467 {
2468 #if TARGET_API_MAC_CARBON
2469 length = AEGetDescDataSize (&error_desc);
2470 *result = (char *) xmalloc (length + 1);
2471 if (*result)
2472 {
2473 AEGetDescData (&error_desc, *result, length);
2474 *(*result + length) = '\0';
2475 }
2476 #else /* not TARGET_API_MAC_CARBON */
2477 HLock (error_desc.dataHandle);
2478 length = GetHandleSize(error_desc.dataHandle);
2479 *result = (char *) xmalloc (length + 1);
2480 if (*result)
2481 {
2482 memcpy (*result, *(error_desc.dataHandle), length);
2483 *(*result + length) = '\0';
2484 }
2485 HUnlock (error_desc.dataHandle);
2486 #endif /* not TARGET_API_MAC_CARBON */
2487 AEDisposeDesc (&error_desc);
2488 }
2489 }
2490 else if (osaerror == noErr) /* success: retrieve resulting script value */
2491 {
2492 #if TARGET_API_MAC_CARBON
2493 length = AEGetDescDataSize (&result_desc);
2494 *result = (char *) xmalloc (length + 1);
2495 if (*result)
2496 {
2497 AEGetDescData (&result_desc, *result, length);
2498 *(*result + length) = '\0';
2499 }
2500 #else /* not TARGET_API_MAC_CARBON */
2501 HLock (result_desc.dataHandle);
2502 length = GetHandleSize(result_desc.dataHandle);
2503 *result = (char *) xmalloc (length + 1);
2504 if (*result)
2505 {
2506 memcpy (*result, *(result_desc.dataHandle), length);
2507 *(*result + length) = '\0';
2508 }
2509 HUnlock (result_desc.dataHandle);
2510 #endif /* not TARGET_API_MAC_CARBON */
2511 AEDisposeDesc (&result_desc);
2512 }
2513
2514 AEDisposeDesc (&script_desc);
2515
2516 return osaerror;
2517 }
2518
2519
2520 DEFUN ("do-applescript", Fdo_applescript, Sdo_applescript, 1, 1, 0,
2521 doc: /* Compile and execute AppleScript SCRIPT and retrieve and return the result.
2522 If compilation and execution are successful, the resulting script
2523 value is returned as a string. Otherwise the function aborts and
2524 displays the error message returned by the AppleScript scripting
2525 component. */)
2526 (script)
2527 Lisp_Object script;
2528 {
2529 char *result, *temp;
2530 Lisp_Object lisp_result;
2531 long status;
2532
2533 CHECK_STRING (script);
2534
2535 status = do_applescript (SDATA (script), &result);
2536 if (status)
2537 {
2538 if (!result)
2539 error ("AppleScript error %d", status);
2540 else
2541 {
2542 /* Unfortunately only OSADoScript in do_applescript knows how
2543 how large the resulting script value or error message is
2544 going to be and therefore as caller memory must be
2545 deallocated here. It is necessary to free the error
2546 message before calling error to avoid a memory leak. */
2547 temp = (char *) alloca (strlen (result) + 1);
2548 strcpy (temp, result);
2549 xfree (result);
2550 error (temp);
2551 }
2552 }
2553 else
2554 {
2555 lisp_result = build_string (result);
2556 xfree (result);
2557 return lisp_result;
2558 }
2559 }
2560
2561
2562 DEFUN ("mac-file-name-to-posix", Fmac_file_name_to_posix,
2563 Smac_file_name_to_posix, 1, 1, 0,
2564 doc: /* Convert Macintosh filename to Posix form. */)
2565 (mac_filename)
2566 Lisp_Object mac_filename;
2567 {
2568 char posix_filename[MAXPATHLEN+1];
2569
2570 CHECK_STRING (mac_filename);
2571
2572 if (mac_to_posix_pathname (SDATA (mac_filename), posix_filename,
2573 MAXPATHLEN))
2574 return build_string (posix_filename);
2575 else
2576 return Qnil;
2577 }
2578
2579
2580 DEFUN ("posix-file-name-to-mac", Fposix_file_name_to_mac,
2581 Sposix_file_name_to_mac, 1, 1, 0,
2582 doc: /* Convert Posix filename to Mac form. */)
2583 (posix_filename)
2584 Lisp_Object posix_filename;
2585 {
2586 char mac_filename[MAXPATHLEN+1];
2587
2588 CHECK_STRING (posix_filename);
2589
2590 if (posix_to_mac_pathname (SDATA (posix_filename), mac_filename,
2591 MAXPATHLEN))
2592 return build_string (mac_filename);
2593 else
2594 return Qnil;
2595 }
2596
2597
2598 /* set interprogram-paste-function to mac-paste-function in mac-win.el
2599 to enable Emacs to obtain the contents of the Mac clipboard. */
2600 DEFUN ("mac-paste-function", Fmac_paste_function, Smac_paste_function, 0, 0, 0,
2601 doc: /* Return the contents of the Mac clipboard as a string. */)
2602 ()
2603 {
2604 #if TARGET_API_MAC_CARBON
2605 ScrapRef scrap;
2606 ScrapFlavorFlags sff;
2607 Size s;
2608 int i;
2609 char *data;
2610
2611 if (GetCurrentScrap (&scrap) != noErr)
2612 return Qnil;
2613
2614 if (GetScrapFlavorFlags (scrap, kScrapFlavorTypeText, &sff) != noErr)
2615 return Qnil;
2616
2617 if (GetScrapFlavorSize (scrap, kScrapFlavorTypeText, &s) != noErr)
2618 return Qnil;
2619
2620 if ((data = (char*) alloca (s)) == NULL)
2621 return Qnil;
2622
2623 if (GetScrapFlavorData (scrap, kScrapFlavorTypeText, &s, data) != noErr
2624 || s == 0)
2625 return Qnil;
2626
2627 /* Emacs expects clipboard contents have Unix-style eol's */
2628 for (i = 0; i < s; i++)
2629 if (data[i] == '\r')
2630 data[i] = '\n';
2631
2632 return make_string (data, s);
2633 #else /* not TARGET_API_MAC_CARBON */
2634 Lisp_Object value;
2635 Handle my_handle;
2636 long scrap_offset, rc, i;
2637
2638 my_handle = NewHandle (0); /* allocate 0-length data area */
2639
2640 rc = GetScrap (my_handle, 'TEXT', &scrap_offset);
2641 if (rc < 0)
2642 return Qnil;
2643
2644 HLock (my_handle);
2645
2646 /* Emacs expects clipboard contents have Unix-style eol's */
2647 for (i = 0; i < rc; i++)
2648 if ((*my_handle)[i] == '\r')
2649 (*my_handle)[i] = '\n';
2650
2651 value = make_string (*my_handle, rc);
2652
2653 HUnlock (my_handle);
2654
2655 DisposeHandle (my_handle);
2656
2657 return value;
2658 #endif /* not TARGET_API_MAC_CARBON */
2659 }
2660
2661
2662 /* set interprogram-cut-function to mac-cut-function in mac-win.el
2663 to enable Emacs to write the top of the kill-ring to the Mac clipboard. */
2664 DEFUN ("mac-cut-function", Fmac_cut_function, Smac_cut_function, 1, 2, 0,
2665 doc: /* Put the value of the string parameter to the Mac clipboard. */)
2666 (value, push)
2667 Lisp_Object value, push;
2668 {
2669 char *buf;
2670 int len, i;
2671
2672 /* fixme: ignore the push flag for now */
2673
2674 CHECK_STRING (value);
2675
2676 len = SCHARS (value);
2677 buf = (char *) alloca (len+1);
2678 bcopy (SDATA (value), buf, len);
2679 buf[len] = '\0';
2680
2681 /* convert to Mac-style eol's before sending to clipboard */
2682 for (i = 0; i < len; i++)
2683 if (buf[i] == '\n')
2684 buf[i] = '\r';
2685
2686 #if TARGET_API_MAC_CARBON
2687 {
2688 ScrapRef scrap;
2689 ClearCurrentScrap ();
2690 if (GetCurrentScrap (&scrap) != noErr)
2691 error ("cannot get current scrap");
2692
2693 if (PutScrapFlavor (scrap, kScrapFlavorTypeText, kScrapFlavorMaskNone, len,
2694 buf) != noErr)
2695 error ("cannot put to scrap");
2696 }
2697 #else /* not TARGET_API_MAC_CARBON */
2698 ZeroScrap ();
2699 PutScrap (len, 'TEXT', buf);
2700 #endif /* not TARGET_API_MAC_CARBON */
2701
2702 return Qnil;
2703 }
2704
2705
2706 DEFUN ("x-selection-exists-p", Fx_selection_exists_p, Sx_selection_exists_p,
2707 0, 1, 0,
2708 doc: /* Whether there is an owner for the given X Selection.
2709 The arg should be the name of the selection in question, typically one of
2710 the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.
2711 \(Those are literal upper-case symbol names, since that's what X expects.)
2712 For convenience, the symbol nil is the same as `PRIMARY',
2713 and t is the same as `SECONDARY'. */)
2714 (selection)
2715 Lisp_Object selection;
2716 {
2717 CHECK_SYMBOL (selection);
2718
2719 /* Return nil for PRIMARY and SECONDARY selections; for CLIPBOARD, check
2720 if the clipboard currently has valid text format contents. */
2721
2722 if (EQ (selection, QCLIPBOARD))
2723 {
2724 Lisp_Object val = Qnil;
2725
2726 #if TARGET_API_MAC_CARBON
2727 ScrapRef scrap;
2728 ScrapFlavorFlags sff;
2729
2730 if (GetCurrentScrap (&scrap) == noErr)
2731 if (GetScrapFlavorFlags (scrap, kScrapFlavorTypeText, &sff) == noErr)
2732 val = Qt;
2733 #else /* not TARGET_API_MAC_CARBON */
2734 Handle my_handle;
2735 long rc, scrap_offset;
2736
2737 my_handle = NewHandle (0);
2738
2739 rc = GetScrap (my_handle, 'TEXT', &scrap_offset);
2740 if (rc >= 0)
2741 val = Qt;
2742
2743 DisposeHandle (my_handle);
2744 #endif /* not TARGET_API_MAC_CARBON */
2745
2746 return val;
2747 }
2748 return Qnil;
2749 }
2750
2751 #ifdef MAC_OSX
2752 #undef select
2753
2754 extern int inhibit_window_system;
2755 extern int noninteractive;
2756
2757 /* When Emacs is started from the Finder, SELECT always immediately
2758 returns as if input is present when file descriptor 0 is polled for
2759 input. Strangely, when Emacs is run as a GUI application from the
2760 command line, it blocks in the same situation. This `wrapper' of
2761 the system call SELECT corrects this discrepancy. */
2762 int
2763 sys_select (n, rfds, wfds, efds, timeout)
2764 int n;
2765 SELECT_TYPE *rfds;
2766 SELECT_TYPE *wfds;
2767 SELECT_TYPE *efds;
2768 struct timeval *timeout;
2769 {
2770 if (!inhibit_window_system && rfds && FD_ISSET (0, rfds))
2771 return 1;
2772 else if (inhibit_window_system || noninteractive ||
2773 (timeout && (EMACS_SECS(*timeout)==0) &&
2774 (EMACS_USECS(*timeout)==0)))
2775 return select(n, rfds, wfds, efds, timeout);
2776 else
2777 {
2778 EMACS_TIME end_time, now;
2779
2780 EMACS_GET_TIME (end_time);
2781 if (timeout)
2782 EMACS_ADD_TIME (end_time, end_time, *timeout);
2783
2784 do
2785 {
2786 int r;
2787 EMACS_TIME one_second;
2788 SELECT_TYPE orfds;
2789
2790 FD_ZERO (&orfds);
2791 if (rfds)
2792 {
2793 orfds = *rfds;
2794 }
2795
2796 EMACS_SET_SECS (one_second, 1);
2797 EMACS_SET_USECS (one_second, 0);
2798
2799 if (timeout && EMACS_TIME_LT(*timeout, one_second))
2800 one_second = *timeout;
2801
2802 if ((r = select (n, &orfds, wfds, efds, &one_second)) > 0)
2803 {
2804 *rfds = orfds;
2805 return r;
2806 }
2807
2808 mac_check_for_quit_char();
2809
2810 EMACS_GET_TIME (now);
2811 EMACS_SUB_TIME (now, end_time, now);
2812 }
2813 while (!timeout || !EMACS_TIME_NEG_P (now));
2814
2815 return 0;
2816 }
2817 }
2818
2819 #undef read
2820 int sys_read (fds, buf, nbyte)
2821 int fds;
2822 char *buf;
2823 unsigned int nbyte;
2824 {
2825 SELECT_TYPE rfds;
2826 EMACS_TIME one_second;
2827 int r;
2828
2829 /* Use select to block on IO while still checking for quit_char */
2830 if (!inhibit_window_system && !noninteractive &&
2831 ! (fcntl(fds, F_GETFL, 0) & O_NONBLOCK))
2832 {
2833 FD_ZERO (&rfds);
2834 FD_SET (fds, &rfds);
2835 if (sys_select (fds+1, &rfds, 0, 0, NULL) < 0)
2836 return -1;
2837 }
2838
2839 return read (fds, buf, nbyte);
2840 }
2841
2842
2843 /* Set up environment variables so that Emacs can correctly find its
2844 support files when packaged as an application bundle. Directories
2845 placed in /usr/local/share/emacs/<emacs-version>/, /usr/local/bin,
2846 and /usr/local/libexec/emacs/<emacs-version>/<system-configuration>
2847 by `make install' by default can instead be placed in
2848 .../Emacs.app/Contents/Resources/ and
2849 .../Emacs.app/Contents/MacOS/. Each of these environment variables
2850 is changed only if it is not already set. Presumably if the user
2851 sets an environment variable, he will want to use files in his path
2852 instead of ones in the application bundle. */
2853 void
2854 init_mac_osx_environment ()
2855 {
2856 CFBundleRef bundle;
2857 CFURLRef bundleURL;
2858 CFStringRef cf_app_bundle_pathname;
2859 int app_bundle_pathname_len;
2860 char *app_bundle_pathname;
2861 char *p, *q;
2862 struct stat st;
2863
2864 /* Fetch the pathname of the application bundle as a C string into
2865 app_bundle_pathname. */
2866
2867 bundle = CFBundleGetMainBundle ();
2868 if (!bundle)
2869 return;
2870
2871 bundleURL = CFBundleCopyBundleURL (bundle);
2872 if (!bundleURL)
2873 return;
2874
2875 cf_app_bundle_pathname = CFURLCopyFileSystemPath (bundleURL,
2876 kCFURLPOSIXPathStyle);
2877 app_bundle_pathname_len = CFStringGetLength (cf_app_bundle_pathname);
2878 app_bundle_pathname = (char *) alloca (app_bundle_pathname_len + 1);
2879
2880 if (!CFStringGetCString (cf_app_bundle_pathname,
2881 app_bundle_pathname,
2882 app_bundle_pathname_len + 1,
2883 kCFStringEncodingISOLatin1))
2884 {
2885 CFRelease (cf_app_bundle_pathname);
2886 return;
2887 }
2888
2889 CFRelease (cf_app_bundle_pathname);
2890
2891 /* P should have sufficient room for the pathname of the bundle plus
2892 the subpath in it leading to the respective directories. Q
2893 should have three times that much room because EMACSLOADPATH can
2894 have the value "<path to lisp dir>:<path to leim dir>:<path to
2895 site-lisp dir>". */
2896 p = (char *) alloca (app_bundle_pathname_len + 50);
2897 q = (char *) alloca (3 * app_bundle_pathname_len + 150);
2898 if (!getenv ("EMACSLOADPATH"))
2899 {
2900 q[0] = '\0';
2901
2902 strcpy (p, app_bundle_pathname);
2903 strcat (p, "/Contents/Resources/lisp");
2904 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
2905 strcat (q, p);
2906
2907 strcpy (p, app_bundle_pathname);
2908 strcat (p, "/Contents/Resources/leim");
2909 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
2910 {
2911 if (q[0] != '\0')
2912 strcat (q, ":");
2913 strcat (q, p);
2914 }
2915
2916 strcpy (p, app_bundle_pathname);
2917 strcat (p, "/Contents/Resources/site-lisp");
2918 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
2919 {
2920 if (q[0] != '\0')
2921 strcat (q, ":");
2922 strcat (q, p);
2923 }
2924
2925 if (q[0] != '\0')
2926 setenv ("EMACSLOADPATH", q, 1);
2927 }
2928
2929 if (!getenv ("EMACSPATH"))
2930 {
2931 q[0] = '\0';
2932
2933 strcpy (p, app_bundle_pathname);
2934 strcat (p, "/Contents/MacOS/libexec");
2935 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
2936 strcat (q, p);
2937
2938 strcpy (p, app_bundle_pathname);
2939 strcat (p, "/Contents/MacOS/bin");
2940 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
2941 {
2942 if (q[0] != '\0')
2943 strcat (q, ":");
2944 strcat (q, p);
2945 }
2946
2947 if (q[0] != '\0')
2948 setenv ("EMACSPATH", q, 1);
2949 }
2950
2951 if (!getenv ("EMACSDATA"))
2952 {
2953 strcpy (p, app_bundle_pathname);
2954 strcat (p, "/Contents/Resources/etc");
2955 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
2956 setenv ("EMACSDATA", p, 1);
2957 }
2958
2959 if (!getenv ("EMACSDOC"))
2960 {
2961 strcpy (p, app_bundle_pathname);
2962 strcat (p, "/Contents/Resources/etc");
2963 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
2964 setenv ("EMACSDOC", p, 1);
2965 }
2966
2967 if (!getenv ("INFOPATH"))
2968 {
2969 strcpy (p, app_bundle_pathname);
2970 strcat (p, "/Contents/Resources/info");
2971 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
2972 setenv ("INFOPATH", p, 1);
2973 }
2974 }
2975 #endif /* MAC_OSX */
2976
2977 void
2978 syms_of_mac ()
2979 {
2980 QCLIPBOARD = intern ("CLIPBOARD");
2981 staticpro (&QCLIPBOARD);
2982
2983 defsubr (&Smac_paste_function);
2984 defsubr (&Smac_cut_function);
2985 defsubr (&Sx_selection_exists_p);
2986
2987 defsubr (&Sdo_applescript);
2988 defsubr (&Smac_file_name_to_posix);
2989 defsubr (&Sposix_file_name_to_mac);
2990 }