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