Add emacs-xtra.
[emacs.git] / src / mac.c
blob9f3455ab5dc09d4e15b57db61d34f9960fe4f7af
1 /* Unix emulation routines for GNU Emacs on the Mac OS.
2 Copyright (C) 2000, 2001 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
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.
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.
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. */
21 /* Contributed by Andrew Choi (akochoi@mac.com). */
23 #include <config.h>
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 <grp.h>
34 #include <sys/param.h>
35 #include <stdlib.h>
36 #include <fcntl.h>
37 #if __MWERKS__
38 #include <unistd.h>
39 #endif
41 #ifdef MAC_OSX
42 #undef mktime
43 #undef DEBUG
44 #undef free
45 #undef malloc
46 #undef realloc
47 #undef init_process
48 #include <Carbon/Carbon.h>
49 #undef free
50 #define free unexec_free
51 #undef malloc
52 #define malloc unexec_malloc
53 #undef realloc
54 #define realloc unexec_realloc
55 #undef init_process
56 #define init_process emacs_init_process
57 #else /* not MAC_OSX */
58 #include <Files.h>
59 #include <MacTypes.h>
60 #include <TextUtils.h>
61 #include <Folders.h>
62 #include <Resources.h>
63 #include <Aliases.h>
64 #include <FixMath.h>
65 #include <Timer.h>
66 #include <OSA.h>
67 #include <AppleScript.h>
68 #include <Scrap.h>
69 #endif /* not MAC_OSX */
71 #include "lisp.h"
72 #include "process.h"
73 #include "sysselect.h"
74 #include "systime.h"
76 Lisp_Object QCLIPBOARD;
78 /* An instance of the AppleScript component. */
79 static ComponentInstance as_scripting_component;
80 /* The single script context used for all script executions. */
81 static OSAID as_script_context;
84 /* When converting from Mac to Unix pathnames, /'s in folder names are
85 converted to :'s. This function, used in copying folder names,
86 performs a strncat and converts all character a to b in the copy of
87 the string s2 appended to the end of s1. */
89 void
90 string_cat_and_replace (char *s1, const char *s2, int n, char a, char b)
92 int l1 = strlen (s1);
93 int l2 = strlen (s2);
94 char *p = s1 + l1;
95 int i;
97 strncat (s1, s2, n);
98 for (i = 0; i < l2; i++)
100 if (*p == a)
101 *p = b;
102 p++;
107 /* Convert a Mac pathname to Posix form. A Mac full pathname is one
108 that does not begin with a ':' and contains at least one ':'. A Mac
109 full pathname causes a '/' to be prepended to the Posix pathname.
110 The algorithm for the rest of the pathname is as follows:
111 For each segment between two ':',
112 if it is non-null, copy as is and then add a '/' at the end,
113 otherwise, insert a "../" into the Posix pathname.
114 Returns 1 if successful; 0 if fails. */
117 mac_to_posix_pathname (const char *mfn, char *ufn, int ufnbuflen)
119 const char *p, *q, *pe;
121 strcpy (ufn, "");
123 if (*mfn == '\0')
124 return 1;
126 p = strchr (mfn, ':');
127 if (p != 0 && p != mfn) /* full pathname */
128 strcat (ufn, "/");
130 p = mfn;
131 if (*p == ':')
132 p++;
134 pe = mfn + strlen (mfn);
135 while (p < pe)
137 q = strchr (p, ':');
138 if (q)
140 if (q == p)
141 { /* two consecutive ':' */
142 if (strlen (ufn) + 3 >= ufnbuflen)
143 return 0;
144 strcat (ufn, "../");
146 else
148 if (strlen (ufn) + (q - p) + 1 >= ufnbuflen)
149 return 0;
150 string_cat_and_replace (ufn, p, q - p, '/', ':');
151 strcat (ufn, "/");
153 p = q + 1;
155 else
157 if (strlen (ufn) + (pe - p) >= ufnbuflen)
158 return 0;
159 string_cat_and_replace (ufn, p, pe - p, '/', ':');
160 /* no separator for last one */
161 p = pe;
165 return 1;
169 extern char *get_temp_dir_name ();
172 /* Convert a Posix pathname to Mac form. Approximately reverse of the
173 above in algorithm. */
176 posix_to_mac_pathname (const char *ufn, char *mfn, int mfnbuflen)
178 const char *p, *q, *pe;
179 char expanded_pathname[MAXPATHLEN+1];
181 strcpy (mfn, "");
183 if (*ufn == '\0')
184 return 1;
186 p = ufn;
188 /* Check for and handle volume names. Last comparison: strangely
189 somewhere "/.emacs" is passed. A temporary fix for now. */
190 if (*p == '/' && strchr (p+1, '/') == NULL && strcmp (p, "/.emacs") != 0)
192 if (strlen (p) + 1 > mfnbuflen)
193 return 0;
194 strcpy (mfn, p+1);
195 strcat (mfn, ":");
196 return 1;
199 /* expand to emacs dir found by init_emacs_passwd_dir */
200 if (strncmp (p, "~emacs/", 7) == 0)
202 struct passwd *pw = getpwnam ("emacs");
203 p += 7;
204 if (strlen (pw->pw_dir) + strlen (p) > MAXPATHLEN)
205 return 0;
206 strcpy (expanded_pathname, pw->pw_dir);
207 strcat (expanded_pathname, p);
208 p = expanded_pathname;
209 /* now p points to the pathname with emacs dir prefix */
211 else if (strncmp (p, "/tmp/", 5) == 0)
213 char *t = get_temp_dir_name ();
214 p += 5;
215 if (strlen (t) + strlen (p) > MAXPATHLEN)
216 return 0;
217 strcpy (expanded_pathname, t);
218 strcat (expanded_pathname, p);
219 p = expanded_pathname;
220 /* now p points to the pathname with emacs dir prefix */
222 else if (*p != '/') /* relative pathname */
223 strcat (mfn, ":");
225 if (*p == '/')
226 p++;
228 pe = p + strlen (p);
229 while (p < pe)
231 q = strchr (p, '/');
232 if (q)
234 if (q - p == 2 && *p == '.' && *(p+1) == '.')
236 if (strlen (mfn) + 1 >= mfnbuflen)
237 return 0;
238 strcat (mfn, ":");
240 else
242 if (strlen (mfn) + (q - p) + 1 >= mfnbuflen)
243 return 0;
244 string_cat_and_replace (mfn, p, q - p, ':', '/');
245 strcat (mfn, ":");
247 p = q + 1;
249 else
251 if (strlen (mfn) + (pe - p) >= mfnbuflen)
252 return 0;
253 string_cat_and_replace (mfn, p, pe - p, ':', '/');
254 p = pe;
258 return 1;
261 #ifndef MAC_OSX
263 /* The following functions with "sys_" prefix are stubs to Unix
264 functions that have already been implemented by CW or MPW. The
265 calls to them in Emacs source course are #define'd to call the sys_
266 versions by the header files s-mac.h. In these stubs pathnames are
267 converted between their Unix and Mac forms. */
270 /* Unix epoch is Jan 1, 1970 while Mac epoch is Jan 1, 1904: 66 years
271 + 17 leap days. These are for adjusting time values returned by
272 MacOS Toolbox functions. */
274 #define MAC_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
276 #ifdef __MWERKS__
277 #if __MSL__ < 0x6000
278 /* CW Pro 5 epoch is Jan 1, 1900 (aaarghhhhh!); remember, 1900 is not
279 a leap year! This is for adjusting time_t values returned by MSL
280 functions. */
281 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 70 + 17) * 24 * 60 * 60)
282 #else /* __MSL__ >= 0x6000 */
283 /* CW changes Pro 6 to follow Unix! */
284 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
285 #endif /* __MSL__ >= 0x6000 */
286 #elif __MRC__
287 /* MPW library functions follow Unix (confused?). */
288 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
289 #else /* not __MRC__ */
290 You lose!!!
291 #endif /* not __MRC__ */
294 /* Define our own stat function for both MrC and CW. The reason for
295 doing this: "stat" is both the name of a struct and function name:
296 can't use the same trick like that for sys_open, sys_close, etc. to
297 redirect Emacs's calls to our own version that converts Unix style
298 filenames to Mac style filename because all sorts of compilation
299 errors will be generated if stat is #define'd to be sys_stat. */
302 stat_noalias (const char *path, struct stat *buf)
304 char mac_pathname[MAXPATHLEN+1];
305 CInfoPBRec cipb;
307 if (posix_to_mac_pathname (path, mac_pathname, MAXPATHLEN+1) == 0)
308 return -1;
310 c2pstr (mac_pathname);
311 cipb.hFileInfo.ioNamePtr = mac_pathname;
312 cipb.hFileInfo.ioVRefNum = 0;
313 cipb.hFileInfo.ioDirID = 0;
314 cipb.hFileInfo.ioFDirIndex = 0;
315 /* set to 0 to get information about specific dir or file */
317 errno = PBGetCatInfo (&cipb, false);
318 if (errno == -43) /* -43: fnfErr defined in Errors.h */
319 errno = ENOENT;
320 if (errno != noErr)
321 return -1;
323 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* bit 4 = 1 for directories */
325 buf->st_mode = S_IFDIR | S_IREAD | S_IEXEC;
327 if (!(cipb.hFileInfo.ioFlAttrib & 0x1))
328 buf->st_mode |= S_IWRITE; /* bit 1 = 1 for locked files/directories */
329 buf->st_ino = cipb.dirInfo.ioDrDirID;
330 buf->st_dev = cipb.dirInfo.ioVRefNum;
331 buf->st_size = cipb.dirInfo.ioDrNmFls;
332 /* size of dir = number of files and dirs */
333 buf->st_atime
334 = buf->st_mtime
335 = cipb.dirInfo.ioDrMdDat - MAC_UNIX_EPOCH_DIFF;
336 buf->st_ctime = cipb.dirInfo.ioDrCrDat - MAC_UNIX_EPOCH_DIFF;
338 else
340 buf->st_mode = S_IFREG | S_IREAD;
341 if (!(cipb.hFileInfo.ioFlAttrib & 0x1))
342 buf->st_mode |= S_IWRITE; /* bit 1 = 1 for locked files/directories */
343 if (cipb.hFileInfo.ioFlFndrInfo.fdType == 'APPL')
344 buf->st_mode |= S_IEXEC;
345 buf->st_ino = cipb.hFileInfo.ioDirID;
346 buf->st_dev = cipb.hFileInfo.ioVRefNum;
347 buf->st_size = cipb.hFileInfo.ioFlLgLen;
348 buf->st_atime
349 = buf->st_mtime
350 = cipb.hFileInfo.ioFlMdDat - MAC_UNIX_EPOCH_DIFF;
351 buf->st_ctime = cipb.hFileInfo.ioFlCrDat - MAC_UNIX_EPOCH_DIFF;
354 if (cipb.hFileInfo.ioFlFndrInfo.fdFlags & 0x8000)
356 /* identify alias files as symlinks */
357 buf->st_mode &= ~S_IFREG;
358 buf->st_mode |= S_IFLNK;
361 buf->st_nlink = 1;
362 buf->st_uid = getuid ();
363 buf->st_gid = getgid ();
364 buf->st_rdev = 0;
366 return 0;
371 lstat (const char *path, struct stat *buf)
373 int result;
374 char true_pathname[MAXPATHLEN+1];
376 /* Try looking for the file without resolving aliases first. */
377 if ((result = stat_noalias (path, buf)) >= 0)
378 return result;
380 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
381 return -1;
383 return stat_noalias (true_pathname, buf);
388 stat (const char *path, struct stat *sb)
390 int result;
391 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
392 int len;
394 if ((result = stat_noalias (path, sb)) >= 0 &&
395 ! (sb->st_mode & S_IFLNK))
396 return result;
398 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
399 return -1;
401 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
402 if (len > -1)
404 fully_resolved_name[len] = '\0';
405 /* in fact our readlink terminates strings */
406 return lstat (fully_resolved_name, sb);
408 else
409 return lstat (true_pathname, sb);
413 #if __MRC__
414 /* CW defines fstat in stat.mac.c while MPW does not provide this
415 function. Without the information of how to get from a file
416 descriptor in MPW StdCLib to a Mac OS file spec, it should be hard
417 to implement this function. Fortunately, there is only one place
418 where this function is called in our configuration: in fileio.c,
419 where only the st_dev and st_ino fields are used to determine
420 whether two fildes point to different i-nodes to prevent copying
421 a file onto itself equal. What we have here probably needs
422 improvement. */
425 fstat (int fildes, struct stat *buf)
427 buf->st_dev = 0;
428 buf->st_ino = fildes;
429 buf->st_mode = S_IFREG; /* added by T.I. for the copy-file */
430 return 0; /* success */
432 #endif /* __MRC__ */
436 mkdir (const char *dirname, int mode)
438 #pragma unused(mode)
440 HFileParam hfpb;
441 char true_pathname[MAXPATHLEN+1], mac_pathname[MAXPATHLEN+1];
443 if (find_true_pathname (dirname, true_pathname, MAXPATHLEN+1) == -1)
444 return -1;
446 if (posix_to_mac_pathname (true_pathname, mac_pathname, MAXPATHLEN+1) == 0)
447 return -1;
449 c2pstr (mac_pathname);
450 hfpb.ioNamePtr = mac_pathname;
451 hfpb.ioVRefNum = 0; /* ignored unless name is invalid */
452 hfpb.ioDirID = 0; /* parent is the root */
454 errno = PBDirCreate ((HParmBlkPtr) &hfpb, false);
455 /* just return the Mac OSErr code for now */
456 return errno == noErr ? 0 : -1;
460 #undef rmdir
461 sys_rmdir (const char *dirname)
463 HFileParam hfpb;
464 char mac_pathname[MAXPATHLEN+1];
466 if (posix_to_mac_pathname (dirname, mac_pathname, MAXPATHLEN+1) == 0)
467 return -1;
469 c2pstr (mac_pathname);
470 hfpb.ioNamePtr = mac_pathname;
471 hfpb.ioVRefNum = 0; /* ignored unless name is invalid */
472 hfpb.ioDirID = 0; /* parent is the root */
474 errno = PBHDelete ((HParmBlkPtr) &hfpb, false);
475 return errno == noErr ? 0 : -1;
479 #ifdef __MRC__
480 /* No implementation yet. */
482 execvp (const char *path, ...)
484 return -1;
486 #endif /* __MRC__ */
490 utime (const char *path, const struct utimbuf *times)
492 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
493 int len;
494 char mac_pathname[MAXPATHLEN+1];
495 CInfoPBRec cipb;
497 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
498 return -1;
500 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
501 if (len > -1)
502 fully_resolved_name[len] = '\0';
503 else
504 strcpy (fully_resolved_name, true_pathname);
506 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
507 return -1;
509 c2pstr (mac_pathname);
510 cipb.hFileInfo.ioNamePtr = mac_pathname;
511 cipb.hFileInfo.ioVRefNum = 0;
512 cipb.hFileInfo.ioDirID = 0;
513 cipb.hFileInfo.ioFDirIndex = 0;
514 /* set to 0 to get information about specific dir or file */
516 errno = PBGetCatInfo (&cipb, false);
517 if (errno != noErr)
518 return -1;
520 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* bit 4 = 1 for directories */
522 if (times)
523 cipb.dirInfo.ioDrMdDat = times->modtime + MAC_UNIX_EPOCH_DIFF;
524 else
525 GetDateTime (&cipb.dirInfo.ioDrMdDat);
527 else
529 if (times)
530 cipb.hFileInfo.ioFlMdDat = times->modtime + MAC_UNIX_EPOCH_DIFF;
531 else
532 GetDateTime (&cipb.hFileInfo.ioFlMdDat);
535 errno = PBSetCatInfo (&cipb, false);
536 return errno == noErr ? 0 : -1;
540 #ifndef F_OK
541 #define F_OK 0
542 #endif
543 #ifndef X_OK
544 #define X_OK 1
545 #endif
546 #ifndef W_OK
547 #define W_OK 2
548 #endif
550 /* Like stat, but test for access mode in hfpb.ioFlAttrib */
552 access (const char *path, int mode)
554 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
555 int len;
556 char mac_pathname[MAXPATHLEN+1];
557 CInfoPBRec cipb;
559 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
560 return -1;
562 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
563 if (len > -1)
564 fully_resolved_name[len] = '\0';
565 else
566 strcpy (fully_resolved_name, true_pathname);
568 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
569 return -1;
571 c2pstr (mac_pathname);
572 cipb.hFileInfo.ioNamePtr = mac_pathname;
573 cipb.hFileInfo.ioVRefNum = 0;
574 cipb.hFileInfo.ioDirID = 0;
575 cipb.hFileInfo.ioFDirIndex = 0;
576 /* set to 0 to get information about specific dir or file */
578 errno = PBGetCatInfo (&cipb, false);
579 if (errno != noErr)
580 return -1;
582 if (mode == F_OK) /* got this far, file exists */
583 return 0;
585 if (mode & X_OK)
586 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* path refers to a directory */
587 return 0;
588 else
590 if (cipb.hFileInfo.ioFlFndrInfo.fdType == 'APPL')
591 return 0;
592 else
593 return -1;
596 if (mode & W_OK)
597 return (cipb.hFileInfo.ioFlAttrib & 0x1) ? -1 : 0;
598 /* don't allow if lock bit is on */
600 return -1;
604 #define DEV_NULL_FD 0x10000
606 #undef open
608 sys_open (const char *path, int oflag)
610 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
611 int len;
612 char mac_pathname[MAXPATHLEN+1];
614 if (strcmp (path, "/dev/null") == 0)
615 return DEV_NULL_FD; /* some bogus fd to be ignored in write */
617 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
618 return -1;
620 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
621 if (len > -1)
622 fully_resolved_name[len] = '\0';
623 else
624 strcpy (fully_resolved_name, true_pathname);
626 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
627 return -1;
628 else
630 #ifdef __MRC__
631 int res = open (mac_pathname, oflag);
632 /* if (oflag == O_WRONLY || oflag == O_RDWR) */
633 if (oflag & O_CREAT)
634 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
635 return res;
636 #else /* not __MRC__ */
637 return open (mac_pathname, oflag);
638 #endif /* not __MRC__ */
643 #undef creat
645 sys_creat (const char *path, mode_t mode)
647 char true_pathname[MAXPATHLEN+1];
648 int len;
649 char mac_pathname[MAXPATHLEN+1];
651 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
652 return -1;
654 if (!posix_to_mac_pathname (true_pathname, mac_pathname, MAXPATHLEN+1))
655 return -1;
656 else
658 #ifdef __MRC__
659 int result = creat (mac_pathname);
660 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
661 return result;
662 #else /* not __MRC__ */
663 return creat (mac_pathname, mode);
664 #endif /* not __MRC__ */
669 #undef unlink
671 sys_unlink (const char *path)
673 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
674 int len;
675 char mac_pathname[MAXPATHLEN+1];
677 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
678 return -1;
680 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
681 if (len > -1)
682 fully_resolved_name[len] = '\0';
683 else
684 strcpy (fully_resolved_name, true_pathname);
686 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
687 return -1;
688 else
689 return unlink (mac_pathname);
693 #undef read
695 sys_read (int fildes, char *buf, int count)
697 if (fildes == 0) /* this should not be used for console input */
698 return -1;
699 else
700 #if __MSL__ >= 0x6000
701 return _read (fildes, buf, count);
702 #else
703 return read (fildes, buf, count);
704 #endif
708 #undef write
710 sys_write (int fildes, const char *buf, int count)
712 if (fildes == DEV_NULL_FD)
713 return count;
714 else
715 #if __MSL__ >= 0x6000
716 return _write (fildes, buf, count);
717 #else
718 return write (fildes, buf, count);
719 #endif
723 #undef rename
725 sys_rename (const char * old_name, const char * new_name)
727 char true_old_pathname[MAXPATHLEN+1], true_new_pathname[MAXPATHLEN+1];
728 char fully_resolved_old_name[MAXPATHLEN+1];
729 int len;
730 char mac_old_name[MAXPATHLEN+1], mac_new_name[MAXPATHLEN+1];
732 if (find_true_pathname (old_name, true_old_pathname, MAXPATHLEN+1) == -1)
733 return -1;
735 len = readlink (true_old_pathname, fully_resolved_old_name, MAXPATHLEN);
736 if (len > -1)
737 fully_resolved_old_name[len] = '\0';
738 else
739 strcpy (fully_resolved_old_name, true_old_pathname);
741 if (find_true_pathname (new_name, true_new_pathname, MAXPATHLEN+1) == -1)
742 return -1;
744 if (strcmp (fully_resolved_old_name, true_new_pathname) == 0)
745 return 0;
747 if (!posix_to_mac_pathname (fully_resolved_old_name,
748 mac_old_name,
749 MAXPATHLEN+1))
750 return -1;
752 if (!posix_to_mac_pathname(true_new_pathname, mac_new_name, MAXPATHLEN+1))
753 return -1;
755 /* If a file with new_name already exists, rename deletes the old
756 file in Unix. CW version fails in these situation. So we add a
757 call to unlink here. */
758 (void) unlink (mac_new_name);
760 return rename (mac_old_name, mac_new_name);
764 #undef fopen
765 extern FILE *fopen (const char *name, const char *mode);
766 FILE *
767 sys_fopen (const char *name, const char *mode)
769 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
770 int len;
771 char mac_pathname[MAXPATHLEN+1];
773 if (find_true_pathname (name, true_pathname, MAXPATHLEN+1) == -1)
774 return 0;
776 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
777 if (len > -1)
778 fully_resolved_name[len] = '\0';
779 else
780 strcpy (fully_resolved_name, true_pathname);
782 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
783 return 0;
784 else
786 #ifdef __MRC__
787 if (mode[0] == 'w' || mode[0] == 'a')
788 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
789 #endif /* not __MRC__ */
790 return fopen (mac_pathname, mode);
795 #include <Events.h>
797 long target_ticks = 0;
799 #ifdef __MRC__
800 __sigfun alarm_signal_func = (__sigfun) 0;
801 #elif __MWERKS__
802 __signal_func_ptr alarm_signal_func = (__signal_func_ptr) 0;
803 #else /* not __MRC__ and not __MWERKS__ */
804 You lose!!!
805 #endif /* not __MRC__ and not __MWERKS__ */
808 /* These functions simulate SIG_ALRM. The stub for function signal
809 stores the signal handler function in alarm_signal_func if a
810 SIG_ALRM is encountered. check_alarm is called in XTread_socket,
811 which emacs calls periodically. A pending alarm is represented by
812 a non-zero target_ticks value. check_alarm calls the handler
813 function pointed to by alarm_signal_func if one has been set up and
814 an alarm is pending. */
816 void
817 check_alarm ()
819 if (target_ticks && TickCount () > target_ticks)
821 target_ticks = 0;
822 if (alarm_signal_func)
823 (*alarm_signal_func)(SIGALRM);
829 select (n, rfds, wfds, efds, timeout)
830 int n;
831 SELECT_TYPE *rfds;
832 SELECT_TYPE *wfds;
833 SELECT_TYPE *efds;
834 struct timeval *timeout;
836 #ifdef TARGET_API_MAC_CARBON
837 return 1;
838 #else /* not TARGET_API_MAC_CARBON */
839 EMACS_TIME end_time, now;
840 EventRecord e;
842 /* Can only handle wait for keyboard input. */
843 if (n > 1 || wfds || efds)
844 return -1;
846 EMACS_GET_TIME (end_time);
847 EMACS_ADD_TIME (end_time, end_time, *timeout);
851 /* Also return true if an event other than a keyDown has
852 occurred. This causes kbd_buffer_get_event in keyboard.c to
853 call read_avail_input which in turn calls XTread_socket to
854 poll for these events. Otherwise these never get processed
855 except but a very slow poll timer. */
856 if (FD_ISSET (0, rfds) && EventAvail (everyEvent, &e))
857 return 1;
859 /* Also check movement of the mouse. */
861 Point mouse_pos;
862 static Point old_mouse_pos = {-1, -1};
864 GetMouse (&mouse_pos);
865 if (!EqualPt (mouse_pos, old_mouse_pos))
867 old_mouse_pos = mouse_pos;
868 return 1;
872 WaitNextEvent (0, &e, 1UL, NULL); /* Accept no event; wait 1
873 tic. by T.I. */
875 EMACS_GET_TIME (now);
876 EMACS_SUB_TIME (now, end_time, now);
878 while (!EMACS_TIME_NEG_P (now));
880 return 0;
881 #endif /* not TARGET_API_MAC_CARBON */
885 /* Called in sys_select to wait for an alarm signal to arrive. */
888 pause ()
890 EventRecord e;
891 unsigned long tick;
893 if (!target_ticks) /* no alarm pending */
894 return -1;
896 if ((tick = TickCount ()) < target_ticks)
897 WaitNextEvent (0, &e, target_ticks - tick, NULL); /* Accept no event;
898 just wait. by T.I. */
900 target_ticks = 0;
901 if (alarm_signal_func)
902 (*alarm_signal_func)(SIGALRM);
904 return 0;
909 alarm (int seconds)
911 long remaining = target_ticks ? (TickCount () - target_ticks) / 60 : 0;
913 target_ticks = seconds ? TickCount () + 60 * seconds : 0;
915 return (remaining < 0) ? 0 : (unsigned int) remaining;
919 #undef signal
920 #ifdef __MRC__
921 extern __sigfun signal (int signal, __sigfun signal_func);
922 __sigfun
923 sys_signal (int signal_num, __sigfun signal_func)
924 #elif __MWERKS__
925 extern __signal_func_ptr signal (int signal, __signal_func_ptr signal_func);
926 __signal_func_ptr
927 sys_signal (int signal_num, __signal_func_ptr signal_func)
928 #else /* not __MRC__ and not __MWERKS__ */
929 You lose!!!
930 #endif /* not __MRC__ and not __MWERKS__ */
932 if (signal_num != SIGALRM)
933 return signal (signal_num, signal_func);
934 else
936 #ifdef __MRC__
937 __sigfun old_signal_func;
938 #elif __MWERKS__
939 __signal_func_ptr old_signal_func;
940 #else
941 You lose!!!
942 #endif
943 old_signal_func = alarm_signal_func;
944 alarm_signal_func = signal_func;
945 return old_signal_func;
950 /* gettimeofday should return the amount of time (in a timeval
951 structure) since midnight today. The toolbox function Microseconds
952 returns the number of microseconds (in a UnsignedWide value) since
953 the machine was booted. Also making this complicated is WideAdd,
954 WideSubtract, etc. take wide values. */
957 gettimeofday (tp)
958 struct timeval *tp;
960 static inited = 0;
961 static wide wall_clock_at_epoch, clicks_at_epoch;
962 UnsignedWide uw_microseconds;
963 wide w_microseconds;
964 time_t sys_time (time_t *);
966 /* If this function is called for the first time, record the number
967 of seconds since midnight and the number of microseconds since
968 boot at the time of this first call. */
969 if (!inited)
971 time_t systime;
972 inited = 1;
973 systime = sys_time (NULL);
974 /* Store microseconds since midnight in wall_clock_at_epoch. */
975 WideMultiply (systime, 1000000L, &wall_clock_at_epoch);
976 Microseconds (&uw_microseconds);
977 /* Store microseconds since boot in clicks_at_epoch. */
978 clicks_at_epoch.hi = uw_microseconds.hi;
979 clicks_at_epoch.lo = uw_microseconds.lo;
982 /* Get time since boot */
983 Microseconds (&uw_microseconds);
985 /* Convert to time since midnight*/
986 w_microseconds.hi = uw_microseconds.hi;
987 w_microseconds.lo = uw_microseconds.lo;
988 WideSubtract (&w_microseconds, &clicks_at_epoch);
989 WideAdd (&w_microseconds, &wall_clock_at_epoch);
990 tp->tv_sec = WideDivide (&w_microseconds, 1000000L, &tp->tv_usec);
992 return 0;
996 #ifdef __MRC__
997 unsigned int
998 sleep (unsigned int seconds)
1000 unsigned long time_up;
1001 EventRecord e;
1003 time_up = TickCount () + seconds * 60;
1004 while (TickCount () < time_up)
1006 /* Accept no event; just wait. by T.I. */
1007 WaitNextEvent (0, &e, 30, NULL);
1010 return (0);
1012 #endif /* __MRC__ */
1015 /* The time functions adjust time values according to the difference
1016 between the Unix and CW epoches. */
1018 #undef gmtime
1019 extern struct tm *gmtime (const time_t *);
1020 struct tm *
1021 sys_gmtime (const time_t *timer)
1023 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
1025 return gmtime (&unix_time);
1029 #undef localtime
1030 extern struct tm *localtime (const time_t *);
1031 struct tm *
1032 sys_localtime (const time_t *timer)
1034 #if __MSL__ >= 0x6000
1035 time_t unix_time = *timer;
1036 #else
1037 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
1038 #endif
1040 return localtime (&unix_time);
1044 #undef ctime
1045 extern char *ctime (const time_t *);
1046 char *
1047 sys_ctime (const time_t *timer)
1049 #if __MSL__ >= 0x6000
1050 time_t unix_time = *timer;
1051 #else
1052 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
1053 #endif
1055 return ctime (&unix_time);
1059 #undef time
1060 extern time_t time (time_t *);
1061 time_t
1062 sys_time (time_t *timer)
1064 #if __MSL__ >= 0x6000
1065 time_t mac_time = time (NULL);
1066 #else
1067 time_t mac_time = time (NULL) - CW_OR_MPW_UNIX_EPOCH_DIFF;
1068 #endif
1070 if (timer)
1071 *timer = mac_time;
1073 return mac_time;
1077 /* MPW strftime broken for "%p" format */
1078 #ifdef __MRC__
1079 #undef strftime
1080 #include <time.h>
1081 size_t
1082 sys_strftime (char * s, size_t maxsize, const char * format,
1083 const struct tm * timeptr)
1085 if (strcmp (format, "%p") == 0)
1087 if (maxsize < 3)
1088 return 0;
1089 if (timeptr->tm_hour < 12)
1091 strcpy (s, "AM");
1092 return 2;
1094 else
1096 strcpy (s, "PM");
1097 return 2;
1100 else
1101 return strftime (s, maxsize, format, timeptr);
1103 #endif /* __MRC__ */
1106 /* no subprocesses, empty wait */
1109 wait (int pid)
1111 return 0;
1115 void
1116 croak (char *badfunc)
1118 printf ("%s not yet implemented\r\n", badfunc);
1119 exit (1);
1123 char *
1124 index (const char * str, int chr)
1126 return strchr (str, chr);
1130 char *
1131 mktemp (char *template)
1133 int len, k;
1134 static seqnum = 0;
1136 len = strlen (template);
1137 k = len - 1;
1138 while (k >= 0 && template[k] == 'X')
1139 k--;
1141 k++; /* make k index of first 'X' */
1143 if (k < len)
1145 /* Zero filled, number of digits equal to the number of X's. */
1146 sprintf (&template[k], "%0*d", len-k, seqnum++);
1148 return template;
1150 else
1151 return 0;
1155 /* Emulate getpwuid, getpwnam and others. */
1157 #define PASSWD_FIELD_SIZE 256
1159 static char my_passwd_name[PASSWD_FIELD_SIZE];
1160 static char my_passwd_dir[MAXPATHLEN+1];
1162 static struct passwd my_passwd =
1164 my_passwd_name,
1165 my_passwd_dir,
1168 static struct group my_group =
1170 /* There are no groups on the mac, so we just return "root" as the
1171 group name. */
1172 "root",
1176 /* Initialized by main () in macterm.c to pathname of emacs directory. */
1178 char emacs_passwd_dir[MAXPATHLEN+1];
1180 char *
1181 getwd (char *);
1183 void
1184 init_emacs_passwd_dir ()
1186 int found = false;
1188 if (getwd (emacs_passwd_dir) && getwd (my_passwd_dir))
1190 /* Need pathname of first ancestor that begins with "emacs"
1191 since Mac emacs application is somewhere in the emacs-*
1192 tree. */
1193 int len = strlen (emacs_passwd_dir);
1194 int j = len - 1;
1195 /* j points to the "/" following the directory name being
1196 compared. */
1197 int i = j - 1;
1198 while (i >= 0 && !found)
1200 while (i >= 0 && emacs_passwd_dir[i] != '/')
1201 i--;
1202 if (emacs_passwd_dir[i] == '/' && i+5 < len)
1203 found = (strncmp (&(emacs_passwd_dir[i+1]), "emacs", 5) == 0);
1204 if (found)
1205 emacs_passwd_dir[j+1] = '\0';
1206 else
1208 j = i;
1209 i = j - 1;
1214 if (!found)
1216 /* Setting to "/" probably won't work but set it to something
1217 anyway. */
1218 strcpy (emacs_passwd_dir, "/");
1219 strcpy (my_passwd_dir, "/");
1224 static struct passwd emacs_passwd =
1226 "emacs",
1227 emacs_passwd_dir,
1230 static int my_passwd_inited = 0;
1233 static void
1234 init_my_passwd ()
1236 char **owner_name;
1238 /* Note: my_passwd_dir initialized in int_emacs_passwd_dir to
1239 directory where Emacs was started. */
1241 owner_name = (char **) GetResource ('STR ',-16096);
1242 if (owner_name)
1244 HLock (owner_name);
1245 BlockMove ((unsigned char *) *owner_name,
1246 (unsigned char *) my_passwd_name,
1247 *owner_name[0]+1);
1248 HUnlock (owner_name);
1249 p2cstr ((unsigned char *) my_passwd_name);
1251 else
1252 my_passwd_name[0] = 0;
1256 struct passwd *
1257 getpwuid (uid_t uid)
1259 if (!my_passwd_inited)
1261 init_my_passwd ();
1262 my_passwd_inited = 1;
1265 return &my_passwd;
1269 struct group *
1270 getgrgid (gid_t gid)
1272 return &my_group;
1276 struct passwd *
1277 getpwnam (const char *name)
1279 if (strcmp (name, "emacs") == 0)
1280 return &emacs_passwd;
1282 if (!my_passwd_inited)
1284 init_my_passwd ();
1285 my_passwd_inited = 1;
1288 return &my_passwd;
1292 /* The functions fork, kill, sigsetmask, sigblock, request_sigio,
1293 setpgrp, setpriority, and unrequest_sigio are defined to be empty
1294 as in msdos.c. */
1298 fork ()
1300 return -1;
1305 kill (int x, int y)
1307 return -1;
1311 void
1312 sys_subshell ()
1314 error ("Can't spawn subshell");
1319 sigsetmask (int x)
1321 return 0;
1326 sigblock (int mask)
1328 return 0;
1332 void
1333 request_sigio (void)
1338 void
1339 unrequest_sigio (void)
1345 setpgrp ()
1347 return 0;
1351 /* No pipes yet. */
1354 pipe (int _fildes[2])
1356 errno = EACCES;
1357 return -1;
1361 /* Hard and symbolic links. */
1364 symlink (const char *name1, const char *name2)
1366 errno = ENOENT;
1367 return -1;
1372 link (const char *name1, const char *name2)
1374 errno = ENOENT;
1375 return -1;
1378 #endif /* ! MAC_OSX */
1380 /* Determine the path name of the file specified by VREFNUM, DIRID,
1381 and NAME and place that in the buffer PATH of length
1382 MAXPATHLEN. */
1384 path_from_vol_dir_name (char *path, int man_path_len, short vol_ref_num,
1385 long dir_id, ConstStr255Param name)
1387 Str255 dir_name;
1388 CInfoPBRec cipb;
1389 OSErr err;
1391 if (strlen (name) > man_path_len)
1392 return 0;
1394 memcpy (dir_name, name, name[0]+1);
1395 memcpy (path, name, name[0]+1);
1396 p2cstr (path);
1398 cipb.dirInfo.ioDrParID = dir_id;
1399 cipb.dirInfo.ioNamePtr = dir_name;
1403 cipb.dirInfo.ioVRefNum = vol_ref_num;
1404 cipb.dirInfo.ioFDirIndex = -1;
1405 cipb.dirInfo.ioDrDirID = cipb.dirInfo.ioDrParID;
1406 /* go up to parent each time */
1408 err = PBGetCatInfo (&cipb, false);
1409 if (err != noErr)
1410 return 0;
1412 p2cstr (dir_name);
1413 if (strlen (dir_name) + strlen (path) + 1 >= man_path_len)
1414 return 0;
1416 strcat (dir_name, ":");
1417 strcat (dir_name, path);
1418 /* attach to front since we're going up directory tree */
1419 strcpy (path, dir_name);
1421 while (cipb.dirInfo.ioDrDirID != fsRtDirID);
1422 /* stop when we see the volume's root directory */
1424 return 1; /* success */
1427 #ifndef MAC_OSX
1430 readlink (const char *path, char *buf, int bufsiz)
1432 char mac_sym_link_name[MAXPATHLEN+1];
1433 OSErr err;
1434 FSSpec fsspec;
1435 Boolean target_is_folder, was_aliased;
1436 Str255 directory_name, mac_pathname;
1437 CInfoPBRec cipb;
1439 if (posix_to_mac_pathname (path, mac_sym_link_name, MAXPATHLEN+1) == 0)
1440 return -1;
1442 c2pstr (mac_sym_link_name);
1443 err = FSMakeFSSpec (0, 0, mac_sym_link_name, &fsspec);
1444 if (err != noErr)
1446 errno = ENOENT;
1447 return -1;
1450 err = ResolveAliasFile (&fsspec, true, &target_is_folder, &was_aliased);
1451 if (err != noErr || !was_aliased)
1453 errno = ENOENT;
1454 return -1;
1457 if (path_from_vol_dir_name (mac_pathname, 255, fsspec.vRefNum, fsspec.parID,
1458 fsspec.name) == 0)
1460 errno = ENOENT;
1461 return -1;
1464 if (mac_to_posix_pathname (mac_pathname, buf, bufsiz) == 0)
1466 errno = ENOENT;
1467 return -1;
1470 return strlen (buf);
1474 /* Convert a path to one with aliases fully expanded. */
1476 static int
1477 find_true_pathname (const char *path, char *buf, int bufsiz)
1479 char *q, temp[MAXPATHLEN+1];
1480 const char *p;
1481 int len;
1483 if (bufsiz <= 0 || path == 0 || path[0] == '\0')
1484 return -1;
1486 buf[0] = '\0';
1488 p = path;
1489 if (*p == '/')
1490 q = strchr (p + 1, '/');
1491 else
1492 q = strchr (p, '/');
1493 len = 0; /* loop may not be entered, e.g., for "/" */
1495 while (q)
1497 strcpy (temp, buf);
1498 strncat (temp, p, q - p);
1499 len = readlink (temp, buf, bufsiz);
1500 if (len <= -1)
1502 if (strlen (temp) + 1 > bufsiz)
1503 return -1;
1504 strcpy (buf, temp);
1506 strcat (buf, "/");
1507 len++;
1508 p = q + 1;
1509 q = strchr(p, '/');
1512 if (len + strlen (p) + 1 >= bufsiz)
1513 return -1;
1515 strcat (buf, p);
1516 return len + strlen (p);
1520 mode_t
1521 umask (mode_t numask)
1523 static mode_t mask = 022;
1524 mode_t oldmask = mask;
1525 mask = numask;
1526 return oldmask;
1531 chmod (const char *path, mode_t mode)
1533 /* say it always succeed for now */
1534 return 0;
1539 dup (int oldd)
1541 #ifdef __MRC__
1542 return fcntl (oldd, F_DUPFD, 0);
1543 #elif __MWERKS__
1544 /* current implementation of fcntl in fcntl.mac.c simply returns old
1545 descriptor */
1546 return fcntl (oldd, F_DUPFD);
1547 #else
1548 You lose!!!
1549 #endif
1553 /* This is from the original sysdep.c. Emulate BSD dup2. First close
1554 newd if it already exists. Then, attempt to dup oldd. If not
1555 successful, call dup2 recursively until we are, then close the
1556 unsuccessful ones. */
1559 dup2 (int oldd, int newd)
1561 int fd, ret;
1563 close (newd);
1565 fd = dup (oldd);
1566 if (fd == -1)
1567 return -1;
1568 if (fd == newd)
1569 return newd;
1570 ret = dup2 (oldd, newd);
1571 close (fd);
1572 return ret;
1576 /* let it fail for now */
1578 char *
1579 sbrk (int incr)
1581 return (char *) -1;
1586 fsync (int fd)
1588 return 0;
1593 ioctl (int d, int request, void *argp)
1595 return -1;
1599 #ifdef __MRC__
1601 isatty (int fildes)
1603 if (fildes >=0 && fildes <= 2)
1604 return 1;
1605 else
1606 return 0;
1611 getgid ()
1613 return 100;
1618 getegid ()
1620 return 100;
1625 getuid ()
1627 return 200;
1632 geteuid ()
1634 return 200;
1636 #endif /* __MRC__ */
1639 #ifdef __MWERKS__
1640 #if __MSL__ < 0x6000
1641 #undef getpid
1643 getpid ()
1645 return 9999;
1647 #endif
1648 #endif /* __MWERKS__ */
1650 #endif /* ! MAC_OSX */
1653 /* Return the path to the directory in which Emacs can create
1654 temporary files. The MacOS "temporary items" directory cannot be
1655 used because it removes the file written by a process when it
1656 exits. In that sense it's more like "/dev/null" than "/tmp" (but
1657 again not exactly). And of course Emacs needs to read back the
1658 files written by its subprocesses. So here we write the files to a
1659 directory "Emacs" in the Preferences Folder. This directory is
1660 created if it does not exist. */
1662 char *
1663 get_temp_dir_name ()
1665 static char *temp_dir_name = NULL;
1666 short vol_ref_num;
1667 long dir_id;
1668 OSErr err;
1669 Str255 dir_name, full_path;
1670 CInfoPBRec cpb;
1671 char unix_dir_name[MAXPATHLEN+1];
1672 DIR *dir;
1674 /* Cache directory name with pointer temp_dir_name.
1675 Look for it only the first time. */
1676 if (!temp_dir_name)
1678 err = FindFolder (kOnSystemDisk, kPreferencesFolderType, kCreateFolder,
1679 &vol_ref_num, &dir_id);
1680 if (err != noErr)
1681 return NULL;
1683 if (!path_from_vol_dir_name (full_path, 255, vol_ref_num, dir_id, "\p"))
1684 return NULL;
1686 if (strlen (full_path) + 6 <= MAXPATHLEN)
1687 strcat (full_path, "Emacs:");
1688 else
1689 return NULL;
1691 if (!mac_to_posix_pathname (full_path, unix_dir_name, MAXPATHLEN+1))
1692 return NULL;
1694 dir = opendir (unix_dir_name); /* check whether temp directory exists */
1695 if (dir)
1696 closedir (dir);
1697 else if (mkdir (unix_dir_name, 0700) != 0) /* create it if not */
1698 return NULL;
1700 temp_dir_name = (char *) malloc (strlen (unix_dir_name) + 1);
1701 strcpy (temp_dir_name, unix_dir_name);
1704 return temp_dir_name;
1707 #ifndef MAC_OSX
1709 /* Allocate and construct an array of pointers to strings from a list
1710 of strings stored in a 'STR#' resource. The returned pointer array
1711 is stored in the style of argv and environ: if the 'STR#' resource
1712 contains numString strings, a pointer array with numString+1
1713 elements is returned in which the last entry contains a null
1714 pointer. The pointer to the pointer array is passed by pointer in
1715 parameter t. The resource ID of the 'STR#' resource is passed in
1716 parameter StringListID.
1719 void
1720 get_string_list (char ***t, short string_list_id)
1722 Handle h;
1723 Ptr p;
1724 int i, num_strings;
1726 h = GetResource ('STR#', string_list_id);
1727 if (h)
1729 HLock (h);
1730 p = *h;
1731 num_strings = * (short *) p;
1732 p += sizeof(short);
1733 *t = (char **) malloc (sizeof (char *) * (num_strings + 1));
1734 for (i = 0; i < num_strings; i++)
1736 short length = *p++;
1737 (*t)[i] = (char *) malloc (length + 1);
1738 strncpy ((*t)[i], p, length);
1739 (*t)[i][length] = '\0';
1740 p += length;
1742 (*t)[num_strings] = 0;
1743 HUnlock (h);
1745 else
1747 /* Return no string in case GetResource fails. Bug fixed by
1748 Ikegami Tsutomu. Caused MPW build to crash without sym -on
1749 option (no sym -on implies -opt local). */
1750 *t = (char **) malloc (sizeof (char *));
1751 (*t)[0] = 0;
1756 static char *
1757 get_path_to_system_folder ()
1759 short vol_ref_num;
1760 long dir_id;
1761 OSErr err;
1762 Str255 dir_name, full_path;
1763 CInfoPBRec cpb;
1764 static char system_folder_unix_name[MAXPATHLEN+1];
1765 DIR *dir;
1767 err = FindFolder (kOnSystemDisk, kSystemFolderType, kDontCreateFolder,
1768 &vol_ref_num, &dir_id);
1769 if (err != noErr)
1770 return NULL;
1772 if (!path_from_vol_dir_name (full_path, 255, vol_ref_num, dir_id, "\p"))
1773 return NULL;
1775 if (!mac_to_posix_pathname (full_path, system_folder_unix_name,
1776 MAXPATHLEN+1))
1777 return NULL;
1779 return system_folder_unix_name;
1783 char **environ;
1785 #define ENVIRON_STRING_LIST_ID 128
1787 /* Get environment variable definitions from STR# resource. */
1789 void
1790 init_environ ()
1792 int i;
1794 get_string_list (&environ, ENVIRON_STRING_LIST_ID);
1796 i = 0;
1797 while (environ[i])
1798 i++;
1800 /* Make HOME directory the one Emacs starts up in if not specified
1801 by resource. */
1802 if (getenv ("HOME") == NULL)
1804 environ = (char **) realloc (environ, sizeof (char *) * (i + 2));
1805 if (environ)
1807 environ[i] = (char *) malloc (strlen (my_passwd_dir) + 6);
1808 if (environ[i])
1810 strcpy (environ[i], "HOME=");
1811 strcat (environ[i], my_passwd_dir);
1813 environ[i+1] = 0;
1814 i++;
1818 /* Make HOME directory the one Emacs starts up in if not specified
1819 by resource. */
1820 if (getenv ("MAIL") == NULL)
1822 environ = (char **) realloc (environ, sizeof (char *) * (i + 2));
1823 if (environ)
1825 char * path_to_system_folder = get_path_to_system_folder ();
1826 environ[i] = (char *) malloc (strlen (path_to_system_folder) + 22);
1827 if (environ[i])
1829 strcpy (environ[i], "MAIL=");
1830 strcat (environ[i], path_to_system_folder);
1831 strcat (environ[i], "Eudora Folder/In");
1833 environ[i+1] = 0;
1839 /* Return the value of the environment variable NAME. */
1841 char *
1842 getenv (const char *name)
1844 int length = strlen(name);
1845 char **e;
1847 for (e = environ; *e != 0; e++)
1848 if (strncmp(*e, name, length) == 0 && (*e)[length] == '=')
1849 return &(*e)[length + 1];
1851 if (strcmp (name, "TMPDIR") == 0)
1852 return get_temp_dir_name ();
1854 return 0;
1858 #ifdef __MRC__
1859 /* see Interfaces&Libraries:Interfaces:CIncludes:signal.h */
1860 char *sys_siglist[] =
1862 "Zero is not a signal!!!",
1863 "Abort", /* 1 */
1864 "Interactive user interrupt", /* 2 */ "?",
1865 "Floating point exception", /* 4 */ "?", "?", "?",
1866 "Illegal instruction", /* 8 */ "?", "?", "?", "?", "?", "?", "?",
1867 "Segment violation", /* 16 */ "?", "?", "?", "?", "?", "?", "?",
1868 "?", "?", "?", "?", "?", "?", "?", "?",
1869 "Terminal" /* 32 */
1871 #elif __MWERKS__
1872 char *sys_siglist[] =
1874 "Zero is not a signal!!!",
1875 "Abort",
1876 "Floating point exception",
1877 "Illegal instruction",
1878 "Interactive user interrupt",
1879 "Segment violation",
1880 "Terminal"
1882 #else /* not __MRC__ and not __MWERKS__ */
1883 You lose!!!
1884 #endif /* not __MRC__ and not __MWERKS__ */
1887 #include <utsname.h>
1890 uname (struct utsname *name)
1892 char **system_name;
1893 system_name = GetString (-16413); /* IM - Resource Manager Reference */
1894 if (system_name)
1896 BlockMove (*system_name, name->nodename, (*system_name)[0]+1);
1897 p2cstr (name->nodename);
1898 return 0;
1900 else
1901 return -1;
1905 #include <Processes.h>
1906 #include <EPPC.h>
1908 /* Event class of HLE sent to subprocess. */
1909 const OSType kEmacsSubprocessSend = 'ESND';
1911 /* Event class of HLE sent back from subprocess. */
1912 const OSType kEmacsSubprocessReply = 'ERPY';
1915 char *
1916 mystrchr (char *s, char c)
1918 while (*s && *s != c)
1920 if (*s == '\\')
1921 s++;
1922 s++;
1925 if (*s)
1927 *s = '\0';
1928 return s;
1930 else
1931 return NULL;
1935 char *
1936 mystrtok (char *s)
1938 while (*s)
1939 s++;
1941 return s + 1;
1945 void
1946 mystrcpy (char *to, char *from)
1948 while (*from)
1950 if (*from == '\\')
1951 from++;
1952 *to++ = *from++;
1954 *to = '\0';
1958 /* Start a Mac subprocess. Arguments for it is passed in argv (null
1959 terminated). The process should run with the default directory
1960 "workdir", read input from "infn", and write output and error to
1961 "outfn" and "errfn", resp. The Process Manager call
1962 LaunchApplication is used to start the subprocess. We use high
1963 level events as the mechanism to pass arguments to the subprocess
1964 and to make Emacs wait for the subprocess to terminate and pass
1965 back a result code. The bulk of the code here packs the arguments
1966 into one message to be passed together with the high level event.
1967 Emacs also sometimes starts a subprocess using a shell to perform
1968 wildcard filename expansion. Since we don't really have a shell on
1969 the Mac, this case is detected and the starting of the shell is
1970 by-passed. We really need to add code here to do filename
1971 expansion to support such functionality. */
1974 run_mac_command (argv, workdir, infn, outfn, errfn)
1975 unsigned char **argv;
1976 const char *workdir;
1977 const char *infn, *outfn, *errfn;
1979 #ifdef TARGET_API_MAC_CARBON
1980 return -1;
1981 #else /* not TARGET_API_MAC_CARBON */
1982 char macappname[MAXPATHLEN+1], macworkdir[MAXPATHLEN+1];
1983 char macinfn[MAXPATHLEN+1], macoutfn[MAXPATHLEN+1], macerrfn[MAXPATHLEN+1];
1984 int paramlen, argc, newargc, j, retries;
1985 char **newargv, *param, *p;
1986 OSErr iErr;
1987 FSSpec spec;
1988 LaunchParamBlockRec lpbr;
1989 EventRecord send_event, reply_event;
1990 RgnHandle cursor_region_handle;
1991 TargetID targ;
1992 unsigned long ref_con, len;
1994 if (posix_to_mac_pathname (workdir, macworkdir, MAXPATHLEN+1) == 0)
1995 return -1;
1996 if (posix_to_mac_pathname (infn, macinfn, MAXPATHLEN+1) == 0)
1997 return -1;
1998 if (posix_to_mac_pathname (outfn, macoutfn, MAXPATHLEN+1) == 0)
1999 return -1;
2000 if (posix_to_mac_pathname (errfn, macerrfn, MAXPATHLEN+1) == 0)
2001 return -1;
2003 paramlen = strlen (macworkdir) + strlen (macinfn) + strlen (macoutfn)
2004 + strlen (macerrfn) + 4; /* count nulls at end of strings */
2006 argc = 0;
2007 while (argv[argc])
2008 argc++;
2010 if (argc == 0)
2011 return -1;
2013 /* If a subprocess is invoked with a shell, we receive 3 arguments
2014 of the form: "<path to emacs bins>/sh" "-c" "<path to emacs
2015 bins>/<command> <command args>" */
2016 j = strlen (argv[0]);
2017 if (j >= 3 && strcmp (argv[0]+j-3, "/sh") == 0
2018 && argc == 3 && strcmp (argv[1], "-c") == 0)
2020 char *command, *t, tempmacpathname[MAXPATHLEN+1];
2022 /* The arguments for the command in argv[2] are separated by
2023 spaces. Count them and put the count in newargc. */
2024 command = (char *) alloca (strlen (argv[2])+2);
2025 strcpy (command, argv[2]);
2026 if (command[strlen (command) - 1] != ' ')
2027 strcat (command, " ");
2029 t = command;
2030 newargc = 0;
2031 t = mystrchr (t, ' ');
2032 while (t)
2034 newargc++;
2035 t = mystrchr (t+1, ' ');
2038 newargv = (char **) alloca (sizeof (char *) * newargc);
2040 t = command;
2041 for (j = 0; j < newargc; j++)
2043 newargv[j] = (char *) alloca (strlen (t) + 1);
2044 mystrcpy (newargv[j], t);
2046 t = mystrtok (t);
2047 paramlen += strlen (newargv[j]) + 1;
2050 if (strncmp (newargv[0], "~emacs/", 7) == 0)
2052 if (posix_to_mac_pathname (newargv[0], tempmacpathname, MAXPATHLEN+1)
2053 == 0)
2054 return -1;
2056 else
2057 { /* sometimes Emacs call "sh" without a path for the command */
2058 #if 0
2059 char *t = (char *) alloca (strlen (newargv[0]) + 7 + 1);
2060 strcpy (t, "~emacs/");
2061 strcat (t, newargv[0]);
2062 #endif /* 0 */
2063 Lisp_Object path;
2064 openp (Vexec_path, build_string (newargv[0]), EXEC_SUFFIXES, &path,
2065 make_number (X_OK));
2067 if (NILP (path))
2068 return -1;
2069 if (posix_to_mac_pathname (SDATA (path), tempmacpathname,
2070 MAXPATHLEN+1) == 0)
2071 return -1;
2073 strcpy (macappname, tempmacpathname);
2075 else
2077 if (posix_to_mac_pathname (argv[0], macappname, MAXPATHLEN+1) == 0)
2078 return -1;
2080 newargv = (char **) alloca (sizeof (char *) * argc);
2081 newargc = argc;
2082 for (j = 1; j < argc; j++)
2084 if (strncmp (argv[j], "~emacs/", 7) == 0)
2086 char *t = strchr (argv[j], ' ');
2087 if (t)
2089 char tempcmdname[MAXPATHLEN+1], tempmaccmdname[MAXPATHLEN+1];
2090 strncpy (tempcmdname, argv[j], t-argv[j]);
2091 tempcmdname[t-argv[j]] = '\0';
2092 if (posix_to_mac_pathname (tempcmdname, tempmaccmdname,
2093 MAXPATHLEN+1) == 0)
2094 return -1;
2095 newargv[j] = (char *) alloca (strlen (tempmaccmdname)
2096 + strlen (t) + 1);
2097 strcpy (newargv[j], tempmaccmdname);
2098 strcat (newargv[j], t);
2100 else
2102 char tempmaccmdname[MAXPATHLEN+1];
2103 if (posix_to_mac_pathname (argv[j], tempmaccmdname,
2104 MAXPATHLEN+1) == 0)
2105 return -1;
2106 newargv[j] = (char *) alloca (strlen (tempmaccmdname)+1);
2107 strcpy (newargv[j], tempmaccmdname);
2110 else
2111 newargv[j] = argv[j];
2112 paramlen += strlen (newargv[j]) + 1;
2116 /* After expanding all the arguments, we now know the length of the
2117 parameter block to be sent to the subprocess as a message
2118 attached to the HLE. */
2119 param = (char *) malloc (paramlen + 1);
2120 if (!param)
2121 return -1;
2123 p = param;
2124 *p++ = newargc;
2125 /* first byte of message contains number of arguments for command */
2126 strcpy (p, macworkdir);
2127 p += strlen (macworkdir);
2128 *p++ = '\0';
2129 /* null terminate strings sent so it's possible to use strcpy over there */
2130 strcpy (p, macinfn);
2131 p += strlen (macinfn);
2132 *p++ = '\0';
2133 strcpy (p, macoutfn);
2134 p += strlen (macoutfn);
2135 *p++ = '\0';
2136 strcpy (p, macerrfn);
2137 p += strlen (macerrfn);
2138 *p++ = '\0';
2139 for (j = 1; j < newargc; j++)
2141 strcpy (p, newargv[j]);
2142 p += strlen (newargv[j]);
2143 *p++ = '\0';
2146 c2pstr (macappname);
2148 iErr = FSMakeFSSpec (0, 0, macappname, &spec);
2150 if (iErr != noErr)
2152 free (param);
2153 return -1;
2156 lpbr.launchBlockID = extendedBlock;
2157 lpbr.launchEPBLength = extendedBlockLen;
2158 lpbr.launchControlFlags = launchContinue + launchNoFileFlags;
2159 lpbr.launchAppSpec = &spec;
2160 lpbr.launchAppParameters = NULL;
2162 iErr = LaunchApplication (&lpbr); /* call the subprocess */
2163 if (iErr != noErr)
2165 free (param);
2166 return -1;
2169 send_event.what = kHighLevelEvent;
2170 send_event.message = kEmacsSubprocessSend;
2171 /* Event ID stored in "where" unused */
2173 retries = 3;
2174 /* OS may think current subprocess has terminated if previous one
2175 terminated recently. */
2178 iErr = PostHighLevelEvent (&send_event, &lpbr.launchProcessSN, 0, param,
2179 paramlen + 1, receiverIDisPSN);
2181 while (iErr == sessClosedErr && retries-- > 0);
2183 if (iErr != noErr)
2185 free (param);
2186 return -1;
2189 cursor_region_handle = NewRgn ();
2191 /* Wait for the subprocess to finish, when it will send us a ERPY
2192 high level event. */
2193 while (1)
2194 if (WaitNextEvent (highLevelEventMask, &reply_event, 180,
2195 cursor_region_handle)
2196 && reply_event.message == kEmacsSubprocessReply)
2197 break;
2199 /* The return code is sent through the refCon */
2200 iErr = AcceptHighLevelEvent (&targ, &ref_con, NULL, &len);
2201 if (iErr != noErr)
2203 DisposeHandle ((Handle) cursor_region_handle);
2204 free (param);
2205 return -1;
2208 DisposeHandle ((Handle) cursor_region_handle);
2209 free (param);
2211 return ref_con;
2212 #endif /* not TARGET_API_MAC_CARBON */
2216 DIR *
2217 opendir (const char *dirname)
2219 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
2220 char mac_pathname[MAXPATHLEN+1], vol_name[MAXPATHLEN+1];
2221 DIR *dirp;
2222 CInfoPBRec cipb;
2223 HVolumeParam vpb;
2224 int len, vol_name_len;
2226 if (find_true_pathname (dirname, true_pathname, MAXPATHLEN+1) == -1)
2227 return 0;
2229 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
2230 if (len > -1)
2231 fully_resolved_name[len] = '\0';
2232 else
2233 strcpy (fully_resolved_name, true_pathname);
2235 dirp = (DIR *) malloc (sizeof(DIR));
2236 if (!dirp)
2237 return 0;
2239 /* Handle special case when dirname is "/": sets up for readir to
2240 get all mount volumes. */
2241 if (strcmp (fully_resolved_name, "/") == 0)
2243 dirp->getting_volumes = 1; /* special all mounted volumes DIR struct */
2244 dirp->current_index = 1; /* index for first volume */
2245 return dirp;
2248 /* Handle typical cases: not accessing all mounted volumes. */
2249 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
2250 return 0;
2252 /* Emacs calls opendir without the trailing '/', Mac needs trailing ':' */
2253 len = strlen (mac_pathname);
2254 if (mac_pathname[len - 1] != ':' && len < MAXPATHLEN)
2255 strcat (mac_pathname, ":");
2257 /* Extract volume name */
2258 vol_name_len = strchr (mac_pathname, ':') - mac_pathname;
2259 strncpy (vol_name, mac_pathname, vol_name_len);
2260 vol_name[vol_name_len] = '\0';
2261 strcat (vol_name, ":");
2263 c2pstr (mac_pathname);
2264 cipb.hFileInfo.ioNamePtr = mac_pathname;
2265 /* using full pathname so vRefNum and DirID ignored */
2266 cipb.hFileInfo.ioVRefNum = 0;
2267 cipb.hFileInfo.ioDirID = 0;
2268 cipb.hFileInfo.ioFDirIndex = 0;
2269 /* set to 0 to get information about specific dir or file */
2271 errno = PBGetCatInfo (&cipb, false);
2272 if (errno != noErr)
2274 errno = ENOENT;
2275 return 0;
2278 if (!(cipb.hFileInfo.ioFlAttrib & 0x10)) /* bit 4 = 1 for directories */
2279 return 0; /* not a directory */
2281 dirp->dir_id = cipb.dirInfo.ioDrDirID; /* used later in readdir */
2282 dirp->getting_volumes = 0;
2283 dirp->current_index = 1; /* index for first file/directory */
2285 c2pstr (vol_name);
2286 vpb.ioNamePtr = vol_name;
2287 /* using full pathname so vRefNum and DirID ignored */
2288 vpb.ioVRefNum = 0;
2289 vpb.ioVolIndex = -1;
2290 errno = PBHGetVInfo ((union HParamBlockRec *) &vpb, false);
2291 if (errno != noErr)
2293 errno = ENOENT;
2294 return 0;
2297 dirp->vol_ref_num = vpb.ioVRefNum;
2299 return dirp;
2303 closedir (DIR *dp)
2305 free (dp);
2307 return 0;
2311 struct dirent *
2312 readdir (DIR *dp)
2314 HParamBlockRec hpblock;
2315 CInfoPBRec cipb;
2316 static struct dirent s_dirent;
2317 static Str255 s_name;
2318 int done;
2319 char *p;
2321 /* Handle the root directory containing the mounted volumes. Call
2322 PBHGetVInfo specifying an index to obtain the info for a volume.
2323 PBHGetVInfo returns an error when it receives an index beyond the
2324 last volume, at which time we should return a nil dirent struct
2325 pointer. */
2326 if (dp->getting_volumes)
2328 hpblock.volumeParam.ioNamePtr = s_name;
2329 hpblock.volumeParam.ioVRefNum = 0;
2330 hpblock.volumeParam.ioVolIndex = dp->current_index;
2332 errno = PBHGetVInfo (&hpblock, false);
2333 if (errno != noErr)
2335 errno = ENOENT;
2336 return 0;
2339 p2cstr (s_name);
2340 strcat (s_name, "/"); /* need "/" for stat to work correctly */
2342 dp->current_index++;
2344 s_dirent.d_ino = hpblock.volumeParam.ioVRefNum;
2345 s_dirent.d_name = s_name;
2347 return &s_dirent;
2349 else
2351 cipb.hFileInfo.ioVRefNum = dp->vol_ref_num;
2352 cipb.hFileInfo.ioNamePtr = s_name;
2353 /* location to receive filename returned */
2355 /* return only visible files */
2356 done = false;
2357 while (!done)
2359 cipb.hFileInfo.ioDirID = dp->dir_id;
2360 /* directory ID found by opendir */
2361 cipb.hFileInfo.ioFDirIndex = dp->current_index;
2363 errno = PBGetCatInfo (&cipb, false);
2364 if (errno != noErr)
2366 errno = ENOENT;
2367 return 0;
2370 /* insist on a visible entry */
2371 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* directory? */
2372 done = !(cipb.dirInfo.ioDrUsrWds.frFlags & fInvisible);
2373 else
2374 done = !(cipb.hFileInfo.ioFlFndrInfo.fdFlags & fInvisible);
2376 dp->current_index++;
2379 p2cstr (s_name);
2381 p = s_name;
2382 while (*p)
2384 if (*p == '/')
2385 *p = ':';
2386 p++;
2389 s_dirent.d_ino = cipb.dirInfo.ioDrDirID;
2390 /* value unimportant: non-zero for valid file */
2391 s_dirent.d_name = s_name;
2393 return &s_dirent;
2398 char *
2399 getwd (char *path)
2401 char mac_pathname[MAXPATHLEN+1];
2402 Str255 directory_name;
2403 OSErr errno;
2404 CInfoPBRec cipb;
2406 if (path_from_vol_dir_name (mac_pathname, 255, 0, 0, "\p") == 0)
2407 return NULL;
2409 if (mac_to_posix_pathname (mac_pathname, path, MAXPATHLEN+1) == 0)
2410 return 0;
2411 else
2412 return path;
2415 #endif /* ! MAC_OSX */
2418 void
2419 initialize_applescript ()
2421 AEDesc null_desc;
2422 OSAError osaerror;
2424 /* if open fails, as_scripting_component is set to NULL. Its
2425 subsequent use in OSA calls will fail with badComponentInstance
2426 error. */
2427 as_scripting_component = OpenDefaultComponent (kOSAComponentType,
2428 kAppleScriptSubtype);
2430 null_desc.descriptorType = typeNull;
2431 null_desc.dataHandle = 0;
2432 osaerror = OSAMakeContext (as_scripting_component, &null_desc,
2433 kOSANullScript, &as_script_context);
2434 if (osaerror)
2435 as_script_context = kOSANullScript;
2436 /* use default context if create fails */
2440 void terminate_applescript()
2442 OSADispose (as_scripting_component, as_script_context);
2443 CloseComponent (as_scripting_component);
2447 /* Compile and execute the AppleScript SCRIPT and return the error
2448 status as function value. A zero is returned if compilation and
2449 execution is successful, in which case RESULT returns a pointer to
2450 a string containing the resulting script value. Otherwise, the Mac
2451 error code is returned and RESULT returns a pointer to an error
2452 string. In both cases the caller should deallocate the storage
2453 used by the string pointed to by RESULT if it is non-NULL. For
2454 documentation on the MacOS scripting architecture, see Inside
2455 Macintosh - Interapplication Communications: Scripting Components. */
2457 static long
2458 do_applescript (char *script, char **result)
2460 AEDesc script_desc, result_desc, error_desc;
2461 OSErr error;
2462 OSAError osaerror;
2463 long length;
2465 *result = 0;
2467 if (!as_scripting_component)
2468 initialize_applescript();
2470 error = AECreateDesc (typeChar, script, strlen(script), &script_desc);
2471 if (error)
2472 return error;
2474 osaerror = OSADoScript (as_scripting_component, &script_desc, kOSANullScript,
2475 typeChar, kOSAModeNull, &result_desc);
2477 if (osaerror == errOSAScriptError)
2479 /* error executing AppleScript: retrieve error message */
2480 if (!OSAScriptError (as_scripting_component, kOSAErrorMessage, typeChar,
2481 &error_desc))
2483 #if TARGET_API_MAC_CARBON
2484 length = AEGetDescDataSize (&error_desc);
2485 *result = (char *) xmalloc (length + 1);
2486 if (*result)
2488 AEGetDescData (&error_desc, *result, length);
2489 *(*result + length) = '\0';
2491 #else /* not TARGET_API_MAC_CARBON */
2492 HLock (error_desc.dataHandle);
2493 length = GetHandleSize(error_desc.dataHandle);
2494 *result = (char *) xmalloc (length + 1);
2495 if (*result)
2497 memcpy (*result, *(error_desc.dataHandle), length);
2498 *(*result + length) = '\0';
2500 HUnlock (error_desc.dataHandle);
2501 #endif /* not TARGET_API_MAC_CARBON */
2502 AEDisposeDesc (&error_desc);
2505 else if (osaerror == noErr) /* success: retrieve resulting script value */
2507 #if TARGET_API_MAC_CARBON
2508 length = AEGetDescDataSize (&result_desc);
2509 *result = (char *) xmalloc (length + 1);
2510 if (*result)
2512 AEGetDescData (&result_desc, *result, length);
2513 *(*result + length) = '\0';
2515 #else /* not TARGET_API_MAC_CARBON */
2516 HLock (result_desc.dataHandle);
2517 length = GetHandleSize(result_desc.dataHandle);
2518 *result = (char *) xmalloc (length + 1);
2519 if (*result)
2521 memcpy (*result, *(result_desc.dataHandle), length);
2522 *(*result + length) = '\0';
2524 HUnlock (result_desc.dataHandle);
2525 #endif /* not TARGET_API_MAC_CARBON */
2526 AEDisposeDesc (&result_desc);
2529 AEDisposeDesc (&script_desc);
2531 return osaerror;
2535 DEFUN ("do-applescript", Fdo_applescript, Sdo_applescript, 1, 1, 0,
2536 doc: /* Compile and execute AppleScript SCRIPT and retrieve and return the result.
2537 If compilation and execution are successful, the resulting script
2538 value is returned as a string. Otherwise the function aborts and
2539 displays the error message returned by the AppleScript scripting
2540 component. */)
2541 (script)
2542 Lisp_Object script;
2544 char *result, *temp;
2545 Lisp_Object lisp_result;
2546 long status;
2548 CHECK_STRING (script);
2550 status = do_applescript (SDATA (script), &result);
2551 if (status)
2553 if (!result)
2554 error ("AppleScript error %d", status);
2555 else
2557 /* Unfortunately only OSADoScript in do_applescript knows how
2558 how large the resulting script value or error message is
2559 going to be and therefore as caller memory must be
2560 deallocated here. It is necessary to free the error
2561 message before calling error to avoid a memory leak. */
2562 temp = (char *) alloca (strlen (result) + 1);
2563 strcpy (temp, result);
2564 xfree (result);
2565 error (temp);
2568 else
2570 lisp_result = build_string (result);
2571 xfree (result);
2572 return lisp_result;
2577 DEFUN ("mac-file-name-to-posix", Fmac_file_name_to_posix,
2578 Smac_file_name_to_posix, 1, 1, 0,
2579 doc: /* Convert Macintosh filename to Posix form. */)
2580 (mac_filename)
2581 Lisp_Object mac_filename;
2583 char posix_filename[MAXPATHLEN+1];
2585 CHECK_STRING (mac_filename);
2587 if (mac_to_posix_pathname (SDATA (mac_filename), posix_filename,
2588 MAXPATHLEN))
2589 return build_string (posix_filename);
2590 else
2591 return Qnil;
2595 DEFUN ("posix-file-name-to-mac", Fposix_file_name_to_mac,
2596 Sposix_file_name_to_mac, 1, 1, 0,
2597 doc: /* Convert Posix filename to Mac form. */)
2598 (posix_filename)
2599 Lisp_Object posix_filename;
2601 char mac_filename[MAXPATHLEN+1];
2603 CHECK_STRING (posix_filename);
2605 if (posix_to_mac_pathname (SDATA (posix_filename), mac_filename,
2606 MAXPATHLEN))
2607 return build_string (mac_filename);
2608 else
2609 return Qnil;
2613 /* set interprogram-paste-function to mac-paste-function in mac-win.el
2614 to enable Emacs to obtain the contents of the Mac clipboard. */
2615 DEFUN ("mac-paste-function", Fmac_paste_function, Smac_paste_function, 0, 0, 0,
2616 doc: /* Return the contents of the Mac clipboard as a string. */)
2619 #if TARGET_API_MAC_CARBON
2620 ScrapRef scrap;
2621 ScrapFlavorFlags sff;
2622 Size s;
2623 int i;
2624 char *data;
2626 if (GetCurrentScrap (&scrap) != noErr)
2627 return Qnil;
2629 if (GetScrapFlavorFlags (scrap, kScrapFlavorTypeText, &sff) != noErr)
2630 return Qnil;
2632 if (GetScrapFlavorSize (scrap, kScrapFlavorTypeText, &s) != noErr)
2633 return Qnil;
2635 if ((data = (char*) alloca (s)) == NULL)
2636 return Qnil;
2638 if (GetScrapFlavorData (scrap, kScrapFlavorTypeText, &s, data) != noErr
2639 || s == 0)
2640 return Qnil;
2642 /* Emacs expects clipboard contents have Unix-style eol's */
2643 for (i = 0; i < s; i++)
2644 if (data[i] == '\r')
2645 data[i] = '\n';
2647 return make_string (data, s);
2648 #else /* not TARGET_API_MAC_CARBON */
2649 Lisp_Object value;
2650 Handle my_handle;
2651 long scrap_offset, rc, i;
2653 my_handle = NewHandle (0); /* allocate 0-length data area */
2655 rc = GetScrap (my_handle, 'TEXT', &scrap_offset);
2656 if (rc < 0)
2657 return Qnil;
2659 HLock (my_handle);
2661 /* Emacs expects clipboard contents have Unix-style eol's */
2662 for (i = 0; i < rc; i++)
2663 if ((*my_handle)[i] == '\r')
2664 (*my_handle)[i] = '\n';
2666 value = make_string (*my_handle, rc);
2668 HUnlock (my_handle);
2670 DisposeHandle (my_handle);
2672 return value;
2673 #endif /* not TARGET_API_MAC_CARBON */
2677 /* set interprogram-cut-function to mac-cut-function in mac-win.el
2678 to enable Emacs to write the top of the kill-ring to the Mac clipboard. */
2679 DEFUN ("mac-cut-function", Fmac_cut_function, Smac_cut_function, 1, 2, 0,
2680 doc: /* Put the value of the string parameter to the Mac clipboard. */)
2681 (value, push)
2682 Lisp_Object value, push;
2684 char *buf;
2685 int len, i;
2687 /* fixme: ignore the push flag for now */
2689 CHECK_STRING (value);
2691 len = SCHARS (value);
2692 buf = (char *) alloca (len+1);
2693 bcopy (SDATA (value), buf, len);
2694 buf[len] = '\0';
2696 /* convert to Mac-style eol's before sending to clipboard */
2697 for (i = 0; i < len; i++)
2698 if (buf[i] == '\n')
2699 buf[i] = '\r';
2701 #if TARGET_API_MAC_CARBON
2703 ScrapRef scrap;
2704 ClearCurrentScrap ();
2705 if (GetCurrentScrap (&scrap) != noErr)
2706 error ("cannot get current scrap");
2708 if (PutScrapFlavor (scrap, kScrapFlavorTypeText, kScrapFlavorMaskNone, len,
2709 buf) != noErr)
2710 error ("cannot put to scrap");
2712 #else /* not TARGET_API_MAC_CARBON */
2713 ZeroScrap ();
2714 PutScrap (len, 'TEXT', buf);
2715 #endif /* not TARGET_API_MAC_CARBON */
2717 return Qnil;
2721 DEFUN ("x-selection-exists-p", Fx_selection_exists_p, Sx_selection_exists_p,
2722 0, 1, 0,
2723 doc: /* Whether there is an owner for the given X Selection.
2724 The arg should be the name of the selection in question, typically one of
2725 the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.
2726 \(Those are literal upper-case symbol names, since that's what X expects.)
2727 For convenience, the symbol nil is the same as `PRIMARY',
2728 and t is the same as `SECONDARY'. */)
2729 (selection)
2730 Lisp_Object selection;
2732 CHECK_SYMBOL (selection);
2734 /* Return nil for PRIMARY and SECONDARY selections; for CLIPBOARD, check
2735 if the clipboard currently has valid text format contents. */
2737 if (EQ (selection, QCLIPBOARD))
2739 Lisp_Object val = Qnil;
2741 #if TARGET_API_MAC_CARBON
2742 ScrapRef scrap;
2743 ScrapFlavorFlags sff;
2745 if (GetCurrentScrap (&scrap) == noErr)
2746 if (GetScrapFlavorFlags (scrap, kScrapFlavorTypeText, &sff) == noErr)
2747 val = Qt;
2748 #else /* not TARGET_API_MAC_CARBON */
2749 Handle my_handle;
2750 long rc, scrap_offset;
2752 my_handle = NewHandle (0);
2754 rc = GetScrap (my_handle, 'TEXT', &scrap_offset);
2755 if (rc >= 0)
2756 val = Qt;
2758 DisposeHandle (my_handle);
2759 #endif /* not TARGET_API_MAC_CARBON */
2761 return val;
2763 return Qnil;
2766 #ifdef MAC_OSX
2767 #undef select
2769 extern int inhibit_window_system;
2770 extern int noninteractive;
2772 /* When Emacs is started from the Finder, SELECT always immediately
2773 returns as if input is present when file descriptor 0 is polled for
2774 input. Strangely, when Emacs is run as a GUI application from the
2775 command line, it blocks in the same situation. This `wrapper' of
2776 the system call SELECT corrects this discrepancy. */
2778 sys_select (n, rfds, wfds, efds, timeout)
2779 int n;
2780 SELECT_TYPE *rfds;
2781 SELECT_TYPE *wfds;
2782 SELECT_TYPE *efds;
2783 struct timeval *timeout;
2785 if (!inhibit_window_system && rfds && FD_ISSET (0, rfds))
2786 return 1;
2787 else if (inhibit_window_system || noninteractive ||
2788 (timeout && (EMACS_SECS(*timeout)==0) &&
2789 (EMACS_USECS(*timeout)==0)))
2790 return select(n, rfds, wfds, efds, timeout);
2791 else
2793 EMACS_TIME end_time, now;
2795 EMACS_GET_TIME (end_time);
2796 if (timeout)
2797 EMACS_ADD_TIME (end_time, end_time, *timeout);
2801 int r;
2802 EMACS_TIME one_second;
2803 SELECT_TYPE orfds;
2805 FD_ZERO (&orfds);
2806 if (rfds)
2808 orfds = *rfds;
2811 EMACS_SET_SECS (one_second, 1);
2812 EMACS_SET_USECS (one_second, 0);
2814 if (timeout && EMACS_TIME_LT(*timeout, one_second))
2815 one_second = *timeout;
2817 if ((r = select (n, &orfds, wfds, efds, &one_second)) > 0)
2819 *rfds = orfds;
2820 return r;
2823 mac_check_for_quit_char();
2825 EMACS_GET_TIME (now);
2826 EMACS_SUB_TIME (now, end_time, now);
2828 while (!timeout || !EMACS_TIME_NEG_P (now));
2830 return 0;
2834 #undef read
2835 int sys_read (fds, buf, nbyte)
2836 int fds;
2837 char *buf;
2838 unsigned int nbyte;
2840 SELECT_TYPE rfds;
2841 EMACS_TIME one_second;
2842 int r;
2844 /* Use select to block on IO while still checking for quit_char */
2845 if (!inhibit_window_system && !noninteractive &&
2846 ! (fcntl(fds, F_GETFL, 0) & O_NONBLOCK))
2848 FD_ZERO (&rfds);
2849 FD_SET (fds, &rfds);
2850 if (sys_select (fds+1, &rfds, 0, 0, NULL) < 0)
2851 return -1;
2854 return read (fds, buf, nbyte);
2858 /* Set up environment variables so that Emacs can correctly find its
2859 support files when packaged as an application bundle. Directories
2860 placed in /usr/local/share/emacs/<emacs-version>/, /usr/local/bin,
2861 and /usr/local/libexec/emacs/<emacs-version>/<system-configuration>
2862 by `make install' by default can instead be placed in
2863 .../Emacs.app/Contents/Resources/ and
2864 .../Emacs.app/Contents/MacOS/. Each of these environment variables
2865 is changed only if it is not already set. Presumably if the user
2866 sets an environment variable, he will want to use files in his path
2867 instead of ones in the application bundle. */
2868 void
2869 init_mac_osx_environment ()
2871 CFBundleRef bundle;
2872 CFURLRef bundleURL;
2873 CFStringRef cf_app_bundle_pathname;
2874 int app_bundle_pathname_len;
2875 char *app_bundle_pathname;
2876 char *p, *q;
2877 struct stat st;
2879 /* Fetch the pathname of the application bundle as a C string into
2880 app_bundle_pathname. */
2882 bundle = CFBundleGetMainBundle ();
2883 if (!bundle)
2884 return;
2886 bundleURL = CFBundleCopyBundleURL (bundle);
2887 if (!bundleURL)
2888 return;
2890 cf_app_bundle_pathname = CFURLCopyFileSystemPath (bundleURL,
2891 kCFURLPOSIXPathStyle);
2892 app_bundle_pathname_len = CFStringGetLength (cf_app_bundle_pathname);
2893 app_bundle_pathname = (char *) alloca (app_bundle_pathname_len + 1);
2895 if (!CFStringGetCString (cf_app_bundle_pathname,
2896 app_bundle_pathname,
2897 app_bundle_pathname_len + 1,
2898 kCFStringEncodingISOLatin1))
2900 CFRelease (cf_app_bundle_pathname);
2901 return;
2904 CFRelease (cf_app_bundle_pathname);
2906 /* P should have sufficient room for the pathname of the bundle plus
2907 the subpath in it leading to the respective directories. Q
2908 should have three times that much room because EMACSLOADPATH can
2909 have the value "<path to lisp dir>:<path to leim dir>:<path to
2910 site-lisp dir>". */
2911 p = (char *) alloca (app_bundle_pathname_len + 50);
2912 q = (char *) alloca (3 * app_bundle_pathname_len + 150);
2913 if (!getenv ("EMACSLOADPATH"))
2915 q[0] = '\0';
2917 strcpy (p, app_bundle_pathname);
2918 strcat (p, "/Contents/Resources/lisp");
2919 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
2920 strcat (q, p);
2922 strcpy (p, app_bundle_pathname);
2923 strcat (p, "/Contents/Resources/leim");
2924 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
2926 if (q[0] != '\0')
2927 strcat (q, ":");
2928 strcat (q, p);
2931 strcpy (p, app_bundle_pathname);
2932 strcat (p, "/Contents/Resources/site-lisp");
2933 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
2935 if (q[0] != '\0')
2936 strcat (q, ":");
2937 strcat (q, p);
2940 if (q[0] != '\0')
2941 setenv ("EMACSLOADPATH", q, 1);
2944 if (!getenv ("EMACSPATH"))
2946 q[0] = '\0';
2948 strcpy (p, app_bundle_pathname);
2949 strcat (p, "/Contents/MacOS/libexec");
2950 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
2951 strcat (q, p);
2953 strcpy (p, app_bundle_pathname);
2954 strcat (p, "/Contents/MacOS/bin");
2955 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
2957 if (q[0] != '\0')
2958 strcat (q, ":");
2959 strcat (q, p);
2962 if (q[0] != '\0')
2963 setenv ("EMACSPATH", q, 1);
2966 if (!getenv ("EMACSDATA"))
2968 strcpy (p, app_bundle_pathname);
2969 strcat (p, "/Contents/Resources/etc");
2970 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
2971 setenv ("EMACSDATA", p, 1);
2974 if (!getenv ("EMACSDOC"))
2976 strcpy (p, app_bundle_pathname);
2977 strcat (p, "/Contents/Resources/etc");
2978 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
2979 setenv ("EMACSDOC", p, 1);
2982 if (!getenv ("INFOPATH"))
2984 strcpy (p, app_bundle_pathname);
2985 strcat (p, "/Contents/Resources/info");
2986 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
2987 setenv ("INFOPATH", p, 1);
2990 #endif /* MAC_OSX */
2992 void
2993 syms_of_mac ()
2995 QCLIPBOARD = intern ("CLIPBOARD");
2996 staticpro (&QCLIPBOARD);
2998 defsubr (&Smac_paste_function);
2999 defsubr (&Smac_cut_function);
3000 defsubr (&Sx_selection_exists_p);
3002 defsubr (&Sdo_applescript);
3003 defsubr (&Smac_file_name_to_posix);
3004 defsubr (&Sposix_file_name_to_mac);
3007 /* arch-tag: 29d30c1f-0c6b-4f88-8a6d-0558d7f9dbff
3008 (do not change this comment) */