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)
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). */
29 #include <sys/types.h>
34 #include <sys/param.h>
48 #include <Carbon/Carbon.h>
50 #define free unexec_free
52 #define malloc unexec_malloc
54 #define realloc unexec_realloc
56 #define init_process emacs_init_process
57 #else /* not MAC_OSX */
60 #include <TextUtils.h>
62 #include <Resources.h>
67 #include <AppleScript.h>
69 #endif /* not MAC_OSX */
73 #include "sysselect.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. */
90 string_cat_and_replace (char *s1
, const char *s2
, int n
, char a
, char b
)
98 for (i
= 0; i
< l2
; i
++)
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
;
126 p
= strchr (mfn
, ':');
127 if (p
!= 0 && p
!= mfn
) /* full pathname */
134 pe
= mfn
+ strlen (mfn
);
141 { /* two consecutive ':' */
142 if (strlen (ufn
) + 3 >= ufnbuflen
)
148 if (strlen (ufn
) + (q
- p
) + 1 >= ufnbuflen
)
150 string_cat_and_replace (ufn
, p
, q
- p
, '/', ':');
157 if (strlen (ufn
) + (pe
- p
) >= ufnbuflen
)
159 string_cat_and_replace (ufn
, p
, pe
- p
, '/', ':');
160 /* no separator for last one */
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];
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
)
199 /* expand to emacs dir found by init_emacs_passwd_dir */
200 if (strncmp (p
, "~emacs/", 7) == 0)
202 struct passwd
*pw
= getpwnam ("emacs");
204 if (strlen (pw
->pw_dir
) + strlen (p
) > MAXPATHLEN
)
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 ();
215 if (strlen (t
) + strlen (p
) > MAXPATHLEN
)
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 */
234 if (q
- p
== 2 && *p
== '.' && *(p
+1) == '.')
236 if (strlen (mfn
) + 1 >= mfnbuflen
)
242 if (strlen (mfn
) + (q
- p
) + 1 >= mfnbuflen
)
244 string_cat_and_replace (mfn
, p
, q
- p
, ':', '/');
251 if (strlen (mfn
) + (pe
- p
) >= mfnbuflen
)
253 string_cat_and_replace (mfn
, p
, pe
- p
, ':', '/');
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)
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
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 */
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__ */
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];
307 if (posix_to_mac_pathname (path
, mac_pathname
, MAXPATHLEN
+1) == 0)
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 */
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 */
335 = cipb
.dirInfo
.ioDrMdDat
- MAC_UNIX_EPOCH_DIFF
;
336 buf
->st_ctime
= cipb
.dirInfo
.ioDrCrDat
- MAC_UNIX_EPOCH_DIFF
;
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
;
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
;
362 buf
->st_uid
= getuid ();
363 buf
->st_gid
= getgid ();
371 lstat (const char *path
, struct stat
*buf
)
374 char true_pathname
[MAXPATHLEN
+1];
376 /* Try looking for the file without resolving aliases first. */
377 if ((result
= stat_noalias (path
, buf
)) >= 0)
380 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
383 return stat_noalias (true_pathname
, buf
);
388 stat (const char *path
, struct stat
*sb
)
391 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
394 if ((result
= stat_noalias (path
, sb
)) >= 0 &&
395 ! (sb
->st_mode
& S_IFLNK
))
398 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
401 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
404 fully_resolved_name
[len
] = '\0';
405 /* in fact our readlink terminates strings */
406 return lstat (fully_resolved_name
, sb
);
409 return lstat (true_pathname
, sb
);
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
425 fstat (int fildes
, struct stat
*buf
)
428 buf
->st_ino
= fildes
;
429 buf
->st_mode
= S_IFREG
; /* added by T.I. for the copy-file */
430 return 0; /* success */
436 mkdir (const char *dirname
, int mode
)
441 char true_pathname
[MAXPATHLEN
+1], mac_pathname
[MAXPATHLEN
+1];
443 if (find_true_pathname (dirname
, true_pathname
, MAXPATHLEN
+1) == -1)
446 if (posix_to_mac_pathname (true_pathname
, mac_pathname
, MAXPATHLEN
+1) == 0)
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;
461 sys_rmdir (const char *dirname
)
464 char mac_pathname
[MAXPATHLEN
+1];
466 if (posix_to_mac_pathname (dirname
, mac_pathname
, MAXPATHLEN
+1) == 0)
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;
480 /* No implementation yet. */
482 execvp (const char *path
, ...)
490 utime (const char *path
, const struct utimbuf
*times
)
492 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
494 char mac_pathname
[MAXPATHLEN
+1];
497 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
500 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
502 fully_resolved_name
[len
] = '\0';
504 strcpy (fully_resolved_name
, true_pathname
);
506 if (!posix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+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);
520 if (cipb
.hFileInfo
.ioFlAttrib
& 0x10) /* bit 4 = 1 for directories */
523 cipb
.dirInfo
.ioDrMdDat
= times
->modtime
+ MAC_UNIX_EPOCH_DIFF
;
525 GetDateTime (&cipb
.dirInfo
.ioDrMdDat
);
530 cipb
.hFileInfo
.ioFlMdDat
= times
->modtime
+ MAC_UNIX_EPOCH_DIFF
;
532 GetDateTime (&cipb
.hFileInfo
.ioFlMdDat
);
535 errno
= PBSetCatInfo (&cipb
, false);
536 return errno
== noErr
? 0 : -1;
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];
556 char mac_pathname
[MAXPATHLEN
+1];
559 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
562 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
564 fully_resolved_name
[len
] = '\0';
566 strcpy (fully_resolved_name
, true_pathname
);
568 if (!posix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+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);
582 if (mode
== F_OK
) /* got this far, file exists */
586 if (cipb
.hFileInfo
.ioFlAttrib
& 0x10) /* path refers to a directory */
590 if (cipb
.hFileInfo
.ioFlFndrInfo
.fdType
== 'APPL')
597 return (cipb
.hFileInfo
.ioFlAttrib
& 0x1) ? -1 : 0;
598 /* don't allow if lock bit is on */
604 #define DEV_NULL_FD 0x10000
608 sys_open (const char *path
, int oflag
)
610 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
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)
620 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
622 fully_resolved_name
[len
] = '\0';
624 strcpy (fully_resolved_name
, true_pathname
);
626 if (!posix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
631 int res
= open (mac_pathname
, oflag
);
632 /* if (oflag == O_WRONLY || oflag == O_RDWR) */
634 fsetfileinfo (mac_pathname
, 'EMAx', 'TEXT');
636 #else /* not __MRC__ */
637 return open (mac_pathname
, oflag
);
638 #endif /* not __MRC__ */
645 sys_creat (const char *path
, mode_t mode
)
647 char true_pathname
[MAXPATHLEN
+1];
649 char mac_pathname
[MAXPATHLEN
+1];
651 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
654 if (!posix_to_mac_pathname (true_pathname
, mac_pathname
, MAXPATHLEN
+1))
659 int result
= creat (mac_pathname
);
660 fsetfileinfo (mac_pathname
, 'EMAx', 'TEXT');
662 #else /* not __MRC__ */
663 return creat (mac_pathname
, mode
);
664 #endif /* not __MRC__ */
671 sys_unlink (const char *path
)
673 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
675 char mac_pathname
[MAXPATHLEN
+1];
677 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
680 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
682 fully_resolved_name
[len
] = '\0';
684 strcpy (fully_resolved_name
, true_pathname
);
686 if (!posix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
689 return unlink (mac_pathname
);
695 sys_read (int fildes
, char *buf
, int count
)
697 if (fildes
== 0) /* this should not be used for console input */
700 #if __MSL__ >= 0x6000
701 return _read (fildes
, buf
, count
);
703 return read (fildes
, buf
, count
);
710 sys_write (int fildes
, const char *buf
, int count
)
712 if (fildes
== DEV_NULL_FD
)
715 #if __MSL__ >= 0x6000
716 return _write (fildes
, buf
, count
);
718 return write (fildes
, buf
, count
);
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];
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)
735 len
= readlink (true_old_pathname
, fully_resolved_old_name
, MAXPATHLEN
);
737 fully_resolved_old_name
[len
] = '\0';
739 strcpy (fully_resolved_old_name
, true_old_pathname
);
741 if (find_true_pathname (new_name
, true_new_pathname
, MAXPATHLEN
+1) == -1)
744 if (strcmp (fully_resolved_old_name
, true_new_pathname
) == 0)
747 if (!posix_to_mac_pathname (fully_resolved_old_name
,
752 if (!posix_to_mac_pathname(true_new_pathname
, mac_new_name
, MAXPATHLEN
+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
);
765 extern FILE *fopen (const char *name
, const char *mode
);
767 sys_fopen (const char *name
, const char *mode
)
769 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
771 char mac_pathname
[MAXPATHLEN
+1];
773 if (find_true_pathname (name
, true_pathname
, MAXPATHLEN
+1) == -1)
776 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
778 fully_resolved_name
[len
] = '\0';
780 strcpy (fully_resolved_name
, true_pathname
);
782 if (!posix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
787 if (mode
[0] == 'w' || mode
[0] == 'a')
788 fsetfileinfo (mac_pathname
, 'EMAx', 'TEXT');
789 #endif /* not __MRC__ */
790 return fopen (mac_pathname
, mode
);
797 long target_ticks
= 0;
800 __sigfun alarm_signal_func
= (__sigfun
) 0;
802 __signal_func_ptr alarm_signal_func
= (__signal_func_ptr
) 0;
803 #else /* not __MRC__ and not __MWERKS__ */
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. */
819 if (target_ticks
&& TickCount () > target_ticks
)
822 if (alarm_signal_func
)
823 (*alarm_signal_func
)(SIGALRM
);
829 select (n
, rfds
, wfds
, efds
, timeout
)
834 struct timeval
*timeout
;
836 #ifdef TARGET_API_MAC_CARBON
838 #else /* not TARGET_API_MAC_CARBON */
839 EMACS_TIME end_time
, now
;
842 /* Can only handle wait for keyboard input. */
843 if (n
> 1 || wfds
|| efds
)
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
))
859 /* Also check movement of the mouse. */
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
;
872 WaitNextEvent (0, &e
, 1UL, NULL
); /* Accept no event; wait 1
875 EMACS_GET_TIME (now
);
876 EMACS_SUB_TIME (now
, end_time
, now
);
878 while (!EMACS_TIME_NEG_P (now
));
881 #endif /* not TARGET_API_MAC_CARBON */
885 /* Called in sys_select to wait for an alarm signal to arrive. */
893 if (!target_ticks
) /* no alarm pending */
896 if ((tick
= TickCount ()) < target_ticks
)
897 WaitNextEvent (0, &e
, target_ticks
- tick
, NULL
); /* Accept no event;
898 just wait. by T.I. */
901 if (alarm_signal_func
)
902 (*alarm_signal_func
)(SIGALRM
);
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
;
921 extern __sigfun
signal (int signal
, __sigfun signal_func
);
923 sys_signal (int signal_num
, __sigfun signal_func
)
925 extern __signal_func_ptr
signal (int signal
, __signal_func_ptr signal_func
);
927 sys_signal (int signal_num
, __signal_func_ptr signal_func
)
928 #else /* not __MRC__ and not __MWERKS__ */
930 #endif /* not __MRC__ and not __MWERKS__ */
932 if (signal_num
!= SIGALRM
)
933 return signal (signal_num
, signal_func
);
937 __sigfun old_signal_func
;
939 __signal_func_ptr old_signal_func
;
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. */
961 static wide wall_clock_at_epoch
, clicks_at_epoch
;
962 UnsignedWide uw_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. */
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
);
998 sleep (unsigned int seconds
)
1000 unsigned long time_up
;
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
);
1012 #endif /* __MRC__ */
1015 /* The time functions adjust time values according to the difference
1016 between the Unix and CW epoches. */
1019 extern struct tm
*gmtime (const time_t *);
1021 sys_gmtime (const time_t *timer
)
1023 time_t unix_time
= *timer
+ CW_OR_MPW_UNIX_EPOCH_DIFF
;
1025 return gmtime (&unix_time
);
1030 extern struct tm
*localtime (const time_t *);
1032 sys_localtime (const time_t *timer
)
1034 #if __MSL__ >= 0x6000
1035 time_t unix_time
= *timer
;
1037 time_t unix_time
= *timer
+ CW_OR_MPW_UNIX_EPOCH_DIFF
;
1040 return localtime (&unix_time
);
1045 extern char *ctime (const time_t *);
1047 sys_ctime (const time_t *timer
)
1049 #if __MSL__ >= 0x6000
1050 time_t unix_time
= *timer
;
1052 time_t unix_time
= *timer
+ CW_OR_MPW_UNIX_EPOCH_DIFF
;
1055 return ctime (&unix_time
);
1060 extern time_t time (time_t *);
1062 sys_time (time_t *timer
)
1064 #if __MSL__ >= 0x6000
1065 time_t mac_time
= time (NULL
);
1067 time_t mac_time
= time (NULL
) - CW_OR_MPW_UNIX_EPOCH_DIFF
;
1077 /* MPW strftime broken for "%p" format */
1082 sys_strftime (char * s
, size_t maxsize
, const char * format
,
1083 const struct tm
* timeptr
)
1085 if (strcmp (format
, "%p") == 0)
1089 if (timeptr
->tm_hour
< 12)
1101 return strftime (s
, maxsize
, format
, timeptr
);
1103 #endif /* __MRC__ */
1106 /* no subprocesses, empty wait */
1116 croak (char *badfunc
)
1118 printf ("%s not yet implemented\r\n", badfunc
);
1124 index (const char * str
, int chr
)
1126 return strchr (str
, chr
);
1131 mktemp (char *template)
1136 len
= strlen (template);
1138 while (k
>= 0 && template[k
] == 'X')
1141 k
++; /* make k index of first 'X' */
1145 /* Zero filled, number of digits equal to the number of X's. */
1146 sprintf (&template[k
], "%0*d", len
-k
, seqnum
++);
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
=
1168 static struct group my_group
=
1170 /* There are no groups on the mac, so we just return "root" as the
1176 /* Initialized by main () in macterm.c to pathname of emacs directory. */
1178 char emacs_passwd_dir
[MAXPATHLEN
+1];
1184 init_emacs_passwd_dir ()
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-*
1193 int len
= strlen (emacs_passwd_dir
);
1195 /* j points to the "/" following the directory name being
1198 while (i
>= 0 && !found
)
1200 while (i
>= 0 && emacs_passwd_dir
[i
] != '/')
1202 if (emacs_passwd_dir
[i
] == '/' && i
+5 < len
)
1203 found
= (strncmp (&(emacs_passwd_dir
[i
+1]), "emacs", 5) == 0);
1205 emacs_passwd_dir
[j
+1] = '\0';
1216 /* Setting to "/" probably won't work but set it to something
1218 strcpy (emacs_passwd_dir
, "/");
1219 strcpy (my_passwd_dir
, "/");
1224 static struct passwd emacs_passwd
=
1230 static int my_passwd_inited
= 0;
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);
1245 BlockMove ((unsigned char *) *owner_name
,
1246 (unsigned char *) my_passwd_name
,
1248 HUnlock (owner_name
);
1249 p2cstr ((unsigned char *) my_passwd_name
);
1252 my_passwd_name
[0] = 0;
1257 getpwuid (uid_t uid
)
1259 if (!my_passwd_inited
)
1262 my_passwd_inited
= 1;
1270 getgrgid (gid_t gid
)
1277 getpwnam (const char *name
)
1279 if (strcmp (name
, "emacs") == 0)
1280 return &emacs_passwd
;
1282 if (!my_passwd_inited
)
1285 my_passwd_inited
= 1;
1292 /* The functions fork, kill, sigsetmask, sigblock, request_sigio,
1293 setpgrp, setpriority, and unrequest_sigio are defined to be empty
1314 error ("Can't spawn subshell");
1333 request_sigio (void)
1339 unrequest_sigio (void)
1354 pipe (int _fildes
[2])
1361 /* Hard and symbolic links. */
1364 symlink (const char *name1
, const char *name2
)
1372 link (const char *name1
, const char *name2
)
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
1384 path_from_vol_dir_name (char *path
, int man_path_len
, short vol_ref_num
,
1385 long dir_id
, ConstStr255Param name
)
1391 if (strlen (name
) > man_path_len
)
1394 memcpy (dir_name
, name
, name
[0]+1);
1395 memcpy (path
, name
, name
[0]+1);
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);
1413 if (strlen (dir_name
) + strlen (path
) + 1 >= man_path_len
)
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 */
1430 readlink (const char *path
, char *buf
, int bufsiz
)
1432 char mac_sym_link_name
[MAXPATHLEN
+1];
1435 Boolean target_is_folder
, was_aliased
;
1436 Str255 directory_name
, mac_pathname
;
1439 if (posix_to_mac_pathname (path
, mac_sym_link_name
, MAXPATHLEN
+1) == 0)
1442 c2pstr (mac_sym_link_name
);
1443 err
= FSMakeFSSpec (0, 0, mac_sym_link_name
, &fsspec
);
1450 err
= ResolveAliasFile (&fsspec
, true, &target_is_folder
, &was_aliased
);
1451 if (err
!= noErr
|| !was_aliased
)
1457 if (path_from_vol_dir_name (mac_pathname
, 255, fsspec
.vRefNum
, fsspec
.parID
,
1464 if (mac_to_posix_pathname (mac_pathname
, buf
, bufsiz
) == 0)
1470 return strlen (buf
);
1474 /* Convert a path to one with aliases fully expanded. */
1477 find_true_pathname (const char *path
, char *buf
, int bufsiz
)
1479 char *q
, temp
[MAXPATHLEN
+1];
1483 if (bufsiz
<= 0 || path
== 0 || path
[0] == '\0')
1490 q
= strchr (p
+ 1, '/');
1492 q
= strchr (p
, '/');
1493 len
= 0; /* loop may not be entered, e.g., for "/" */
1498 strncat (temp
, p
, q
- p
);
1499 len
= readlink (temp
, buf
, bufsiz
);
1502 if (strlen (temp
) + 1 > bufsiz
)
1512 if (len
+ strlen (p
) + 1 >= bufsiz
)
1516 return len
+ strlen (p
);
1521 umask (mode_t numask
)
1523 static mode_t mask
= 022;
1524 mode_t oldmask
= mask
;
1531 chmod (const char *path
, mode_t mode
)
1533 /* say it always succeed for now */
1542 return fcntl (oldd
, F_DUPFD
, 0);
1544 /* current implementation of fcntl in fcntl.mac.c simply returns old
1546 return fcntl (oldd
, F_DUPFD
);
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
)
1570 ret
= dup2 (oldd
, newd
);
1576 /* let it fail for now */
1593 ioctl (int d
, int request
, void *argp
)
1603 if (fildes
>=0 && fildes
<= 2)
1636 #endif /* __MRC__ */
1640 #if __MSL__ < 0x6000
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. */
1663 get_temp_dir_name ()
1665 static char *temp_dir_name
= NULL
;
1669 Str255 dir_name
, full_path
;
1671 char unix_dir_name
[MAXPATHLEN
+1];
1674 /* Cache directory name with pointer temp_dir_name.
1675 Look for it only the first time. */
1678 err
= FindFolder (kOnSystemDisk
, kPreferencesFolderType
, kCreateFolder
,
1679 &vol_ref_num
, &dir_id
);
1683 if (!path_from_vol_dir_name (full_path
, 255, vol_ref_num
, dir_id
, "\p"))
1686 if (strlen (full_path
) + 6 <= MAXPATHLEN
)
1687 strcat (full_path
, "Emacs:");
1691 if (!mac_to_posix_pathname (full_path
, unix_dir_name
, MAXPATHLEN
+1))
1694 dir
= opendir (unix_dir_name
); /* check whether temp directory exists */
1697 else if (mkdir (unix_dir_name
, 0700) != 0) /* create it if not */
1700 temp_dir_name
= (char *) malloc (strlen (unix_dir_name
) + 1);
1701 strcpy (temp_dir_name
, unix_dir_name
);
1704 return temp_dir_name
;
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.
1720 get_string_list (char ***t
, short string_list_id
)
1726 h
= GetResource ('STR#', string_list_id
);
1731 num_strings
= * (short *) p
;
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';
1742 (*t
)[num_strings
] = 0;
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 *));
1757 get_path_to_system_folder ()
1762 Str255 dir_name
, full_path
;
1764 static char system_folder_unix_name
[MAXPATHLEN
+1];
1767 err
= FindFolder (kOnSystemDisk
, kSystemFolderType
, kDontCreateFolder
,
1768 &vol_ref_num
, &dir_id
);
1772 if (!path_from_vol_dir_name (full_path
, 255, vol_ref_num
, dir_id
, "\p"))
1775 if (!mac_to_posix_pathname (full_path
, system_folder_unix_name
,
1779 return system_folder_unix_name
;
1785 #define ENVIRON_STRING_LIST_ID 128
1787 /* Get environment variable definitions from STR# resource. */
1794 get_string_list (&environ
, ENVIRON_STRING_LIST_ID
);
1800 /* Make HOME directory the one Emacs starts up in if not specified
1802 if (getenv ("HOME") == NULL
)
1804 environ
= (char **) realloc (environ
, sizeof (char *) * (i
+ 2));
1807 environ
[i
] = (char *) malloc (strlen (my_passwd_dir
) + 6);
1810 strcpy (environ
[i
], "HOME=");
1811 strcat (environ
[i
], my_passwd_dir
);
1818 /* Make HOME directory the one Emacs starts up in if not specified
1820 if (getenv ("MAIL") == NULL
)
1822 environ
= (char **) realloc (environ
, sizeof (char *) * (i
+ 2));
1825 char * path_to_system_folder
= get_path_to_system_folder ();
1826 environ
[i
] = (char *) malloc (strlen (path_to_system_folder
) + 22);
1829 strcpy (environ
[i
], "MAIL=");
1830 strcat (environ
[i
], path_to_system_folder
);
1831 strcat (environ
[i
], "Eudora Folder/In");
1839 /* Return the value of the environment variable NAME. */
1842 getenv (const char *name
)
1844 int length
= strlen(name
);
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 ();
1859 /* see Interfaces&Libraries:Interfaces:CIncludes:signal.h */
1860 char *sys_siglist
[] =
1862 "Zero is not a signal!!!",
1864 "Interactive user interrupt", /* 2 */ "?",
1865 "Floating point exception", /* 4 */ "?", "?", "?",
1866 "Illegal instruction", /* 8 */ "?", "?", "?", "?", "?", "?", "?",
1867 "Segment violation", /* 16 */ "?", "?", "?", "?", "?", "?", "?",
1868 "?", "?", "?", "?", "?", "?", "?", "?",
1872 char *sys_siglist
[] =
1874 "Zero is not a signal!!!",
1876 "Floating point exception",
1877 "Illegal instruction",
1878 "Interactive user interrupt",
1879 "Segment violation",
1882 #else /* not __MRC__ and not __MWERKS__ */
1884 #endif /* not __MRC__ and not __MWERKS__ */
1887 #include <utsname.h>
1890 uname (struct utsname
*name
)
1893 system_name
= GetString (-16413); /* IM - Resource Manager Reference */
1896 BlockMove (*system_name
, name
->nodename
, (*system_name
)[0]+1);
1897 p2cstr (name
->nodename
);
1905 #include <Processes.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';
1916 mystrchr (char *s
, char c
)
1918 while (*s
&& *s
!= c
)
1946 mystrcpy (char *to
, char *from
)
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
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
;
1988 LaunchParamBlockRec lpbr
;
1989 EventRecord send_event
, reply_event
;
1990 RgnHandle cursor_region_handle
;
1992 unsigned long ref_con
, len
;
1994 if (posix_to_mac_pathname (workdir
, macworkdir
, MAXPATHLEN
+1) == 0)
1996 if (posix_to_mac_pathname (infn
, macinfn
, MAXPATHLEN
+1) == 0)
1998 if (posix_to_mac_pathname (outfn
, macoutfn
, MAXPATHLEN
+1) == 0)
2000 if (posix_to_mac_pathname (errfn
, macerrfn
, MAXPATHLEN
+1) == 0)
2003 paramlen
= strlen (macworkdir
) + strlen (macinfn
) + strlen (macoutfn
)
2004 + strlen (macerrfn
) + 4; /* count nulls at end of strings */
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
, " ");
2031 t
= mystrchr (t
, ' ');
2035 t
= mystrchr (t
+1, ' ');
2038 newargv
= (char **) alloca (sizeof (char *) * newargc
);
2041 for (j
= 0; j
< newargc
; j
++)
2043 newargv
[j
] = (char *) alloca (strlen (t
) + 1);
2044 mystrcpy (newargv
[j
], 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)
2057 { /* sometimes Emacs call "sh" without a path for the command */
2059 char *t
= (char *) alloca (strlen (newargv
[0]) + 7 + 1);
2060 strcpy (t
, "~emacs/");
2061 strcat (t
, newargv
[0]);
2064 openp (Vexec_path
, build_string (newargv
[0]), EXEC_SUFFIXES
, &path
,
2065 make_number (X_OK
));
2069 if (posix_to_mac_pathname (SDATA (path
), tempmacpathname
,
2073 strcpy (macappname
, tempmacpathname
);
2077 if (posix_to_mac_pathname (argv
[0], macappname
, MAXPATHLEN
+1) == 0)
2080 newargv
= (char **) alloca (sizeof (char *) * argc
);
2082 for (j
= 1; j
< argc
; j
++)
2084 if (strncmp (argv
[j
], "~emacs/", 7) == 0)
2086 char *t
= strchr (argv
[j
], ' ');
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
,
2095 newargv
[j
] = (char *) alloca (strlen (tempmaccmdname
)
2097 strcpy (newargv
[j
], tempmaccmdname
);
2098 strcat (newargv
[j
], t
);
2102 char tempmaccmdname
[MAXPATHLEN
+1];
2103 if (posix_to_mac_pathname (argv
[j
], tempmaccmdname
,
2106 newargv
[j
] = (char *) alloca (strlen (tempmaccmdname
)+1);
2107 strcpy (newargv
[j
], tempmaccmdname
);
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);
2125 /* first byte of message contains number of arguments for command */
2126 strcpy (p
, macworkdir
);
2127 p
+= strlen (macworkdir
);
2129 /* null terminate strings sent so it's possible to use strcpy over there */
2130 strcpy (p
, macinfn
);
2131 p
+= strlen (macinfn
);
2133 strcpy (p
, macoutfn
);
2134 p
+= strlen (macoutfn
);
2136 strcpy (p
, macerrfn
);
2137 p
+= strlen (macerrfn
);
2139 for (j
= 1; j
< newargc
; j
++)
2141 strcpy (p
, newargv
[j
]);
2142 p
+= strlen (newargv
[j
]);
2146 c2pstr (macappname
);
2148 iErr
= FSMakeFSSpec (0, 0, macappname
, &spec
);
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 */
2169 send_event
.what
= kHighLevelEvent
;
2170 send_event
.message
= kEmacsSubprocessSend
;
2171 /* Event ID stored in "where" unused */
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);
2189 cursor_region_handle
= NewRgn ();
2191 /* Wait for the subprocess to finish, when it will send us a ERPY
2192 high level event. */
2194 if (WaitNextEvent (highLevelEventMask
, &reply_event
, 180,
2195 cursor_region_handle
)
2196 && reply_event
.message
== kEmacsSubprocessReply
)
2199 /* The return code is sent through the refCon */
2200 iErr
= AcceptHighLevelEvent (&targ
, &ref_con
, NULL
, &len
);
2203 DisposeHandle ((Handle
) cursor_region_handle
);
2208 DisposeHandle ((Handle
) cursor_region_handle
);
2212 #endif /* not TARGET_API_MAC_CARBON */
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];
2224 int len
, vol_name_len
;
2226 if (find_true_pathname (dirname
, true_pathname
, MAXPATHLEN
+1) == -1)
2229 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
2231 fully_resolved_name
[len
] = '\0';
2233 strcpy (fully_resolved_name
, true_pathname
);
2235 dirp
= (DIR *) malloc (sizeof(DIR));
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 */
2248 /* Handle typical cases: not accessing all mounted volumes. */
2249 if (!posix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
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);
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 */
2286 vpb
.ioNamePtr
= vol_name
;
2287 /* using full pathname so vRefNum and DirID ignored */
2289 vpb
.ioVolIndex
= -1;
2290 errno
= PBHGetVInfo ((union HParamBlockRec
*) &vpb
, false);
2297 dirp
->vol_ref_num
= vpb
.ioVRefNum
;
2314 HParamBlockRec hpblock
;
2316 static struct dirent s_dirent
;
2317 static Str255 s_name
;
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
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);
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
;
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 */
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);
2370 /* insist on a visible entry */
2371 if (cipb
.hFileInfo
.ioFlAttrib
& 0x10) /* directory? */
2372 done
= !(cipb
.dirInfo
.ioDrUsrWds
.frFlags
& fInvisible
);
2374 done
= !(cipb
.hFileInfo
.ioFlFndrInfo
.fdFlags
& fInvisible
);
2376 dp
->current_index
++;
2389 s_dirent
.d_ino
= cipb
.dirInfo
.ioDrDirID
;
2390 /* value unimportant: non-zero for valid file */
2391 s_dirent
.d_name
= s_name
;
2401 char mac_pathname
[MAXPATHLEN
+1];
2402 Str255 directory_name
;
2406 if (path_from_vol_dir_name (mac_pathname
, 255, 0, 0, "\p") == 0)
2409 if (mac_to_posix_pathname (mac_pathname
, path
, MAXPATHLEN
+1) == 0)
2415 #endif /* ! MAC_OSX */
2419 initialize_applescript ()
2424 /* if open fails, as_scripting_component is set to NULL. Its
2425 subsequent use in OSA calls will fail with badComponentInstance
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
);
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. */
2458 do_applescript (char *script
, char **result
)
2460 AEDesc script_desc
, result_desc
, error_desc
;
2467 if (!as_scripting_component
)
2468 initialize_applescript();
2470 error
= AECreateDesc (typeChar
, script
, strlen(script
), &script_desc
);
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
,
2483 #if TARGET_API_MAC_CARBON
2484 length
= AEGetDescDataSize (&error_desc
);
2485 *result
= (char *) xmalloc (length
+ 1);
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);
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);
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);
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
);
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
2544 char *result
, *temp
;
2545 Lisp_Object lisp_result
;
2548 CHECK_STRING (script
);
2550 status
= do_applescript (SDATA (script
), &result
);
2554 error ("AppleScript error %d", status
);
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
);
2570 lisp_result
= build_string (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. */)
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
,
2589 return build_string (posix_filename
);
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. */)
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
,
2607 return build_string (mac_filename
);
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
2621 ScrapFlavorFlags sff
;
2626 if (GetCurrentScrap (&scrap
) != noErr
)
2629 if (GetScrapFlavorFlags (scrap
, kScrapFlavorTypeText
, &sff
) != noErr
)
2632 if (GetScrapFlavorSize (scrap
, kScrapFlavorTypeText
, &s
) != noErr
)
2635 if ((data
= (char*) alloca (s
)) == NULL
)
2638 if (GetScrapFlavorData (scrap
, kScrapFlavorTypeText
, &s
, data
) != noErr
2642 /* Emacs expects clipboard contents have Unix-style eol's */
2643 for (i
= 0; i
< s
; i
++)
2644 if (data
[i
] == '\r')
2647 return make_string (data
, s
);
2648 #else /* not TARGET_API_MAC_CARBON */
2651 long scrap_offset
, rc
, i
;
2653 my_handle
= NewHandle (0); /* allocate 0-length data area */
2655 rc
= GetScrap (my_handle
, 'TEXT', &scrap_offset
);
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
);
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. */)
2682 Lisp_Object value
, push
;
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
);
2696 /* convert to Mac-style eol's before sending to clipboard */
2697 for (i
= 0; i
< len
; i
++)
2701 #if TARGET_API_MAC_CARBON
2704 ClearCurrentScrap ();
2705 if (GetCurrentScrap (&scrap
) != noErr
)
2706 error ("cannot get current scrap");
2708 if (PutScrapFlavor (scrap
, kScrapFlavorTypeText
, kScrapFlavorMaskNone
, len
,
2710 error ("cannot put to scrap");
2712 #else /* not TARGET_API_MAC_CARBON */
2714 PutScrap (len
, 'TEXT', buf
);
2715 #endif /* not TARGET_API_MAC_CARBON */
2721 DEFUN ("x-selection-exists-p", Fx_selection_exists_p
, Sx_selection_exists_p
,
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'. */)
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
2743 ScrapFlavorFlags sff
;
2745 if (GetCurrentScrap (&scrap
) == noErr
)
2746 if (GetScrapFlavorFlags (scrap
, kScrapFlavorTypeText
, &sff
) == noErr
)
2748 #else /* not TARGET_API_MAC_CARBON */
2750 long rc
, scrap_offset
;
2752 my_handle
= NewHandle (0);
2754 rc
= GetScrap (my_handle
, 'TEXT', &scrap_offset
);
2758 DisposeHandle (my_handle
);
2759 #endif /* not TARGET_API_MAC_CARBON */
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
)
2783 struct timeval
*timeout
;
2785 if (!inhibit_window_system
&& rfds
&& FD_ISSET (0, rfds
))
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
);
2793 EMACS_TIME end_time
, now
;
2795 EMACS_GET_TIME (end_time
);
2797 EMACS_ADD_TIME (end_time
, end_time
, *timeout
);
2802 EMACS_TIME one_second
;
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)
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
));
2835 int sys_read (fds
, buf
, nbyte
)
2841 EMACS_TIME one_second
;
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
))
2849 FD_SET (fds
, &rfds
);
2850 if (sys_select (fds
+1, &rfds
, 0, 0, NULL
) < 0)
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. */
2869 init_mac_osx_environment ()
2873 CFStringRef cf_app_bundle_pathname
;
2874 int app_bundle_pathname_len
;
2875 char *app_bundle_pathname
;
2879 /* Fetch the pathname of the application bundle as a C string into
2880 app_bundle_pathname. */
2882 bundle
= CFBundleGetMainBundle ();
2886 bundleURL
= CFBundleCopyBundleURL (bundle
);
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
);
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
2911 p
= (char *) alloca (app_bundle_pathname_len
+ 50);
2912 q
= (char *) alloca (3 * app_bundle_pathname_len
+ 150);
2913 if (!getenv ("EMACSLOADPATH"))
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
)
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
)
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
)
2941 setenv ("EMACSLOADPATH", q
, 1);
2944 if (!getenv ("EMACSPATH"))
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
)
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
)
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 */
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) */