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>
33 #include <sys/param.h>
47 #include <Carbon/Carbon.h>
49 #define free unexec_free
51 #define malloc unexec_malloc
53 #define realloc unexec_realloc
55 #define init_process emacs_init_process
56 #else /* not MAC_OSX */
59 #include <TextUtils.h>
61 #include <Resources.h>
66 #include <AppleScript.h>
68 #endif /* not MAC_OSX */
72 #include "sysselect.h"
75 Lisp_Object QCLIPBOARD
;
77 /* An instance of the AppleScript component. */
78 static ComponentInstance as_scripting_component
;
79 /* The single script context used for all script executions. */
80 static OSAID as_script_context
;
83 /* When converting from Mac to Unix pathnames, /'s in folder names are
84 converted to :'s. This function, used in copying folder names,
85 performs a strncat and converts all character a to b in the copy of
86 the string s2 appended to the end of s1. */
89 string_cat_and_replace (char *s1
, const char *s2
, int n
, char a
, char b
)
97 for (i
= 0; i
< l2
; i
++)
106 /* Convert a Mac pathname to Posix form. A Mac full pathname is one
107 that does not begin with a ':' and contains at least one ':'. A Mac
108 full pathname causes a '/' to be prepended to the Posix pathname.
109 The algorithm for the rest of the pathname is as follows:
110 For each segment between two ':',
111 if it is non-null, copy as is and then add a '/' at the end,
112 otherwise, insert a "../" into the Posix pathname.
113 Returns 1 if successful; 0 if fails. */
116 mac_to_posix_pathname (const char *mfn
, char *ufn
, int ufnbuflen
)
118 const char *p
, *q
, *pe
;
125 p
= strchr (mfn
, ':');
126 if (p
!= 0 && p
!= mfn
) /* full pathname */
133 pe
= mfn
+ strlen (mfn
);
140 { /* two consecutive ':' */
141 if (strlen (ufn
) + 3 >= ufnbuflen
)
147 if (strlen (ufn
) + (q
- p
) + 1 >= ufnbuflen
)
149 string_cat_and_replace (ufn
, p
, q
- p
, '/', ':');
156 if (strlen (ufn
) + (pe
- p
) >= ufnbuflen
)
158 string_cat_and_replace (ufn
, p
, pe
- p
, '/', ':');
159 /* no separator for last one */
168 extern char *get_temp_dir_name ();
171 /* Convert a Posix pathname to Mac form. Approximately reverse of the
172 above in algorithm. */
175 posix_to_mac_pathname (const char *ufn
, char *mfn
, int mfnbuflen
)
177 const char *p
, *q
, *pe
;
178 char expanded_pathname
[MAXPATHLEN
+1];
187 /* Check for and handle volume names. Last comparison: strangely
188 somewhere "/.emacs" is passed. A temporary fix for now. */
189 if (*p
== '/' && strchr (p
+1, '/') == NULL
&& strcmp (p
, "/.emacs") != 0)
191 if (strlen (p
) + 1 > mfnbuflen
)
198 /* expand to emacs dir found by init_emacs_passwd_dir */
199 if (strncmp (p
, "~emacs/", 7) == 0)
201 struct passwd
*pw
= getpwnam ("emacs");
203 if (strlen (pw
->pw_dir
) + strlen (p
) > MAXPATHLEN
)
205 strcpy (expanded_pathname
, pw
->pw_dir
);
206 strcat (expanded_pathname
, p
);
207 p
= expanded_pathname
;
208 /* now p points to the pathname with emacs dir prefix */
210 else if (strncmp (p
, "/tmp/", 5) == 0)
212 char *t
= get_temp_dir_name ();
214 if (strlen (t
) + strlen (p
) > MAXPATHLEN
)
216 strcpy (expanded_pathname
, t
);
217 strcat (expanded_pathname
, p
);
218 p
= expanded_pathname
;
219 /* now p points to the pathname with emacs dir prefix */
221 else if (*p
!= '/') /* relative pathname */
233 if (q
- p
== 2 && *p
== '.' && *(p
+1) == '.')
235 if (strlen (mfn
) + 1 >= mfnbuflen
)
241 if (strlen (mfn
) + (q
- p
) + 1 >= mfnbuflen
)
243 string_cat_and_replace (mfn
, p
, q
- p
, ':', '/');
250 if (strlen (mfn
) + (pe
- p
) >= mfnbuflen
)
252 string_cat_and_replace (mfn
, p
, pe
- p
, ':', '/');
262 /* The following functions with "sys_" prefix are stubs to Unix
263 functions that have already been implemented by CW or MPW. The
264 calls to them in Emacs source course are #define'd to call the sys_
265 versions by the header files s-mac.h. In these stubs pathnames are
266 converted between their Unix and Mac forms. */
269 /* Unix epoch is Jan 1, 1970 while Mac epoch is Jan 1, 1904: 66 years
270 + 17 leap days. These are for adjusting time values returned by
271 MacOS Toolbox functions. */
273 #define MAC_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
277 /* CW Pro 5 epoch is Jan 1, 1900 (aaarghhhhh!); remember, 1900 is not
278 a leap year! This is for adjusting time_t values returned by MSL
280 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 70 + 17) * 24 * 60 * 60)
281 #else /* __MSL__ >= 0x6000 */
282 /* CW changes Pro 6 to follow Unix! */
283 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
284 #endif /* __MSL__ >= 0x6000 */
286 /* MPW library functions follow Unix (confused?). */
287 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
288 #else /* not __MRC__ */
290 #endif /* not __MRC__ */
293 /* Define our own stat function for both MrC and CW. The reason for
294 doing this: "stat" is both the name of a struct and function name:
295 can't use the same trick like that for sys_open, sys_close, etc. to
296 redirect Emacs's calls to our own version that converts Unix style
297 filenames to Mac style filename because all sorts of compilation
298 errors will be generated if stat is #define'd to be sys_stat. */
301 stat_noalias (const char *path
, struct stat
*buf
)
303 char mac_pathname
[MAXPATHLEN
+1];
306 if (posix_to_mac_pathname (path
, mac_pathname
, MAXPATHLEN
+1) == 0)
309 c2pstr (mac_pathname
);
310 cipb
.hFileInfo
.ioNamePtr
= mac_pathname
;
311 cipb
.hFileInfo
.ioVRefNum
= 0;
312 cipb
.hFileInfo
.ioDirID
= 0;
313 cipb
.hFileInfo
.ioFDirIndex
= 0;
314 /* set to 0 to get information about specific dir or file */
316 errno
= PBGetCatInfo (&cipb
, false);
317 if (errno
== -43) /* -43: fnfErr defined in Errors.h */
322 if (cipb
.hFileInfo
.ioFlAttrib
& 0x10) /* bit 4 = 1 for directories */
324 buf
->st_mode
= S_IFDIR
| S_IREAD
| S_IEXEC
;
326 if (!(cipb
.hFileInfo
.ioFlAttrib
& 0x1))
327 buf
->st_mode
|= S_IWRITE
; /* bit 1 = 1 for locked files/directories */
328 buf
->st_ino
= cipb
.dirInfo
.ioDrDirID
;
329 buf
->st_dev
= cipb
.dirInfo
.ioVRefNum
;
330 buf
->st_size
= cipb
.dirInfo
.ioDrNmFls
;
331 /* size of dir = number of files and dirs */
334 = cipb
.dirInfo
.ioDrMdDat
- MAC_UNIX_EPOCH_DIFF
;
335 buf
->st_ctime
= cipb
.dirInfo
.ioDrCrDat
- MAC_UNIX_EPOCH_DIFF
;
339 buf
->st_mode
= S_IFREG
| S_IREAD
;
340 if (!(cipb
.hFileInfo
.ioFlAttrib
& 0x1))
341 buf
->st_mode
|= S_IWRITE
; /* bit 1 = 1 for locked files/directories */
342 if (cipb
.hFileInfo
.ioFlFndrInfo
.fdType
== 'APPL')
343 buf
->st_mode
|= S_IEXEC
;
344 buf
->st_ino
= cipb
.hFileInfo
.ioDirID
;
345 buf
->st_dev
= cipb
.hFileInfo
.ioVRefNum
;
346 buf
->st_size
= cipb
.hFileInfo
.ioFlLgLen
;
349 = cipb
.hFileInfo
.ioFlMdDat
- MAC_UNIX_EPOCH_DIFF
;
350 buf
->st_ctime
= cipb
.hFileInfo
.ioFlCrDat
- MAC_UNIX_EPOCH_DIFF
;
353 if (cipb
.hFileInfo
.ioFlFndrInfo
.fdFlags
& 0x8000)
355 /* identify alias files as symlinks */
356 buf
->st_mode
&= ~S_IFREG
;
357 buf
->st_mode
|= S_IFLNK
;
361 buf
->st_uid
= getuid ();
362 buf
->st_gid
= getgid ();
370 lstat (const char *path
, struct stat
*buf
)
373 char true_pathname
[MAXPATHLEN
+1];
375 /* Try looking for the file without resolving aliases first. */
376 if ((result
= stat_noalias (path
, buf
)) >= 0)
379 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
382 return stat_noalias (true_pathname
, buf
);
387 stat (const char *path
, struct stat
*sb
)
390 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
393 if ((result
= stat_noalias (path
, sb
)) >= 0 &&
394 ! (sb
->st_mode
& S_IFLNK
))
397 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
400 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
403 fully_resolved_name
[len
] = '\0';
404 /* in fact our readlink terminates strings */
405 return lstat (fully_resolved_name
, sb
);
408 return lstat (true_pathname
, sb
);
413 /* CW defines fstat in stat.mac.c while MPW does not provide this
414 function. Without the information of how to get from a file
415 descriptor in MPW StdCLib to a Mac OS file spec, it should be hard
416 to implement this function. Fortunately, there is only one place
417 where this function is called in our configuration: in fileio.c,
418 where only the st_dev and st_ino fields are used to determine
419 whether two fildes point to different i-nodes to prevent copying
420 a file onto itself equal. What we have here probably needs
424 fstat (int fildes
, struct stat
*buf
)
427 buf
->st_ino
= fildes
;
428 buf
->st_mode
= S_IFREG
; /* added by T.I. for the copy-file */
429 return 0; /* success */
435 mkdir (const char *dirname
, int mode
)
440 char true_pathname
[MAXPATHLEN
+1], mac_pathname
[MAXPATHLEN
+1];
442 if (find_true_pathname (dirname
, true_pathname
, MAXPATHLEN
+1) == -1)
445 if (posix_to_mac_pathname (true_pathname
, mac_pathname
, MAXPATHLEN
+1) == 0)
448 c2pstr (mac_pathname
);
449 hfpb
.ioNamePtr
= mac_pathname
;
450 hfpb
.ioVRefNum
= 0; /* ignored unless name is invalid */
451 hfpb
.ioDirID
= 0; /* parent is the root */
453 errno
= PBDirCreate ((HParmBlkPtr
) &hfpb
, false);
454 /* just return the Mac OSErr code for now */
455 return errno
== noErr
? 0 : -1;
460 sys_rmdir (const char *dirname
)
463 char mac_pathname
[MAXPATHLEN
+1];
465 if (posix_to_mac_pathname (dirname
, mac_pathname
, MAXPATHLEN
+1) == 0)
468 c2pstr (mac_pathname
);
469 hfpb
.ioNamePtr
= mac_pathname
;
470 hfpb
.ioVRefNum
= 0; /* ignored unless name is invalid */
471 hfpb
.ioDirID
= 0; /* parent is the root */
473 errno
= PBHDelete ((HParmBlkPtr
) &hfpb
, false);
474 return errno
== noErr
? 0 : -1;
479 /* No implementation yet. */
481 execvp (const char *path
, ...)
489 utime (const char *path
, const struct utimbuf
*times
)
491 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
493 char mac_pathname
[MAXPATHLEN
+1];
496 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
499 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
501 fully_resolved_name
[len
] = '\0';
503 strcpy (fully_resolved_name
, true_pathname
);
505 if (!posix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
508 c2pstr (mac_pathname
);
509 cipb
.hFileInfo
.ioNamePtr
= mac_pathname
;
510 cipb
.hFileInfo
.ioVRefNum
= 0;
511 cipb
.hFileInfo
.ioDirID
= 0;
512 cipb
.hFileInfo
.ioFDirIndex
= 0;
513 /* set to 0 to get information about specific dir or file */
515 errno
= PBGetCatInfo (&cipb
, false);
519 if (cipb
.hFileInfo
.ioFlAttrib
& 0x10) /* bit 4 = 1 for directories */
522 cipb
.dirInfo
.ioDrMdDat
= times
->modtime
+ MAC_UNIX_EPOCH_DIFF
;
524 GetDateTime (&cipb
.dirInfo
.ioDrMdDat
);
529 cipb
.hFileInfo
.ioFlMdDat
= times
->modtime
+ MAC_UNIX_EPOCH_DIFF
;
531 GetDateTime (&cipb
.hFileInfo
.ioFlMdDat
);
534 errno
= PBSetCatInfo (&cipb
, false);
535 return errno
== noErr
? 0 : -1;
549 /* Like stat, but test for access mode in hfpb.ioFlAttrib */
551 access (const char *path
, int mode
)
553 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
555 char mac_pathname
[MAXPATHLEN
+1];
558 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
561 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
563 fully_resolved_name
[len
] = '\0';
565 strcpy (fully_resolved_name
, true_pathname
);
567 if (!posix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
570 c2pstr (mac_pathname
);
571 cipb
.hFileInfo
.ioNamePtr
= mac_pathname
;
572 cipb
.hFileInfo
.ioVRefNum
= 0;
573 cipb
.hFileInfo
.ioDirID
= 0;
574 cipb
.hFileInfo
.ioFDirIndex
= 0;
575 /* set to 0 to get information about specific dir or file */
577 errno
= PBGetCatInfo (&cipb
, false);
581 if (mode
== F_OK
) /* got this far, file exists */
585 if (cipb
.hFileInfo
.ioFlAttrib
& 0x10) /* path refers to a directory */
589 if (cipb
.hFileInfo
.ioFlFndrInfo
.fdType
== 'APPL')
596 return (cipb
.hFileInfo
.ioFlAttrib
& 0x1) ? -1 : 0;
597 /* don't allow if lock bit is on */
603 #define DEV_NULL_FD 0x10000
607 sys_open (const char *path
, int oflag
)
609 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
611 char mac_pathname
[MAXPATHLEN
+1];
613 if (strcmp (path
, "/dev/null") == 0)
614 return DEV_NULL_FD
; /* some bogus fd to be ignored in write */
616 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
619 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
621 fully_resolved_name
[len
] = '\0';
623 strcpy (fully_resolved_name
, true_pathname
);
625 if (!posix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
630 int res
= open (mac_pathname
, oflag
);
631 /* if (oflag == O_WRONLY || oflag == O_RDWR) */
633 fsetfileinfo (mac_pathname
, 'EMAx', 'TEXT');
635 #else /* not __MRC__ */
636 return open (mac_pathname
, oflag
);
637 #endif /* not __MRC__ */
644 sys_creat (const char *path
, mode_t mode
)
646 char true_pathname
[MAXPATHLEN
+1];
648 char mac_pathname
[MAXPATHLEN
+1];
650 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
653 if (!posix_to_mac_pathname (true_pathname
, mac_pathname
, MAXPATHLEN
+1))
658 int result
= creat (mac_pathname
);
659 fsetfileinfo (mac_pathname
, 'EMAx', 'TEXT');
661 #else /* not __MRC__ */
662 return creat (mac_pathname
, mode
);
663 #endif /* not __MRC__ */
670 sys_unlink (const char *path
)
672 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
674 char mac_pathname
[MAXPATHLEN
+1];
676 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
679 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
681 fully_resolved_name
[len
] = '\0';
683 strcpy (fully_resolved_name
, true_pathname
);
685 if (!posix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
688 return unlink (mac_pathname
);
694 sys_read (int fildes
, char *buf
, int count
)
696 if (fildes
== 0) /* this should not be used for console input */
699 #if __MSL__ >= 0x6000
700 return _read (fildes
, buf
, count
);
702 return read (fildes
, buf
, count
);
709 sys_write (int fildes
, const char *buf
, int count
)
711 if (fildes
== DEV_NULL_FD
)
714 #if __MSL__ >= 0x6000
715 return _write (fildes
, buf
, count
);
717 return write (fildes
, buf
, count
);
724 sys_rename (const char * old_name
, const char * new_name
)
726 char true_old_pathname
[MAXPATHLEN
+1], true_new_pathname
[MAXPATHLEN
+1];
727 char fully_resolved_old_name
[MAXPATHLEN
+1];
729 char mac_old_name
[MAXPATHLEN
+1], mac_new_name
[MAXPATHLEN
+1];
731 if (find_true_pathname (old_name
, true_old_pathname
, MAXPATHLEN
+1) == -1)
734 len
= readlink (true_old_pathname
, fully_resolved_old_name
, MAXPATHLEN
);
736 fully_resolved_old_name
[len
] = '\0';
738 strcpy (fully_resolved_old_name
, true_old_pathname
);
740 if (find_true_pathname (new_name
, true_new_pathname
, MAXPATHLEN
+1) == -1)
743 if (strcmp (fully_resolved_old_name
, true_new_pathname
) == 0)
746 if (!posix_to_mac_pathname (fully_resolved_old_name
,
751 if (!posix_to_mac_pathname(true_new_pathname
, mac_new_name
, MAXPATHLEN
+1))
754 /* If a file with new_name already exists, rename deletes the old
755 file in Unix. CW version fails in these situation. So we add a
756 call to unlink here. */
757 (void) unlink (mac_new_name
);
759 return rename (mac_old_name
, mac_new_name
);
764 extern FILE *fopen (const char *name
, const char *mode
);
766 sys_fopen (const char *name
, const char *mode
)
768 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
770 char mac_pathname
[MAXPATHLEN
+1];
772 if (find_true_pathname (name
, true_pathname
, MAXPATHLEN
+1) == -1)
775 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
777 fully_resolved_name
[len
] = '\0';
779 strcpy (fully_resolved_name
, true_pathname
);
781 if (!posix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
786 if (mode
[0] == 'w' || mode
[0] == 'a')
787 fsetfileinfo (mac_pathname
, 'EMAx', 'TEXT');
788 #endif /* not __MRC__ */
789 return fopen (mac_pathname
, mode
);
796 long target_ticks
= 0;
799 __sigfun alarm_signal_func
= (__sigfun
) 0;
801 __signal_func_ptr alarm_signal_func
= (__signal_func_ptr
) 0;
802 #else /* not __MRC__ and not __MWERKS__ */
804 #endif /* not __MRC__ and not __MWERKS__ */
807 /* These functions simulate SIG_ALRM. The stub for function signal
808 stores the signal handler function in alarm_signal_func if a
809 SIG_ALRM is encountered. check_alarm is called in XTread_socket,
810 which emacs calls periodically. A pending alarm is represented by
811 a non-zero target_ticks value. check_alarm calls the handler
812 function pointed to by alarm_signal_func if one has been set up and
813 an alarm is pending. */
818 if (target_ticks
&& TickCount () > target_ticks
)
821 if (alarm_signal_func
)
822 (*alarm_signal_func
)(SIGALRM
);
828 select (n
, rfds
, wfds
, efds
, timeout
)
833 struct timeval
*timeout
;
835 #ifdef TARGET_API_MAC_CARBON
837 #else /* not TARGET_API_MAC_CARBON */
838 EMACS_TIME end_time
, now
;
841 /* Can only handle wait for keyboard input. */
842 if (n
> 1 || wfds
|| efds
)
845 EMACS_GET_TIME (end_time
);
846 EMACS_ADD_TIME (end_time
, end_time
, *timeout
);
850 /* Also return true if an event other than a keyDown has
851 occurred. This causes kbd_buffer_get_event in keyboard.c to
852 call read_avail_input which in turn calls XTread_socket to
853 poll for these events. Otherwise these never get processed
854 except but a very slow poll timer. */
855 if (FD_ISSET (0, rfds
) && EventAvail (everyEvent
, &e
))
858 /* Also check movement of the mouse. */
861 static Point old_mouse_pos
= {-1, -1};
863 GetMouse (&mouse_pos
);
864 if (!EqualPt (mouse_pos
, old_mouse_pos
))
866 old_mouse_pos
= mouse_pos
;
871 WaitNextEvent (0, &e
, 1UL, NULL
); /* Accept no event; wait 1
874 EMACS_GET_TIME (now
);
875 EMACS_SUB_TIME (now
, end_time
, now
);
877 while (!EMACS_TIME_NEG_P (now
));
880 #endif /* not TARGET_API_MAC_CARBON */
884 /* Called in sys_select to wait for an alarm signal to arrive. */
892 if (!target_ticks
) /* no alarm pending */
895 if ((tick
= TickCount ()) < target_ticks
)
896 WaitNextEvent (0, &e
, target_ticks
- tick
, NULL
); /* Accept no event;
897 just wait. by T.I. */
900 if (alarm_signal_func
)
901 (*alarm_signal_func
)(SIGALRM
);
910 long remaining
= target_ticks
? (TickCount () - target_ticks
) / 60 : 0;
912 target_ticks
= seconds
? TickCount () + 60 * seconds
: 0;
914 return (remaining
< 0) ? 0 : (unsigned int) remaining
;
920 extern __sigfun
signal (int signal
, __sigfun signal_func
);
922 sys_signal (int signal_num
, __sigfun signal_func
)
924 extern __signal_func_ptr
signal (int signal
, __signal_func_ptr signal_func
);
926 sys_signal (int signal_num
, __signal_func_ptr signal_func
)
927 #else /* not __MRC__ and not __MWERKS__ */
929 #endif /* not __MRC__ and not __MWERKS__ */
931 if (signal_num
!= SIGALRM
)
932 return signal (signal_num
, signal_func
);
936 __sigfun old_signal_func
;
938 __signal_func_ptr old_signal_func
;
942 old_signal_func
= alarm_signal_func
;
943 alarm_signal_func
= signal_func
;
944 return old_signal_func
;
949 /* gettimeofday should return the amount of time (in a timeval
950 structure) since midnight today. The toolbox function Microseconds
951 returns the number of microseconds (in a UnsignedWide value) since
952 the machine was booted. Also making this complicated is WideAdd,
953 WideSubtract, etc. take wide values. */
960 static wide wall_clock_at_epoch
, clicks_at_epoch
;
961 UnsignedWide uw_microseconds
;
963 time_t sys_time (time_t *);
965 /* If this function is called for the first time, record the number
966 of seconds since midnight and the number of microseconds since
967 boot at the time of this first call. */
972 systime
= sys_time (NULL
);
973 /* Store microseconds since midnight in wall_clock_at_epoch. */
974 WideMultiply (systime
, 1000000L, &wall_clock_at_epoch
);
975 Microseconds (&uw_microseconds
);
976 /* Store microseconds since boot in clicks_at_epoch. */
977 clicks_at_epoch
.hi
= uw_microseconds
.hi
;
978 clicks_at_epoch
.lo
= uw_microseconds
.lo
;
981 /* Get time since boot */
982 Microseconds (&uw_microseconds
);
984 /* Convert to time since midnight*/
985 w_microseconds
.hi
= uw_microseconds
.hi
;
986 w_microseconds
.lo
= uw_microseconds
.lo
;
987 WideSubtract (&w_microseconds
, &clicks_at_epoch
);
988 WideAdd (&w_microseconds
, &wall_clock_at_epoch
);
989 tp
->tv_sec
= WideDivide (&w_microseconds
, 1000000L, &tp
->tv_usec
);
997 sleep (unsigned int seconds
)
999 unsigned long time_up
;
1002 time_up
= TickCount () + seconds
* 60;
1003 while (TickCount () < time_up
)
1005 /* Accept no event; just wait. by T.I. */
1006 WaitNextEvent (0, &e
, 30, NULL
);
1011 #endif /* __MRC__ */
1014 /* The time functions adjust time values according to the difference
1015 between the Unix and CW epoches. */
1018 extern struct tm
*gmtime (const time_t *);
1020 sys_gmtime (const time_t *timer
)
1022 time_t unix_time
= *timer
+ CW_OR_MPW_UNIX_EPOCH_DIFF
;
1024 return gmtime (&unix_time
);
1029 extern struct tm
*localtime (const time_t *);
1031 sys_localtime (const time_t *timer
)
1033 #if __MSL__ >= 0x6000
1034 time_t unix_time
= *timer
;
1036 time_t unix_time
= *timer
+ CW_OR_MPW_UNIX_EPOCH_DIFF
;
1039 return localtime (&unix_time
);
1044 extern char *ctime (const time_t *);
1046 sys_ctime (const time_t *timer
)
1048 #if __MSL__ >= 0x6000
1049 time_t unix_time
= *timer
;
1051 time_t unix_time
= *timer
+ CW_OR_MPW_UNIX_EPOCH_DIFF
;
1054 return ctime (&unix_time
);
1059 extern time_t time (time_t *);
1061 sys_time (time_t *timer
)
1063 #if __MSL__ >= 0x6000
1064 time_t mac_time
= time (NULL
);
1066 time_t mac_time
= time (NULL
) - CW_OR_MPW_UNIX_EPOCH_DIFF
;
1076 /* MPW strftime broken for "%p" format */
1081 sys_strftime (char * s
, size_t maxsize
, const char * format
,
1082 const struct tm
* timeptr
)
1084 if (strcmp (format
, "%p") == 0)
1088 if (timeptr
->tm_hour
< 12)
1100 return strftime (s
, maxsize
, format
, timeptr
);
1102 #endif /* __MRC__ */
1105 /* no subprocesses, empty wait */
1115 croak (char *badfunc
)
1117 printf ("%s not yet implemented\r\n", badfunc
);
1123 index (const char * str
, int chr
)
1125 return strchr (str
, chr
);
1130 mktemp (char *template)
1135 len
= strlen (template);
1137 while (k
>= 0 && template[k
] == 'X')
1140 k
++; /* make k index of first 'X' */
1144 /* Zero filled, number of digits equal to the number of X's. */
1145 sprintf (&template[k
], "%0*d", len
-k
, seqnum
++);
1154 /* Emulate getpwuid, getpwnam and others. */
1156 #define PASSWD_FIELD_SIZE 256
1158 static char my_passwd_name
[PASSWD_FIELD_SIZE
];
1159 static char my_passwd_dir
[MAXPATHLEN
+1];
1161 static struct passwd my_passwd
=
1168 /* Initialized by main () in macterm.c to pathname of emacs directory. */
1170 char emacs_passwd_dir
[MAXPATHLEN
+1];
1176 init_emacs_passwd_dir ()
1180 if (getwd (emacs_passwd_dir
) && getwd (my_passwd_dir
))
1182 /* Need pathname of first ancestor that begins with "emacs"
1183 since Mac emacs application is somewhere in the emacs-*
1185 int len
= strlen (emacs_passwd_dir
);
1187 /* j points to the "/" following the directory name being
1190 while (i
>= 0 && !found
)
1192 while (i
>= 0 && emacs_passwd_dir
[i
] != '/')
1194 if (emacs_passwd_dir
[i
] == '/' && i
+5 < len
)
1195 found
= (strncmp (&(emacs_passwd_dir
[i
+1]), "emacs", 5) == 0);
1197 emacs_passwd_dir
[j
+1] = '\0';
1208 /* Setting to "/" probably won't work but set it to something
1210 strcpy (emacs_passwd_dir
, "/");
1211 strcpy (my_passwd_dir
, "/");
1216 static struct passwd emacs_passwd
=
1222 static int my_passwd_inited
= 0;
1230 /* Note: my_passwd_dir initialized in int_emacs_passwd_dir to
1231 directory where Emacs was started. */
1233 owner_name
= (char **) GetResource ('STR ',-16096);
1237 BlockMove ((unsigned char *) *owner_name
,
1238 (unsigned char *) my_passwd_name
,
1240 HUnlock (owner_name
);
1241 p2cstr ((unsigned char *) my_passwd_name
);
1244 my_passwd_name
[0] = 0;
1249 getpwuid (uid_t uid
)
1251 if (!my_passwd_inited
)
1254 my_passwd_inited
= 1;
1262 getpwnam (const char *name
)
1264 if (strcmp (name
, "emacs") == 0)
1265 return &emacs_passwd
;
1267 if (!my_passwd_inited
)
1270 my_passwd_inited
= 1;
1277 /* The functions fork, kill, sigsetmask, sigblock, request_sigio,
1278 setpgrp, setpriority, and unrequest_sigio are defined to be empty
1299 error ("Can't spawn subshell");
1318 request_sigio (void)
1324 unrequest_sigio (void)
1339 pipe (int _fildes
[2])
1346 /* Hard and symbolic links. */
1349 symlink (const char *name1
, const char *name2
)
1357 link (const char *name1
, const char *name2
)
1363 #endif /* ! MAC_OSX */
1365 /* Determine the path name of the file specified by VREFNUM, DIRID,
1366 and NAME and place that in the buffer PATH of length
1369 path_from_vol_dir_name (char *path
, int man_path_len
, short vol_ref_num
,
1370 long dir_id
, ConstStr255Param name
)
1376 if (strlen (name
) > man_path_len
)
1379 memcpy (dir_name
, name
, name
[0]+1);
1380 memcpy (path
, name
, name
[0]+1);
1383 cipb
.dirInfo
.ioDrParID
= dir_id
;
1384 cipb
.dirInfo
.ioNamePtr
= dir_name
;
1388 cipb
.dirInfo
.ioVRefNum
= vol_ref_num
;
1389 cipb
.dirInfo
.ioFDirIndex
= -1;
1390 cipb
.dirInfo
.ioDrDirID
= cipb
.dirInfo
.ioDrParID
;
1391 /* go up to parent each time */
1393 err
= PBGetCatInfo (&cipb
, false);
1398 if (strlen (dir_name
) + strlen (path
) + 1 >= man_path_len
)
1401 strcat (dir_name
, ":");
1402 strcat (dir_name
, path
);
1403 /* attach to front since we're going up directory tree */
1404 strcpy (path
, dir_name
);
1406 while (cipb
.dirInfo
.ioDrDirID
!= fsRtDirID
);
1407 /* stop when we see the volume's root directory */
1409 return 1; /* success */
1415 readlink (const char *path
, char *buf
, int bufsiz
)
1417 char mac_sym_link_name
[MAXPATHLEN
+1];
1420 Boolean target_is_folder
, was_aliased
;
1421 Str255 directory_name
, mac_pathname
;
1424 if (posix_to_mac_pathname (path
, mac_sym_link_name
, MAXPATHLEN
+1) == 0)
1427 c2pstr (mac_sym_link_name
);
1428 err
= FSMakeFSSpec (0, 0, mac_sym_link_name
, &fsspec
);
1435 err
= ResolveAliasFile (&fsspec
, true, &target_is_folder
, &was_aliased
);
1436 if (err
!= noErr
|| !was_aliased
)
1442 if (path_from_vol_dir_name (mac_pathname
, 255, fsspec
.vRefNum
, fsspec
.parID
,
1449 if (mac_to_posix_pathname (mac_pathname
, buf
, bufsiz
) == 0)
1455 return strlen (buf
);
1459 /* Convert a path to one with aliases fully expanded. */
1462 find_true_pathname (const char *path
, char *buf
, int bufsiz
)
1464 char *q
, temp
[MAXPATHLEN
+1];
1468 if (bufsiz
<= 0 || path
== 0 || path
[0] == '\0')
1475 q
= strchr (p
+ 1, '/');
1477 q
= strchr (p
, '/');
1478 len
= 0; /* loop may not be entered, e.g., for "/" */
1483 strncat (temp
, p
, q
- p
);
1484 len
= readlink (temp
, buf
, bufsiz
);
1487 if (strlen (temp
) + 1 > bufsiz
)
1497 if (len
+ strlen (p
) + 1 >= bufsiz
)
1501 return len
+ strlen (p
);
1506 umask (mode_t numask
)
1508 static mode_t mask
= 022;
1509 mode_t oldmask
= mask
;
1516 chmod (const char *path
, mode_t mode
)
1518 /* say it always succeed for now */
1527 return fcntl (oldd
, F_DUPFD
, 0);
1529 /* current implementation of fcntl in fcntl.mac.c simply returns old
1531 return fcntl (oldd
, F_DUPFD
);
1538 /* This is from the original sysdep.c. Emulate BSD dup2. First close
1539 newd if it already exists. Then, attempt to dup oldd. If not
1540 successful, call dup2 recursively until we are, then close the
1541 unsuccessful ones. */
1544 dup2 (int oldd
, int newd
)
1555 ret
= dup2 (oldd
, newd
);
1561 /* let it fail for now */
1578 ioctl (int d
, int request
, void *argp
)
1588 if (fildes
>=0 && fildes
<= 2)
1621 #endif /* __MRC__ */
1625 #if __MSL__ < 0x6000
1633 #endif /* __MWERKS__ */
1635 #endif /* ! MAC_OSX */
1638 /* Return the path to the directory in which Emacs can create
1639 temporary files. The MacOS "temporary items" directory cannot be
1640 used because it removes the file written by a process when it
1641 exits. In that sense it's more like "/dev/null" than "/tmp" (but
1642 again not exactly). And of course Emacs needs to read back the
1643 files written by its subprocesses. So here we write the files to a
1644 directory "Emacs" in the Preferences Folder. This directory is
1645 created if it does not exist. */
1648 get_temp_dir_name ()
1650 static char *temp_dir_name
= NULL
;
1654 Str255 dir_name
, full_path
;
1656 char unix_dir_name
[MAXPATHLEN
+1];
1659 /* Cache directory name with pointer temp_dir_name.
1660 Look for it only the first time. */
1663 err
= FindFolder (kOnSystemDisk
, kPreferencesFolderType
, kCreateFolder
,
1664 &vol_ref_num
, &dir_id
);
1668 if (!path_from_vol_dir_name (full_path
, 255, vol_ref_num
, dir_id
, "\p"))
1671 if (strlen (full_path
) + 6 <= MAXPATHLEN
)
1672 strcat (full_path
, "Emacs:");
1676 if (!mac_to_posix_pathname (full_path
, unix_dir_name
, MAXPATHLEN
+1))
1679 dir
= opendir (unix_dir_name
); /* check whether temp directory exists */
1682 else if (mkdir (unix_dir_name
, 0700) != 0) /* create it if not */
1685 temp_dir_name
= (char *) malloc (strlen (unix_dir_name
) + 1);
1686 strcpy (temp_dir_name
, unix_dir_name
);
1689 return temp_dir_name
;
1694 /* Allocate and construct an array of pointers to strings from a list
1695 of strings stored in a 'STR#' resource. The returned pointer array
1696 is stored in the style of argv and environ: if the 'STR#' resource
1697 contains numString strings, a pointer array with numString+1
1698 elements is returned in which the last entry contains a null
1699 pointer. The pointer to the pointer array is passed by pointer in
1700 parameter t. The resource ID of the 'STR#' resource is passed in
1701 parameter StringListID.
1705 get_string_list (char ***t
, short string_list_id
)
1711 h
= GetResource ('STR#', string_list_id
);
1716 num_strings
= * (short *) p
;
1718 *t
= (char **) malloc (sizeof (char *) * (num_strings
+ 1));
1719 for (i
= 0; i
< num_strings
; i
++)
1721 short length
= *p
++;
1722 (*t
)[i
] = (char *) malloc (length
+ 1);
1723 strncpy ((*t
)[i
], p
, length
);
1724 (*t
)[i
][length
] = '\0';
1727 (*t
)[num_strings
] = 0;
1732 /* Return no string in case GetResource fails. Bug fixed by
1733 Ikegami Tsutomu. Caused MPW build to crash without sym -on
1734 option (no sym -on implies -opt local). */
1735 *t
= (char **) malloc (sizeof (char *));
1742 get_path_to_system_folder ()
1747 Str255 dir_name
, full_path
;
1749 static char system_folder_unix_name
[MAXPATHLEN
+1];
1752 err
= FindFolder (kOnSystemDisk
, kSystemFolderType
, kDontCreateFolder
,
1753 &vol_ref_num
, &dir_id
);
1757 if (!path_from_vol_dir_name (full_path
, 255, vol_ref_num
, dir_id
, "\p"))
1760 if (!mac_to_posix_pathname (full_path
, system_folder_unix_name
,
1764 return system_folder_unix_name
;
1770 #define ENVIRON_STRING_LIST_ID 128
1772 /* Get environment variable definitions from STR# resource. */
1779 get_string_list (&environ
, ENVIRON_STRING_LIST_ID
);
1785 /* Make HOME directory the one Emacs starts up in if not specified
1787 if (getenv ("HOME") == NULL
)
1789 environ
= (char **) realloc (environ
, sizeof (char *) * (i
+ 2));
1792 environ
[i
] = (char *) malloc (strlen (my_passwd_dir
) + 6);
1795 strcpy (environ
[i
], "HOME=");
1796 strcat (environ
[i
], my_passwd_dir
);
1803 /* Make HOME directory the one Emacs starts up in if not specified
1805 if (getenv ("MAIL") == NULL
)
1807 environ
= (char **) realloc (environ
, sizeof (char *) * (i
+ 2));
1810 char * path_to_system_folder
= get_path_to_system_folder ();
1811 environ
[i
] = (char *) malloc (strlen (path_to_system_folder
) + 22);
1814 strcpy (environ
[i
], "MAIL=");
1815 strcat (environ
[i
], path_to_system_folder
);
1816 strcat (environ
[i
], "Eudora Folder/In");
1824 /* Return the value of the environment variable NAME. */
1827 getenv (const char *name
)
1829 int length
= strlen(name
);
1832 for (e
= environ
; *e
!= 0; e
++)
1833 if (strncmp(*e
, name
, length
) == 0 && (*e
)[length
] == '=')
1834 return &(*e
)[length
+ 1];
1836 if (strcmp (name
, "TMPDIR") == 0)
1837 return get_temp_dir_name ();
1844 /* see Interfaces&Libraries:Interfaces:CIncludes:signal.h */
1845 char *sys_siglist
[] =
1847 "Zero is not a signal!!!",
1849 "Interactive user interrupt", /* 2 */ "?",
1850 "Floating point exception", /* 4 */ "?", "?", "?",
1851 "Illegal instruction", /* 8 */ "?", "?", "?", "?", "?", "?", "?",
1852 "Segment violation", /* 16 */ "?", "?", "?", "?", "?", "?", "?",
1853 "?", "?", "?", "?", "?", "?", "?", "?",
1857 char *sys_siglist
[] =
1859 "Zero is not a signal!!!",
1861 "Floating point exception",
1862 "Illegal instruction",
1863 "Interactive user interrupt",
1864 "Segment violation",
1867 #else /* not __MRC__ and not __MWERKS__ */
1869 #endif /* not __MRC__ and not __MWERKS__ */
1872 #include <utsname.h>
1875 uname (struct utsname
*name
)
1878 system_name
= GetString (-16413); /* IM - Resource Manager Reference */
1881 BlockMove (*system_name
, name
->nodename
, (*system_name
)[0]+1);
1882 p2cstr (name
->nodename
);
1890 #include <Processes.h>
1893 /* Event class of HLE sent to subprocess. */
1894 const OSType kEmacsSubprocessSend
= 'ESND';
1896 /* Event class of HLE sent back from subprocess. */
1897 const OSType kEmacsSubprocessReply
= 'ERPY';
1901 mystrchr (char *s
, char c
)
1903 while (*s
&& *s
!= c
)
1931 mystrcpy (char *to
, char *from
)
1943 /* Start a Mac subprocess. Arguments for it is passed in argv (null
1944 terminated). The process should run with the default directory
1945 "workdir", read input from "infn", and write output and error to
1946 "outfn" and "errfn", resp. The Process Manager call
1947 LaunchApplication is used to start the subprocess. We use high
1948 level events as the mechanism to pass arguments to the subprocess
1949 and to make Emacs wait for the subprocess to terminate and pass
1950 back a result code. The bulk of the code here packs the arguments
1951 into one message to be passed together with the high level event.
1952 Emacs also sometimes starts a subprocess using a shell to perform
1953 wildcard filename expansion. Since we don't really have a shell on
1954 the Mac, this case is detected and the starting of the shell is
1955 by-passed. We really need to add code here to do filename
1956 expansion to support such functionality. */
1959 run_mac_command (argv
, workdir
, infn
, outfn
, errfn
)
1960 unsigned char **argv
;
1961 const char *workdir
;
1962 const char *infn
, *outfn
, *errfn
;
1964 #ifdef TARGET_API_MAC_CARBON
1966 #else /* not TARGET_API_MAC_CARBON */
1967 char macappname
[MAXPATHLEN
+1], macworkdir
[MAXPATHLEN
+1];
1968 char macinfn
[MAXPATHLEN
+1], macoutfn
[MAXPATHLEN
+1], macerrfn
[MAXPATHLEN
+1];
1969 int paramlen
, argc
, newargc
, j
, retries
;
1970 char **newargv
, *param
, *p
;
1973 LaunchParamBlockRec lpbr
;
1974 EventRecord send_event
, reply_event
;
1975 RgnHandle cursor_region_handle
;
1977 unsigned long ref_con
, len
;
1979 if (posix_to_mac_pathname (workdir
, macworkdir
, MAXPATHLEN
+1) == 0)
1981 if (posix_to_mac_pathname (infn
, macinfn
, MAXPATHLEN
+1) == 0)
1983 if (posix_to_mac_pathname (outfn
, macoutfn
, MAXPATHLEN
+1) == 0)
1985 if (posix_to_mac_pathname (errfn
, macerrfn
, MAXPATHLEN
+1) == 0)
1988 paramlen
= strlen (macworkdir
) + strlen (macinfn
) + strlen (macoutfn
)
1989 + strlen (macerrfn
) + 4; /* count nulls at end of strings */
1998 /* If a subprocess is invoked with a shell, we receive 3 arguments
1999 of the form: "<path to emacs bins>/sh" "-c" "<path to emacs
2000 bins>/<command> <command args>" */
2001 j
= strlen (argv
[0]);
2002 if (j
>= 3 && strcmp (argv
[0]+j
-3, "/sh") == 0
2003 && argc
== 3 && strcmp (argv
[1], "-c") == 0)
2005 char *command
, *t
, tempmacpathname
[MAXPATHLEN
+1];
2007 /* The arguments for the command in argv[2] are separated by
2008 spaces. Count them and put the count in newargc. */
2009 command
= (char *) alloca (strlen (argv
[2])+2);
2010 strcpy (command
, argv
[2]);
2011 if (command
[strlen (command
) - 1] != ' ')
2012 strcat (command
, " ");
2016 t
= mystrchr (t
, ' ');
2020 t
= mystrchr (t
+1, ' ');
2023 newargv
= (char **) alloca (sizeof (char *) * newargc
);
2026 for (j
= 0; j
< newargc
; j
++)
2028 newargv
[j
] = (char *) alloca (strlen (t
) + 1);
2029 mystrcpy (newargv
[j
], t
);
2032 paramlen
+= strlen (newargv
[j
]) + 1;
2035 if (strncmp (newargv
[0], "~emacs/", 7) == 0)
2037 if (posix_to_mac_pathname (newargv
[0], tempmacpathname
, MAXPATHLEN
+1)
2042 { /* sometimes Emacs call "sh" without a path for the command */
2044 char *t
= (char *) alloca (strlen (newargv
[0]) + 7 + 1);
2045 strcpy (t
, "~emacs/");
2046 strcat (t
, newargv
[0]);
2049 openp (Vexec_path
, build_string (newargv
[0]), EXEC_SUFFIXES
, &path
,
2050 make_number (X_OK
));
2054 if (posix_to_mac_pathname (SDATA (path
), tempmacpathname
,
2058 strcpy (macappname
, tempmacpathname
);
2062 if (posix_to_mac_pathname (argv
[0], macappname
, MAXPATHLEN
+1) == 0)
2065 newargv
= (char **) alloca (sizeof (char *) * argc
);
2067 for (j
= 1; j
< argc
; j
++)
2069 if (strncmp (argv
[j
], "~emacs/", 7) == 0)
2071 char *t
= strchr (argv
[j
], ' ');
2074 char tempcmdname
[MAXPATHLEN
+1], tempmaccmdname
[MAXPATHLEN
+1];
2075 strncpy (tempcmdname
, argv
[j
], t
-argv
[j
]);
2076 tempcmdname
[t
-argv
[j
]] = '\0';
2077 if (posix_to_mac_pathname (tempcmdname
, tempmaccmdname
,
2080 newargv
[j
] = (char *) alloca (strlen (tempmaccmdname
)
2082 strcpy (newargv
[j
], tempmaccmdname
);
2083 strcat (newargv
[j
], t
);
2087 char tempmaccmdname
[MAXPATHLEN
+1];
2088 if (posix_to_mac_pathname (argv
[j
], tempmaccmdname
,
2091 newargv
[j
] = (char *) alloca (strlen (tempmaccmdname
)+1);
2092 strcpy (newargv
[j
], tempmaccmdname
);
2096 newargv
[j
] = argv
[j
];
2097 paramlen
+= strlen (newargv
[j
]) + 1;
2101 /* After expanding all the arguments, we now know the length of the
2102 parameter block to be sent to the subprocess as a message
2103 attached to the HLE. */
2104 param
= (char *) malloc (paramlen
+ 1);
2110 /* first byte of message contains number of arguments for command */
2111 strcpy (p
, macworkdir
);
2112 p
+= strlen (macworkdir
);
2114 /* null terminate strings sent so it's possible to use strcpy over there */
2115 strcpy (p
, macinfn
);
2116 p
+= strlen (macinfn
);
2118 strcpy (p
, macoutfn
);
2119 p
+= strlen (macoutfn
);
2121 strcpy (p
, macerrfn
);
2122 p
+= strlen (macerrfn
);
2124 for (j
= 1; j
< newargc
; j
++)
2126 strcpy (p
, newargv
[j
]);
2127 p
+= strlen (newargv
[j
]);
2131 c2pstr (macappname
);
2133 iErr
= FSMakeFSSpec (0, 0, macappname
, &spec
);
2141 lpbr
.launchBlockID
= extendedBlock
;
2142 lpbr
.launchEPBLength
= extendedBlockLen
;
2143 lpbr
.launchControlFlags
= launchContinue
+ launchNoFileFlags
;
2144 lpbr
.launchAppSpec
= &spec
;
2145 lpbr
.launchAppParameters
= NULL
;
2147 iErr
= LaunchApplication (&lpbr
); /* call the subprocess */
2154 send_event
.what
= kHighLevelEvent
;
2155 send_event
.message
= kEmacsSubprocessSend
;
2156 /* Event ID stored in "where" unused */
2159 /* OS may think current subprocess has terminated if previous one
2160 terminated recently. */
2163 iErr
= PostHighLevelEvent (&send_event
, &lpbr
.launchProcessSN
, 0, param
,
2164 paramlen
+ 1, receiverIDisPSN
);
2166 while (iErr
== sessClosedErr
&& retries
-- > 0);
2174 cursor_region_handle
= NewRgn ();
2176 /* Wait for the subprocess to finish, when it will send us a ERPY
2177 high level event. */
2179 if (WaitNextEvent (highLevelEventMask
, &reply_event
, 180,
2180 cursor_region_handle
)
2181 && reply_event
.message
== kEmacsSubprocessReply
)
2184 /* The return code is sent through the refCon */
2185 iErr
= AcceptHighLevelEvent (&targ
, &ref_con
, NULL
, &len
);
2188 DisposeHandle ((Handle
) cursor_region_handle
);
2193 DisposeHandle ((Handle
) cursor_region_handle
);
2197 #endif /* not TARGET_API_MAC_CARBON */
2202 opendir (const char *dirname
)
2204 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
2205 char mac_pathname
[MAXPATHLEN
+1], vol_name
[MAXPATHLEN
+1];
2209 int len
, vol_name_len
;
2211 if (find_true_pathname (dirname
, true_pathname
, MAXPATHLEN
+1) == -1)
2214 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
2216 fully_resolved_name
[len
] = '\0';
2218 strcpy (fully_resolved_name
, true_pathname
);
2220 dirp
= (DIR *) malloc (sizeof(DIR));
2224 /* Handle special case when dirname is "/": sets up for readir to
2225 get all mount volumes. */
2226 if (strcmp (fully_resolved_name
, "/") == 0)
2228 dirp
->getting_volumes
= 1; /* special all mounted volumes DIR struct */
2229 dirp
->current_index
= 1; /* index for first volume */
2233 /* Handle typical cases: not accessing all mounted volumes. */
2234 if (!posix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
2237 /* Emacs calls opendir without the trailing '/', Mac needs trailing ':' */
2238 len
= strlen (mac_pathname
);
2239 if (mac_pathname
[len
- 1] != ':' && len
< MAXPATHLEN
)
2240 strcat (mac_pathname
, ":");
2242 /* Extract volume name */
2243 vol_name_len
= strchr (mac_pathname
, ':') - mac_pathname
;
2244 strncpy (vol_name
, mac_pathname
, vol_name_len
);
2245 vol_name
[vol_name_len
] = '\0';
2246 strcat (vol_name
, ":");
2248 c2pstr (mac_pathname
);
2249 cipb
.hFileInfo
.ioNamePtr
= mac_pathname
;
2250 /* using full pathname so vRefNum and DirID ignored */
2251 cipb
.hFileInfo
.ioVRefNum
= 0;
2252 cipb
.hFileInfo
.ioDirID
= 0;
2253 cipb
.hFileInfo
.ioFDirIndex
= 0;
2254 /* set to 0 to get information about specific dir or file */
2256 errno
= PBGetCatInfo (&cipb
, false);
2263 if (!(cipb
.hFileInfo
.ioFlAttrib
& 0x10)) /* bit 4 = 1 for directories */
2264 return 0; /* not a directory */
2266 dirp
->dir_id
= cipb
.dirInfo
.ioDrDirID
; /* used later in readdir */
2267 dirp
->getting_volumes
= 0;
2268 dirp
->current_index
= 1; /* index for first file/directory */
2271 vpb
.ioNamePtr
= vol_name
;
2272 /* using full pathname so vRefNum and DirID ignored */
2274 vpb
.ioVolIndex
= -1;
2275 errno
= PBHGetVInfo ((union HParamBlockRec
*) &vpb
, false);
2282 dirp
->vol_ref_num
= vpb
.ioVRefNum
;
2299 HParamBlockRec hpblock
;
2301 static struct dirent s_dirent
;
2302 static Str255 s_name
;
2306 /* Handle the root directory containing the mounted volumes. Call
2307 PBHGetVInfo specifying an index to obtain the info for a volume.
2308 PBHGetVInfo returns an error when it receives an index beyond the
2309 last volume, at which time we should return a nil dirent struct
2311 if (dp
->getting_volumes
)
2313 hpblock
.volumeParam
.ioNamePtr
= s_name
;
2314 hpblock
.volumeParam
.ioVRefNum
= 0;
2315 hpblock
.volumeParam
.ioVolIndex
= dp
->current_index
;
2317 errno
= PBHGetVInfo (&hpblock
, false);
2325 strcat (s_name
, "/"); /* need "/" for stat to work correctly */
2327 dp
->current_index
++;
2329 s_dirent
.d_ino
= hpblock
.volumeParam
.ioVRefNum
;
2330 s_dirent
.d_name
= s_name
;
2336 cipb
.hFileInfo
.ioVRefNum
= dp
->vol_ref_num
;
2337 cipb
.hFileInfo
.ioNamePtr
= s_name
;
2338 /* location to receive filename returned */
2340 /* return only visible files */
2344 cipb
.hFileInfo
.ioDirID
= dp
->dir_id
;
2345 /* directory ID found by opendir */
2346 cipb
.hFileInfo
.ioFDirIndex
= dp
->current_index
;
2348 errno
= PBGetCatInfo (&cipb
, false);
2355 /* insist on a visible entry */
2356 if (cipb
.hFileInfo
.ioFlAttrib
& 0x10) /* directory? */
2357 done
= !(cipb
.dirInfo
.ioDrUsrWds
.frFlags
& fInvisible
);
2359 done
= !(cipb
.hFileInfo
.ioFlFndrInfo
.fdFlags
& fInvisible
);
2361 dp
->current_index
++;
2374 s_dirent
.d_ino
= cipb
.dirInfo
.ioDrDirID
;
2375 /* value unimportant: non-zero for valid file */
2376 s_dirent
.d_name
= s_name
;
2386 char mac_pathname
[MAXPATHLEN
+1];
2387 Str255 directory_name
;
2391 if (path_from_vol_dir_name (mac_pathname
, 255, 0, 0, "\p") == 0)
2394 if (mac_to_posix_pathname (mac_pathname
, path
, MAXPATHLEN
+1) == 0)
2400 #endif /* ! MAC_OSX */
2404 initialize_applescript ()
2409 /* if open fails, as_scripting_component is set to NULL. Its
2410 subsequent use in OSA calls will fail with badComponentInstance
2412 as_scripting_component
= OpenDefaultComponent (kOSAComponentType
,
2413 kAppleScriptSubtype
);
2415 null_desc
.descriptorType
= typeNull
;
2416 null_desc
.dataHandle
= 0;
2417 osaerror
= OSAMakeContext (as_scripting_component
, &null_desc
,
2418 kOSANullScript
, &as_script_context
);
2420 as_script_context
= kOSANullScript
;
2421 /* use default context if create fails */
2425 void terminate_applescript()
2427 OSADispose (as_scripting_component
, as_script_context
);
2428 CloseComponent (as_scripting_component
);
2432 /* Compile and execute the AppleScript SCRIPT and return the error
2433 status as function value. A zero is returned if compilation and
2434 execution is successful, in which case RESULT returns a pointer to
2435 a string containing the resulting script value. Otherwise, the Mac
2436 error code is returned and RESULT returns a pointer to an error
2437 string. In both cases the caller should deallocate the storage
2438 used by the string pointed to by RESULT if it is non-NULL. For
2439 documentation on the MacOS scripting architecture, see Inside
2440 Macintosh - Interapplication Communications: Scripting Components. */
2443 do_applescript (char *script
, char **result
)
2445 AEDesc script_desc
, result_desc
, error_desc
;
2452 if (!as_scripting_component
)
2453 initialize_applescript();
2455 error
= AECreateDesc (typeChar
, script
, strlen(script
), &script_desc
);
2459 osaerror
= OSADoScript (as_scripting_component
, &script_desc
, kOSANullScript
,
2460 typeChar
, kOSAModeNull
, &result_desc
);
2462 if (osaerror
== errOSAScriptError
)
2464 /* error executing AppleScript: retrieve error message */
2465 if (!OSAScriptError (as_scripting_component
, kOSAErrorMessage
, typeChar
,
2468 #if TARGET_API_MAC_CARBON
2469 length
= AEGetDescDataSize (&error_desc
);
2470 *result
= (char *) xmalloc (length
+ 1);
2473 AEGetDescData (&error_desc
, *result
, length
);
2474 *(*result
+ length
) = '\0';
2476 #else /* not TARGET_API_MAC_CARBON */
2477 HLock (error_desc
.dataHandle
);
2478 length
= GetHandleSize(error_desc
.dataHandle
);
2479 *result
= (char *) xmalloc (length
+ 1);
2482 memcpy (*result
, *(error_desc
.dataHandle
), length
);
2483 *(*result
+ length
) = '\0';
2485 HUnlock (error_desc
.dataHandle
);
2486 #endif /* not TARGET_API_MAC_CARBON */
2487 AEDisposeDesc (&error_desc
);
2490 else if (osaerror
== noErr
) /* success: retrieve resulting script value */
2492 #if TARGET_API_MAC_CARBON
2493 length
= AEGetDescDataSize (&result_desc
);
2494 *result
= (char *) xmalloc (length
+ 1);
2497 AEGetDescData (&result_desc
, *result
, length
);
2498 *(*result
+ length
) = '\0';
2500 #else /* not TARGET_API_MAC_CARBON */
2501 HLock (result_desc
.dataHandle
);
2502 length
= GetHandleSize(result_desc
.dataHandle
);
2503 *result
= (char *) xmalloc (length
+ 1);
2506 memcpy (*result
, *(result_desc
.dataHandle
), length
);
2507 *(*result
+ length
) = '\0';
2509 HUnlock (result_desc
.dataHandle
);
2510 #endif /* not TARGET_API_MAC_CARBON */
2511 AEDisposeDesc (&result_desc
);
2514 AEDisposeDesc (&script_desc
);
2520 DEFUN ("do-applescript", Fdo_applescript
, Sdo_applescript
, 1, 1, 0,
2521 doc
: /* Compile and execute AppleScript SCRIPT and retrieve and return the result.
2522 If compilation and execution are successful, the resulting script
2523 value is returned as a string. Otherwise the function aborts and
2524 displays the error message returned by the AppleScript scripting
2529 char *result
, *temp
;
2530 Lisp_Object lisp_result
;
2533 CHECK_STRING (script
);
2535 status
= do_applescript (SDATA (script
), &result
);
2539 error ("AppleScript error %d", status
);
2542 /* Unfortunately only OSADoScript in do_applescript knows how
2543 how large the resulting script value or error message is
2544 going to be and therefore as caller memory must be
2545 deallocated here. It is necessary to free the error
2546 message before calling error to avoid a memory leak. */
2547 temp
= (char *) alloca (strlen (result
) + 1);
2548 strcpy (temp
, result
);
2555 lisp_result
= build_string (result
);
2562 DEFUN ("mac-file-name-to-posix", Fmac_file_name_to_posix
,
2563 Smac_file_name_to_posix
, 1, 1, 0,
2564 doc
: /* Convert Macintosh filename to Posix form. */)
2566 Lisp_Object mac_filename
;
2568 char posix_filename
[MAXPATHLEN
+1];
2570 CHECK_STRING (mac_filename
);
2572 if (mac_to_posix_pathname (SDATA (mac_filename
), posix_filename
,
2574 return build_string (posix_filename
);
2580 DEFUN ("posix-file-name-to-mac", Fposix_file_name_to_mac
,
2581 Sposix_file_name_to_mac
, 1, 1, 0,
2582 doc
: /* Convert Posix filename to Mac form. */)
2584 Lisp_Object posix_filename
;
2586 char mac_filename
[MAXPATHLEN
+1];
2588 CHECK_STRING (posix_filename
);
2590 if (posix_to_mac_pathname (SDATA (posix_filename
), mac_filename
,
2592 return build_string (mac_filename
);
2598 /* set interprogram-paste-function to mac-paste-function in mac-win.el
2599 to enable Emacs to obtain the contents of the Mac clipboard. */
2600 DEFUN ("mac-paste-function", Fmac_paste_function
, Smac_paste_function
, 0, 0, 0,
2601 doc
: /* Return the contents of the Mac clipboard as a string. */)
2604 #if TARGET_API_MAC_CARBON
2606 ScrapFlavorFlags sff
;
2611 if (GetCurrentScrap (&scrap
) != noErr
)
2614 if (GetScrapFlavorFlags (scrap
, kScrapFlavorTypeText
, &sff
) != noErr
)
2617 if (GetScrapFlavorSize (scrap
, kScrapFlavorTypeText
, &s
) != noErr
)
2620 if ((data
= (char*) alloca (s
)) == NULL
)
2623 if (GetScrapFlavorData (scrap
, kScrapFlavorTypeText
, &s
, data
) != noErr
2627 /* Emacs expects clipboard contents have Unix-style eol's */
2628 for (i
= 0; i
< s
; i
++)
2629 if (data
[i
] == '\r')
2632 return make_string (data
, s
);
2633 #else /* not TARGET_API_MAC_CARBON */
2636 long scrap_offset
, rc
, i
;
2638 my_handle
= NewHandle (0); /* allocate 0-length data area */
2640 rc
= GetScrap (my_handle
, 'TEXT', &scrap_offset
);
2646 /* Emacs expects clipboard contents have Unix-style eol's */
2647 for (i
= 0; i
< rc
; i
++)
2648 if ((*my_handle
)[i
] == '\r')
2649 (*my_handle
)[i
] = '\n';
2651 value
= make_string (*my_handle
, rc
);
2653 HUnlock (my_handle
);
2655 DisposeHandle (my_handle
);
2658 #endif /* not TARGET_API_MAC_CARBON */
2662 /* set interprogram-cut-function to mac-cut-function in mac-win.el
2663 to enable Emacs to write the top of the kill-ring to the Mac clipboard. */
2664 DEFUN ("mac-cut-function", Fmac_cut_function
, Smac_cut_function
, 1, 2, 0,
2665 doc
: /* Put the value of the string parameter to the Mac clipboard. */)
2667 Lisp_Object value
, push
;
2672 /* fixme: ignore the push flag for now */
2674 CHECK_STRING (value
);
2676 len
= SCHARS (value
);
2677 buf
= (char *) alloca (len
+1);
2678 bcopy (SDATA (value
), buf
, len
);
2681 /* convert to Mac-style eol's before sending to clipboard */
2682 for (i
= 0; i
< len
; i
++)
2686 #if TARGET_API_MAC_CARBON
2689 ClearCurrentScrap ();
2690 if (GetCurrentScrap (&scrap
) != noErr
)
2691 error ("cannot get current scrap");
2693 if (PutScrapFlavor (scrap
, kScrapFlavorTypeText
, kScrapFlavorMaskNone
, len
,
2695 error ("cannot put to scrap");
2697 #else /* not TARGET_API_MAC_CARBON */
2699 PutScrap (len
, 'TEXT', buf
);
2700 #endif /* not TARGET_API_MAC_CARBON */
2706 DEFUN ("x-selection-exists-p", Fx_selection_exists_p
, Sx_selection_exists_p
,
2708 doc
: /* Whether there is an owner for the given X Selection.
2709 The arg should be the name of the selection in question, typically one of
2710 the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.
2711 \(Those are literal upper-case symbol names, since that's what X expects.)
2712 For convenience, the symbol nil is the same as `PRIMARY',
2713 and t is the same as `SECONDARY'. */)
2715 Lisp_Object selection
;
2717 CHECK_SYMBOL (selection
);
2719 /* Return nil for PRIMARY and SECONDARY selections; for CLIPBOARD, check
2720 if the clipboard currently has valid text format contents. */
2722 if (EQ (selection
, QCLIPBOARD
))
2724 Lisp_Object val
= Qnil
;
2726 #if TARGET_API_MAC_CARBON
2728 ScrapFlavorFlags sff
;
2730 if (GetCurrentScrap (&scrap
) == noErr
)
2731 if (GetScrapFlavorFlags (scrap
, kScrapFlavorTypeText
, &sff
) == noErr
)
2733 #else /* not TARGET_API_MAC_CARBON */
2735 long rc
, scrap_offset
;
2737 my_handle
= NewHandle (0);
2739 rc
= GetScrap (my_handle
, 'TEXT', &scrap_offset
);
2743 DisposeHandle (my_handle
);
2744 #endif /* not TARGET_API_MAC_CARBON */
2754 extern int inhibit_window_system
;
2755 extern int noninteractive
;
2757 /* When Emacs is started from the Finder, SELECT always immediately
2758 returns as if input is present when file descriptor 0 is polled for
2759 input. Strangely, when Emacs is run as a GUI application from the
2760 command line, it blocks in the same situation. This `wrapper' of
2761 the system call SELECT corrects this discrepancy. */
2763 sys_select (n
, rfds
, wfds
, efds
, timeout
)
2768 struct timeval
*timeout
;
2770 if (!inhibit_window_system
&& rfds
&& FD_ISSET (0, rfds
))
2772 else if (inhibit_window_system
|| noninteractive
||
2773 (timeout
&& (EMACS_SECS(*timeout
)==0) &&
2774 (EMACS_USECS(*timeout
)==0)))
2775 return select(n
, rfds
, wfds
, efds
, timeout
);
2778 EMACS_TIME end_time
, now
;
2780 EMACS_GET_TIME (end_time
);
2782 EMACS_ADD_TIME (end_time
, end_time
, *timeout
);
2787 EMACS_TIME one_second
;
2796 EMACS_SET_SECS (one_second
, 1);
2797 EMACS_SET_USECS (one_second
, 0);
2799 if (timeout
&& EMACS_TIME_LT(*timeout
, one_second
))
2800 one_second
= *timeout
;
2802 if ((r
= select (n
, &orfds
, wfds
, efds
, &one_second
)) > 0)
2808 mac_check_for_quit_char();
2810 EMACS_GET_TIME (now
);
2811 EMACS_SUB_TIME (now
, end_time
, now
);
2813 while (!timeout
|| !EMACS_TIME_NEG_P (now
));
2820 int sys_read (fds
, buf
, nbyte
)
2826 EMACS_TIME one_second
;
2829 /* Use select to block on IO while still checking for quit_char */
2830 if (!inhibit_window_system
&& !noninteractive
&&
2831 ! (fcntl(fds
, F_GETFL
, 0) & O_NONBLOCK
))
2834 FD_SET (fds
, &rfds
);
2835 if (sys_select (fds
+1, &rfds
, 0, 0, NULL
) < 0)
2839 return read (fds
, buf
, nbyte
);
2843 /* Set up environment variables so that Emacs can correctly find its
2844 support files when packaged as an application bundle. Directories
2845 placed in /usr/local/share/emacs/<emacs-version>/, /usr/local/bin,
2846 and /usr/local/libexec/emacs/<emacs-version>/<system-configuration>
2847 by `make install' by default can instead be placed in
2848 .../Emacs.app/Contents/Resources/ and
2849 .../Emacs.app/Contents/MacOS/. Each of these environment variables
2850 is changed only if it is not already set. Presumably if the user
2851 sets an environment variable, he will want to use files in his path
2852 instead of ones in the application bundle. */
2854 init_mac_osx_environment ()
2858 CFStringRef cf_app_bundle_pathname
;
2859 int app_bundle_pathname_len
;
2860 char *app_bundle_pathname
;
2864 /* Fetch the pathname of the application bundle as a C string into
2865 app_bundle_pathname. */
2867 bundle
= CFBundleGetMainBundle ();
2871 bundleURL
= CFBundleCopyBundleURL (bundle
);
2875 cf_app_bundle_pathname
= CFURLCopyFileSystemPath (bundleURL
,
2876 kCFURLPOSIXPathStyle
);
2877 app_bundle_pathname_len
= CFStringGetLength (cf_app_bundle_pathname
);
2878 app_bundle_pathname
= (char *) alloca (app_bundle_pathname_len
+ 1);
2880 if (!CFStringGetCString (cf_app_bundle_pathname
,
2881 app_bundle_pathname
,
2882 app_bundle_pathname_len
+ 1,
2883 kCFStringEncodingISOLatin1
))
2885 CFRelease (cf_app_bundle_pathname
);
2889 CFRelease (cf_app_bundle_pathname
);
2891 /* P should have sufficient room for the pathname of the bundle plus
2892 the subpath in it leading to the respective directories. Q
2893 should have three times that much room because EMACSLOADPATH can
2894 have the value "<path to lisp dir>:<path to leim dir>:<path to
2896 p
= (char *) alloca (app_bundle_pathname_len
+ 50);
2897 q
= (char *) alloca (3 * app_bundle_pathname_len
+ 150);
2898 if (!getenv ("EMACSLOADPATH"))
2902 strcpy (p
, app_bundle_pathname
);
2903 strcat (p
, "/Contents/Resources/lisp");
2904 if (stat (p
, &st
) == 0 && (st
.st_mode
& S_IFMT
) == S_IFDIR
)
2907 strcpy (p
, app_bundle_pathname
);
2908 strcat (p
, "/Contents/Resources/leim");
2909 if (stat (p
, &st
) == 0 && (st
.st_mode
& S_IFMT
) == S_IFDIR
)
2916 strcpy (p
, app_bundle_pathname
);
2917 strcat (p
, "/Contents/Resources/site-lisp");
2918 if (stat (p
, &st
) == 0 && (st
.st_mode
& S_IFMT
) == S_IFDIR
)
2926 setenv ("EMACSLOADPATH", q
, 1);
2929 if (!getenv ("EMACSPATH"))
2933 strcpy (p
, app_bundle_pathname
);
2934 strcat (p
, "/Contents/MacOS/bin");
2935 if (stat (p
, &st
) == 0 && (st
.st_mode
& S_IFMT
) == S_IFDIR
)
2938 strcpy (p
, app_bundle_pathname
);
2939 strcat (p
, "/Contents/MacOS/libexec");
2940 if (stat (p
, &st
) == 0 && (st
.st_mode
& S_IFMT
) == S_IFDIR
)
2948 setenv ("EMACSPATH", q
, 1);
2951 if (!getenv ("EMACSDATA"))
2953 strcpy (p
, app_bundle_pathname
);
2954 strcat (p
, "/Contents/Resources/etc");
2955 if (stat (p
, &st
) == 0 && (st
.st_mode
& S_IFMT
) == S_IFDIR
)
2956 setenv ("EMACSDATA", p
, 1);
2959 if (!getenv ("EMACSDOC"))
2961 strcpy (p
, app_bundle_pathname
);
2962 strcat (p
, "/Contents/Resources/etc");
2963 if (stat (p
, &st
) == 0 && (st
.st_mode
& S_IFMT
) == S_IFDIR
)
2964 setenv ("EMACSDOC", p
, 1);
2967 if (!getenv ("INFOPATH"))
2969 strcpy (p
, app_bundle_pathname
);
2970 strcat (p
, "/Contents/Resources/info");
2971 if (stat (p
, &st
) == 0 && (st
.st_mode
& S_IFMT
) == S_IFDIR
)
2972 setenv ("INFOPATH", p
, 1);
2975 #endif /* MAC_OSX */
2980 QCLIPBOARD
= intern ("CLIPBOARD");
2981 staticpro (&QCLIPBOARD
);
2983 defsubr (&Smac_paste_function
);
2984 defsubr (&Smac_cut_function
);
2985 defsubr (&Sx_selection_exists_p
);
2987 defsubr (&Sdo_applescript
);
2988 defsubr (&Smac_file_name_to_posix
);
2989 defsubr (&Sposix_file_name_to_mac
);