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). */
30 #include <sys/types.h>
35 #include <sys/param.h>
49 #include <Carbon/Carbon.h>
51 #define mktime emacs_mktime
53 #define free unexec_free
55 #define malloc unexec_malloc
57 #define realloc unexec_realloc
59 #define init_process emacs_init_process
60 #else /* not MAC_OSX */
63 #include <TextUtils.h>
65 #include <Resources.h>
70 #include <AppleScript.h>
72 #endif /* not MAC_OSX */
76 #include "sysselect.h"
78 #include "blockinput.h"
80 Lisp_Object QCLIPBOARD
;
82 /* An instance of the AppleScript component. */
83 static ComponentInstance as_scripting_component
;
84 /* The single script context used for all script executions. */
85 static OSAID as_script_context
;
88 /* When converting from Mac to Unix pathnames, /'s in folder names are
89 converted to :'s. This function, used in copying folder names,
90 performs a strncat and converts all character a to b in the copy of
91 the string s2 appended to the end of s1. */
94 string_cat_and_replace (char *s1
, const char *s2
, int n
, char a
, char b
)
102 for (i
= 0; i
< l2
; i
++)
111 /* Convert a Mac pathname to Posix form. A Mac full pathname is one
112 that does not begin with a ':' and contains at least one ':'. A Mac
113 full pathname causes a '/' to be prepended to the Posix pathname.
114 The algorithm for the rest of the pathname is as follows:
115 For each segment between two ':',
116 if it is non-null, copy as is and then add a '/' at the end,
117 otherwise, insert a "../" into the Posix pathname.
118 Returns 1 if successful; 0 if fails. */
121 mac_to_posix_pathname (const char *mfn
, char *ufn
, int ufnbuflen
)
123 const char *p
, *q
, *pe
;
130 p
= strchr (mfn
, ':');
131 if (p
!= 0 && p
!= mfn
) /* full pathname */
138 pe
= mfn
+ strlen (mfn
);
145 { /* two consecutive ':' */
146 if (strlen (ufn
) + 3 >= ufnbuflen
)
152 if (strlen (ufn
) + (q
- p
) + 1 >= ufnbuflen
)
154 string_cat_and_replace (ufn
, p
, q
- p
, '/', ':');
161 if (strlen (ufn
) + (pe
- p
) >= ufnbuflen
)
163 string_cat_and_replace (ufn
, p
, pe
- p
, '/', ':');
164 /* no separator for last one */
173 extern char *get_temp_dir_name ();
176 /* Convert a Posix pathname to Mac form. Approximately reverse of the
177 above in algorithm. */
180 posix_to_mac_pathname (const char *ufn
, char *mfn
, int mfnbuflen
)
182 const char *p
, *q
, *pe
;
183 char expanded_pathname
[MAXPATHLEN
+1];
192 /* Check for and handle volume names. Last comparison: strangely
193 somewhere "/.emacs" is passed. A temporary fix for now. */
194 if (*p
== '/' && strchr (p
+1, '/') == NULL
&& strcmp (p
, "/.emacs") != 0)
196 if (strlen (p
) + 1 > mfnbuflen
)
203 /* expand to emacs dir found by init_emacs_passwd_dir */
204 if (strncmp (p
, "~emacs/", 7) == 0)
206 struct passwd
*pw
= getpwnam ("emacs");
208 if (strlen (pw
->pw_dir
) + strlen (p
) > MAXPATHLEN
)
210 strcpy (expanded_pathname
, pw
->pw_dir
);
211 strcat (expanded_pathname
, p
);
212 p
= expanded_pathname
;
213 /* now p points to the pathname with emacs dir prefix */
215 else if (strncmp (p
, "/tmp/", 5) == 0)
217 char *t
= get_temp_dir_name ();
219 if (strlen (t
) + strlen (p
) > MAXPATHLEN
)
221 strcpy (expanded_pathname
, t
);
222 strcat (expanded_pathname
, p
);
223 p
= expanded_pathname
;
224 /* now p points to the pathname with emacs dir prefix */
226 else if (*p
!= '/') /* relative pathname */
238 if (q
- p
== 2 && *p
== '.' && *(p
+1) == '.')
240 if (strlen (mfn
) + 1 >= mfnbuflen
)
246 if (strlen (mfn
) + (q
- p
) + 1 >= mfnbuflen
)
248 string_cat_and_replace (mfn
, p
, q
- p
, ':', '/');
255 if (strlen (mfn
) + (pe
- p
) >= mfnbuflen
)
257 string_cat_and_replace (mfn
, p
, pe
- p
, ':', '/');
267 /* The following functions with "sys_" prefix are stubs to Unix
268 functions that have already been implemented by CW or MPW. The
269 calls to them in Emacs source course are #define'd to call the sys_
270 versions by the header files s-mac.h. In these stubs pathnames are
271 converted between their Unix and Mac forms. */
274 /* Unix epoch is Jan 1, 1970 while Mac epoch is Jan 1, 1904: 66 years
275 + 17 leap days. These are for adjusting time values returned by
276 MacOS Toolbox functions. */
278 #define MAC_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
282 /* CW Pro 5 epoch is Jan 1, 1900 (aaarghhhhh!); remember, 1900 is not
283 a leap year! This is for adjusting time_t values returned by MSL
285 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 70 + 17) * 24 * 60 * 60)
286 #else /* __MSL__ >= 0x6000 */
287 /* CW changes Pro 6 to follow Unix! */
288 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
289 #endif /* __MSL__ >= 0x6000 */
291 /* MPW library functions follow Unix (confused?). */
292 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
293 #else /* not __MRC__ */
295 #endif /* not __MRC__ */
298 /* Define our own stat function for both MrC and CW. The reason for
299 doing this: "stat" is both the name of a struct and function name:
300 can't use the same trick like that for sys_open, sys_close, etc. to
301 redirect Emacs's calls to our own version that converts Unix style
302 filenames to Mac style filename because all sorts of compilation
303 errors will be generated if stat is #define'd to be sys_stat. */
306 stat_noalias (const char *path
, struct stat
*buf
)
308 char mac_pathname
[MAXPATHLEN
+1];
311 if (posix_to_mac_pathname (path
, mac_pathname
, MAXPATHLEN
+1) == 0)
314 c2pstr (mac_pathname
);
315 cipb
.hFileInfo
.ioNamePtr
= mac_pathname
;
316 cipb
.hFileInfo
.ioVRefNum
= 0;
317 cipb
.hFileInfo
.ioDirID
= 0;
318 cipb
.hFileInfo
.ioFDirIndex
= 0;
319 /* set to 0 to get information about specific dir or file */
321 errno
= PBGetCatInfo (&cipb
, false);
322 if (errno
== -43) /* -43: fnfErr defined in Errors.h */
327 if (cipb
.hFileInfo
.ioFlAttrib
& 0x10) /* bit 4 = 1 for directories */
329 buf
->st_mode
= S_IFDIR
| S_IREAD
| S_IEXEC
;
331 if (!(cipb
.hFileInfo
.ioFlAttrib
& 0x1))
332 buf
->st_mode
|= S_IWRITE
; /* bit 1 = 1 for locked files/directories */
333 buf
->st_ino
= cipb
.dirInfo
.ioDrDirID
;
334 buf
->st_dev
= cipb
.dirInfo
.ioVRefNum
;
335 buf
->st_size
= cipb
.dirInfo
.ioDrNmFls
;
336 /* size of dir = number of files and dirs */
339 = cipb
.dirInfo
.ioDrMdDat
- MAC_UNIX_EPOCH_DIFF
;
340 buf
->st_ctime
= cipb
.dirInfo
.ioDrCrDat
- MAC_UNIX_EPOCH_DIFF
;
344 buf
->st_mode
= S_IFREG
| S_IREAD
;
345 if (!(cipb
.hFileInfo
.ioFlAttrib
& 0x1))
346 buf
->st_mode
|= S_IWRITE
; /* bit 1 = 1 for locked files/directories */
347 if (cipb
.hFileInfo
.ioFlFndrInfo
.fdType
== 'APPL')
348 buf
->st_mode
|= S_IEXEC
;
349 buf
->st_ino
= cipb
.hFileInfo
.ioDirID
;
350 buf
->st_dev
= cipb
.hFileInfo
.ioVRefNum
;
351 buf
->st_size
= cipb
.hFileInfo
.ioFlLgLen
;
354 = cipb
.hFileInfo
.ioFlMdDat
- MAC_UNIX_EPOCH_DIFF
;
355 buf
->st_ctime
= cipb
.hFileInfo
.ioFlCrDat
- MAC_UNIX_EPOCH_DIFF
;
358 if (cipb
.hFileInfo
.ioFlFndrInfo
.fdFlags
& 0x8000)
360 /* identify alias files as symlinks */
361 buf
->st_mode
&= ~S_IFREG
;
362 buf
->st_mode
|= S_IFLNK
;
366 buf
->st_uid
= getuid ();
367 buf
->st_gid
= getgid ();
375 lstat (const char *path
, struct stat
*buf
)
378 char true_pathname
[MAXPATHLEN
+1];
380 /* Try looking for the file without resolving aliases first. */
381 if ((result
= stat_noalias (path
, buf
)) >= 0)
384 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
387 return stat_noalias (true_pathname
, buf
);
392 stat (const char *path
, struct stat
*sb
)
395 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
398 if ((result
= stat_noalias (path
, sb
)) >= 0 &&
399 ! (sb
->st_mode
& S_IFLNK
))
402 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
405 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
408 fully_resolved_name
[len
] = '\0';
409 /* in fact our readlink terminates strings */
410 return lstat (fully_resolved_name
, sb
);
413 return lstat (true_pathname
, sb
);
418 /* CW defines fstat in stat.mac.c while MPW does not provide this
419 function. Without the information of how to get from a file
420 descriptor in MPW StdCLib to a Mac OS file spec, it should be hard
421 to implement this function. Fortunately, there is only one place
422 where this function is called in our configuration: in fileio.c,
423 where only the st_dev and st_ino fields are used to determine
424 whether two fildes point to different i-nodes to prevent copying
425 a file onto itself equal. What we have here probably needs
429 fstat (int fildes
, struct stat
*buf
)
432 buf
->st_ino
= fildes
;
433 buf
->st_mode
= S_IFREG
; /* added by T.I. for the copy-file */
434 return 0; /* success */
440 mkdir (const char *dirname
, int mode
)
445 char true_pathname
[MAXPATHLEN
+1], mac_pathname
[MAXPATHLEN
+1];
447 if (find_true_pathname (dirname
, true_pathname
, MAXPATHLEN
+1) == -1)
450 if (posix_to_mac_pathname (true_pathname
, mac_pathname
, MAXPATHLEN
+1) == 0)
453 c2pstr (mac_pathname
);
454 hfpb
.ioNamePtr
= mac_pathname
;
455 hfpb
.ioVRefNum
= 0; /* ignored unless name is invalid */
456 hfpb
.ioDirID
= 0; /* parent is the root */
458 errno
= PBDirCreate ((HParmBlkPtr
) &hfpb
, false);
459 /* just return the Mac OSErr code for now */
460 return errno
== noErr
? 0 : -1;
465 sys_rmdir (const char *dirname
)
468 char mac_pathname
[MAXPATHLEN
+1];
470 if (posix_to_mac_pathname (dirname
, mac_pathname
, MAXPATHLEN
+1) == 0)
473 c2pstr (mac_pathname
);
474 hfpb
.ioNamePtr
= mac_pathname
;
475 hfpb
.ioVRefNum
= 0; /* ignored unless name is invalid */
476 hfpb
.ioDirID
= 0; /* parent is the root */
478 errno
= PBHDelete ((HParmBlkPtr
) &hfpb
, false);
479 return errno
== noErr
? 0 : -1;
484 /* No implementation yet. */
486 execvp (const char *path
, ...)
494 utime (const char *path
, const struct utimbuf
*times
)
496 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
498 char mac_pathname
[MAXPATHLEN
+1];
501 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
504 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
506 fully_resolved_name
[len
] = '\0';
508 strcpy (fully_resolved_name
, true_pathname
);
510 if (!posix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
513 c2pstr (mac_pathname
);
514 cipb
.hFileInfo
.ioNamePtr
= mac_pathname
;
515 cipb
.hFileInfo
.ioVRefNum
= 0;
516 cipb
.hFileInfo
.ioDirID
= 0;
517 cipb
.hFileInfo
.ioFDirIndex
= 0;
518 /* set to 0 to get information about specific dir or file */
520 errno
= PBGetCatInfo (&cipb
, false);
524 if (cipb
.hFileInfo
.ioFlAttrib
& 0x10) /* bit 4 = 1 for directories */
527 cipb
.dirInfo
.ioDrMdDat
= times
->modtime
+ MAC_UNIX_EPOCH_DIFF
;
529 GetDateTime (&cipb
.dirInfo
.ioDrMdDat
);
534 cipb
.hFileInfo
.ioFlMdDat
= times
->modtime
+ MAC_UNIX_EPOCH_DIFF
;
536 GetDateTime (&cipb
.hFileInfo
.ioFlMdDat
);
539 errno
= PBSetCatInfo (&cipb
, false);
540 return errno
== noErr
? 0 : -1;
554 /* Like stat, but test for access mode in hfpb.ioFlAttrib */
556 access (const char *path
, int mode
)
558 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
560 char mac_pathname
[MAXPATHLEN
+1];
563 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
566 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
568 fully_resolved_name
[len
] = '\0';
570 strcpy (fully_resolved_name
, true_pathname
);
572 if (!posix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
575 c2pstr (mac_pathname
);
576 cipb
.hFileInfo
.ioNamePtr
= mac_pathname
;
577 cipb
.hFileInfo
.ioVRefNum
= 0;
578 cipb
.hFileInfo
.ioDirID
= 0;
579 cipb
.hFileInfo
.ioFDirIndex
= 0;
580 /* set to 0 to get information about specific dir or file */
582 errno
= PBGetCatInfo (&cipb
, false);
586 if (mode
== F_OK
) /* got this far, file exists */
590 if (cipb
.hFileInfo
.ioFlAttrib
& 0x10) /* path refers to a directory */
594 if (cipb
.hFileInfo
.ioFlFndrInfo
.fdType
== 'APPL')
601 return (cipb
.hFileInfo
.ioFlAttrib
& 0x1) ? -1 : 0;
602 /* don't allow if lock bit is on */
608 #define DEV_NULL_FD 0x10000
612 sys_open (const char *path
, int oflag
)
614 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
616 char mac_pathname
[MAXPATHLEN
+1];
618 if (strcmp (path
, "/dev/null") == 0)
619 return DEV_NULL_FD
; /* some bogus fd to be ignored in write */
621 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
624 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
626 fully_resolved_name
[len
] = '\0';
628 strcpy (fully_resolved_name
, true_pathname
);
630 if (!posix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
635 int res
= open (mac_pathname
, oflag
);
636 /* if (oflag == O_WRONLY || oflag == O_RDWR) */
638 fsetfileinfo (mac_pathname
, 'EMAx', 'TEXT');
640 #else /* not __MRC__ */
641 return open (mac_pathname
, oflag
);
642 #endif /* not __MRC__ */
649 sys_creat (const char *path
, mode_t mode
)
651 char true_pathname
[MAXPATHLEN
+1];
653 char mac_pathname
[MAXPATHLEN
+1];
655 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
658 if (!posix_to_mac_pathname (true_pathname
, mac_pathname
, MAXPATHLEN
+1))
663 int result
= creat (mac_pathname
);
664 fsetfileinfo (mac_pathname
, 'EMAx', 'TEXT');
666 #else /* not __MRC__ */
667 return creat (mac_pathname
, mode
);
668 #endif /* not __MRC__ */
675 sys_unlink (const char *path
)
677 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
679 char mac_pathname
[MAXPATHLEN
+1];
681 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
684 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
686 fully_resolved_name
[len
] = '\0';
688 strcpy (fully_resolved_name
, true_pathname
);
690 if (!posix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
693 return unlink (mac_pathname
);
699 sys_read (int fildes
, char *buf
, int count
)
701 if (fildes
== 0) /* this should not be used for console input */
704 #if __MSL__ >= 0x6000
705 return _read (fildes
, buf
, count
);
707 return read (fildes
, buf
, count
);
714 sys_write (int fildes
, const char *buf
, int count
)
716 if (fildes
== DEV_NULL_FD
)
719 #if __MSL__ >= 0x6000
720 return _write (fildes
, buf
, count
);
722 return write (fildes
, buf
, count
);
729 sys_rename (const char * old_name
, const char * new_name
)
731 char true_old_pathname
[MAXPATHLEN
+1], true_new_pathname
[MAXPATHLEN
+1];
732 char fully_resolved_old_name
[MAXPATHLEN
+1];
734 char mac_old_name
[MAXPATHLEN
+1], mac_new_name
[MAXPATHLEN
+1];
736 if (find_true_pathname (old_name
, true_old_pathname
, MAXPATHLEN
+1) == -1)
739 len
= readlink (true_old_pathname
, fully_resolved_old_name
, MAXPATHLEN
);
741 fully_resolved_old_name
[len
] = '\0';
743 strcpy (fully_resolved_old_name
, true_old_pathname
);
745 if (find_true_pathname (new_name
, true_new_pathname
, MAXPATHLEN
+1) == -1)
748 if (strcmp (fully_resolved_old_name
, true_new_pathname
) == 0)
751 if (!posix_to_mac_pathname (fully_resolved_old_name
,
756 if (!posix_to_mac_pathname(true_new_pathname
, mac_new_name
, MAXPATHLEN
+1))
759 /* If a file with new_name already exists, rename deletes the old
760 file in Unix. CW version fails in these situation. So we add a
761 call to unlink here. */
762 (void) unlink (mac_new_name
);
764 return rename (mac_old_name
, mac_new_name
);
769 extern FILE *fopen (const char *name
, const char *mode
);
771 sys_fopen (const char *name
, const char *mode
)
773 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
775 char mac_pathname
[MAXPATHLEN
+1];
777 if (find_true_pathname (name
, true_pathname
, MAXPATHLEN
+1) == -1)
780 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
782 fully_resolved_name
[len
] = '\0';
784 strcpy (fully_resolved_name
, true_pathname
);
786 if (!posix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
791 if (mode
[0] == 'w' || mode
[0] == 'a')
792 fsetfileinfo (mac_pathname
, 'EMAx', 'TEXT');
793 #endif /* not __MRC__ */
794 return fopen (mac_pathname
, mode
);
801 long target_ticks
= 0;
804 __sigfun alarm_signal_func
= (__sigfun
) 0;
806 __signal_func_ptr alarm_signal_func
= (__signal_func_ptr
) 0;
807 #else /* not __MRC__ and not __MWERKS__ */
809 #endif /* not __MRC__ and not __MWERKS__ */
812 /* These functions simulate SIG_ALRM. The stub for function signal
813 stores the signal handler function in alarm_signal_func if a
814 SIG_ALRM is encountered. check_alarm is called in XTread_socket,
815 which emacs calls periodically. A pending alarm is represented by
816 a non-zero target_ticks value. check_alarm calls the handler
817 function pointed to by alarm_signal_func if one has been set up and
818 an alarm is pending. */
823 if (target_ticks
&& TickCount () > target_ticks
)
826 if (alarm_signal_func
)
827 (*alarm_signal_func
)(SIGALRM
);
833 select (n
, rfds
, wfds
, efds
, timeout
)
838 struct timeval
*timeout
;
840 #ifdef TARGET_API_MAC_CARBON
842 #else /* not TARGET_API_MAC_CARBON */
843 EMACS_TIME end_time
, now
;
846 /* Can only handle wait for keyboard input. */
847 if (n
> 1 || wfds
|| efds
)
850 EMACS_GET_TIME (end_time
);
851 EMACS_ADD_TIME (end_time
, end_time
, *timeout
);
855 /* Also return true if an event other than a keyDown has
856 occurred. This causes kbd_buffer_get_event in keyboard.c to
857 call read_avail_input which in turn calls XTread_socket to
858 poll for these events. Otherwise these never get processed
859 except but a very slow poll timer. */
860 if (FD_ISSET (0, rfds
) && EventAvail (everyEvent
, &e
))
863 /* Also check movement of the mouse. */
866 static Point old_mouse_pos
= {-1, -1};
868 GetMouse (&mouse_pos
);
869 if (!EqualPt (mouse_pos
, old_mouse_pos
))
871 old_mouse_pos
= mouse_pos
;
876 WaitNextEvent (0, &e
, 1UL, NULL
); /* Accept no event; wait 1
879 EMACS_GET_TIME (now
);
880 EMACS_SUB_TIME (now
, end_time
, now
);
882 while (!EMACS_TIME_NEG_P (now
));
885 #endif /* not TARGET_API_MAC_CARBON */
889 /* Called in sys_select to wait for an alarm signal to arrive. */
897 if (!target_ticks
) /* no alarm pending */
900 if ((tick
= TickCount ()) < target_ticks
)
901 WaitNextEvent (0, &e
, target_ticks
- tick
, NULL
); /* Accept no event;
902 just wait. by T.I. */
905 if (alarm_signal_func
)
906 (*alarm_signal_func
)(SIGALRM
);
915 long remaining
= target_ticks
? (TickCount () - target_ticks
) / 60 : 0;
917 target_ticks
= seconds
? TickCount () + 60 * seconds
: 0;
919 return (remaining
< 0) ? 0 : (unsigned int) remaining
;
925 extern __sigfun
signal (int signal
, __sigfun signal_func
);
927 sys_signal (int signal_num
, __sigfun signal_func
)
929 extern __signal_func_ptr
signal (int signal
, __signal_func_ptr signal_func
);
931 sys_signal (int signal_num
, __signal_func_ptr signal_func
)
932 #else /* not __MRC__ and not __MWERKS__ */
934 #endif /* not __MRC__ and not __MWERKS__ */
936 if (signal_num
!= SIGALRM
)
937 return signal (signal_num
, signal_func
);
941 __sigfun old_signal_func
;
943 __signal_func_ptr old_signal_func
;
947 old_signal_func
= alarm_signal_func
;
948 alarm_signal_func
= signal_func
;
949 return old_signal_func
;
954 /* gettimeofday should return the amount of time (in a timeval
955 structure) since midnight today. The toolbox function Microseconds
956 returns the number of microseconds (in a UnsignedWide value) since
957 the machine was booted. Also making this complicated is WideAdd,
958 WideSubtract, etc. take wide values. */
965 static wide wall_clock_at_epoch
, clicks_at_epoch
;
966 UnsignedWide uw_microseconds
;
968 time_t sys_time (time_t *);
970 /* If this function is called for the first time, record the number
971 of seconds since midnight and the number of microseconds since
972 boot at the time of this first call. */
977 systime
= sys_time (NULL
);
978 /* Store microseconds since midnight in wall_clock_at_epoch. */
979 WideMultiply (systime
, 1000000L, &wall_clock_at_epoch
);
980 Microseconds (&uw_microseconds
);
981 /* Store microseconds since boot in clicks_at_epoch. */
982 clicks_at_epoch
.hi
= uw_microseconds
.hi
;
983 clicks_at_epoch
.lo
= uw_microseconds
.lo
;
986 /* Get time since boot */
987 Microseconds (&uw_microseconds
);
989 /* Convert to time since midnight*/
990 w_microseconds
.hi
= uw_microseconds
.hi
;
991 w_microseconds
.lo
= uw_microseconds
.lo
;
992 WideSubtract (&w_microseconds
, &clicks_at_epoch
);
993 WideAdd (&w_microseconds
, &wall_clock_at_epoch
);
994 tp
->tv_sec
= WideDivide (&w_microseconds
, 1000000L, &tp
->tv_usec
);
1002 sleep (unsigned int seconds
)
1004 unsigned long time_up
;
1007 time_up
= TickCount () + seconds
* 60;
1008 while (TickCount () < time_up
)
1010 /* Accept no event; just wait. by T.I. */
1011 WaitNextEvent (0, &e
, 30, NULL
);
1016 #endif /* __MRC__ */
1019 /* The time functions adjust time values according to the difference
1020 between the Unix and CW epoches. */
1023 extern struct tm
*gmtime (const time_t *);
1025 sys_gmtime (const time_t *timer
)
1027 time_t unix_time
= *timer
+ CW_OR_MPW_UNIX_EPOCH_DIFF
;
1029 return gmtime (&unix_time
);
1034 extern struct tm
*localtime (const time_t *);
1036 sys_localtime (const time_t *timer
)
1038 #if __MSL__ >= 0x6000
1039 time_t unix_time
= *timer
;
1041 time_t unix_time
= *timer
+ CW_OR_MPW_UNIX_EPOCH_DIFF
;
1044 return localtime (&unix_time
);
1049 extern char *ctime (const time_t *);
1051 sys_ctime (const time_t *timer
)
1053 #if __MSL__ >= 0x6000
1054 time_t unix_time
= *timer
;
1056 time_t unix_time
= *timer
+ CW_OR_MPW_UNIX_EPOCH_DIFF
;
1059 return ctime (&unix_time
);
1064 extern time_t time (time_t *);
1066 sys_time (time_t *timer
)
1068 #if __MSL__ >= 0x6000
1069 time_t mac_time
= time (NULL
);
1071 time_t mac_time
= time (NULL
) - CW_OR_MPW_UNIX_EPOCH_DIFF
;
1081 /* MPW strftime broken for "%p" format */
1086 sys_strftime (char * s
, size_t maxsize
, const char * format
,
1087 const struct tm
* timeptr
)
1089 if (strcmp (format
, "%p") == 0)
1093 if (timeptr
->tm_hour
< 12)
1105 return strftime (s
, maxsize
, format
, timeptr
);
1107 #endif /* __MRC__ */
1110 /* no subprocesses, empty wait */
1120 croak (char *badfunc
)
1122 printf ("%s not yet implemented\r\n", badfunc
);
1128 index (const char * str
, int chr
)
1130 return strchr (str
, chr
);
1135 mktemp (char *template)
1140 len
= strlen (template);
1142 while (k
>= 0 && template[k
] == 'X')
1145 k
++; /* make k index of first 'X' */
1149 /* Zero filled, number of digits equal to the number of X's. */
1150 sprintf (&template[k
], "%0*d", len
-k
, seqnum
++);
1159 /* Emulate getpwuid, getpwnam and others. */
1161 #define PASSWD_FIELD_SIZE 256
1163 static char my_passwd_name
[PASSWD_FIELD_SIZE
];
1164 static char my_passwd_dir
[MAXPATHLEN
+1];
1166 static struct passwd my_passwd
=
1172 static struct group my_group
=
1174 /* There are no groups on the mac, so we just return "root" as the
1180 /* Initialized by main () in macterm.c to pathname of emacs directory. */
1182 char emacs_passwd_dir
[MAXPATHLEN
+1];
1188 init_emacs_passwd_dir ()
1192 if (getwd (emacs_passwd_dir
) && getwd (my_passwd_dir
))
1194 /* Need pathname of first ancestor that begins with "emacs"
1195 since Mac emacs application is somewhere in the emacs-*
1197 int len
= strlen (emacs_passwd_dir
);
1199 /* j points to the "/" following the directory name being
1202 while (i
>= 0 && !found
)
1204 while (i
>= 0 && emacs_passwd_dir
[i
] != '/')
1206 if (emacs_passwd_dir
[i
] == '/' && i
+5 < len
)
1207 found
= (strncmp (&(emacs_passwd_dir
[i
+1]), "emacs", 5) == 0);
1209 emacs_passwd_dir
[j
+1] = '\0';
1220 /* Setting to "/" probably won't work but set it to something
1222 strcpy (emacs_passwd_dir
, "/");
1223 strcpy (my_passwd_dir
, "/");
1228 static struct passwd emacs_passwd
=
1234 static int my_passwd_inited
= 0;
1242 /* Note: my_passwd_dir initialized in int_emacs_passwd_dir to
1243 directory where Emacs was started. */
1245 owner_name
= (char **) GetResource ('STR ',-16096);
1249 BlockMove ((unsigned char *) *owner_name
,
1250 (unsigned char *) my_passwd_name
,
1252 HUnlock (owner_name
);
1253 p2cstr ((unsigned char *) my_passwd_name
);
1256 my_passwd_name
[0] = 0;
1261 getpwuid (uid_t uid
)
1263 if (!my_passwd_inited
)
1266 my_passwd_inited
= 1;
1274 getgrgid (gid_t gid
)
1281 getpwnam (const char *name
)
1283 if (strcmp (name
, "emacs") == 0)
1284 return &emacs_passwd
;
1286 if (!my_passwd_inited
)
1289 my_passwd_inited
= 1;
1296 /* The functions fork, kill, sigsetmask, sigblock, request_sigio,
1297 setpgrp, setpriority, and unrequest_sigio are defined to be empty
1318 error ("Can't spawn subshell");
1337 request_sigio (void)
1343 unrequest_sigio (void)
1358 pipe (int _fildes
[2])
1365 /* Hard and symbolic links. */
1368 symlink (const char *name1
, const char *name2
)
1376 link (const char *name1
, const char *name2
)
1382 #endif /* ! MAC_OSX */
1384 /* Determine the path name of the file specified by VREFNUM, DIRID,
1385 and NAME and place that in the buffer PATH of length
1388 path_from_vol_dir_name (char *path
, int man_path_len
, short vol_ref_num
,
1389 long dir_id
, ConstStr255Param name
)
1395 if (strlen (name
) > man_path_len
)
1398 memcpy (dir_name
, name
, name
[0]+1);
1399 memcpy (path
, name
, name
[0]+1);
1402 cipb
.dirInfo
.ioDrParID
= dir_id
;
1403 cipb
.dirInfo
.ioNamePtr
= dir_name
;
1407 cipb
.dirInfo
.ioVRefNum
= vol_ref_num
;
1408 cipb
.dirInfo
.ioFDirIndex
= -1;
1409 cipb
.dirInfo
.ioDrDirID
= cipb
.dirInfo
.ioDrParID
;
1410 /* go up to parent each time */
1412 err
= PBGetCatInfo (&cipb
, false);
1417 if (strlen (dir_name
) + strlen (path
) + 1 >= man_path_len
)
1420 strcat (dir_name
, ":");
1421 strcat (dir_name
, path
);
1422 /* attach to front since we're going up directory tree */
1423 strcpy (path
, dir_name
);
1425 while (cipb
.dirInfo
.ioDrDirID
!= fsRtDirID
);
1426 /* stop when we see the volume's root directory */
1428 return 1; /* success */
1434 readlink (const char *path
, char *buf
, int bufsiz
)
1436 char mac_sym_link_name
[MAXPATHLEN
+1];
1439 Boolean target_is_folder
, was_aliased
;
1440 Str255 directory_name
, mac_pathname
;
1443 if (posix_to_mac_pathname (path
, mac_sym_link_name
, MAXPATHLEN
+1) == 0)
1446 c2pstr (mac_sym_link_name
);
1447 err
= FSMakeFSSpec (0, 0, mac_sym_link_name
, &fsspec
);
1454 err
= ResolveAliasFile (&fsspec
, true, &target_is_folder
, &was_aliased
);
1455 if (err
!= noErr
|| !was_aliased
)
1461 if (path_from_vol_dir_name (mac_pathname
, 255, fsspec
.vRefNum
, fsspec
.parID
,
1468 if (mac_to_posix_pathname (mac_pathname
, buf
, bufsiz
) == 0)
1474 return strlen (buf
);
1478 /* Convert a path to one with aliases fully expanded. */
1481 find_true_pathname (const char *path
, char *buf
, int bufsiz
)
1483 char *q
, temp
[MAXPATHLEN
+1];
1487 if (bufsiz
<= 0 || path
== 0 || path
[0] == '\0')
1494 q
= strchr (p
+ 1, '/');
1496 q
= strchr (p
, '/');
1497 len
= 0; /* loop may not be entered, e.g., for "/" */
1502 strncat (temp
, p
, q
- p
);
1503 len
= readlink (temp
, buf
, bufsiz
);
1506 if (strlen (temp
) + 1 > bufsiz
)
1516 if (len
+ strlen (p
) + 1 >= bufsiz
)
1520 return len
+ strlen (p
);
1525 umask (mode_t numask
)
1527 static mode_t mask
= 022;
1528 mode_t oldmask
= mask
;
1535 chmod (const char *path
, mode_t mode
)
1537 /* say it always succeed for now */
1546 return fcntl (oldd
, F_DUPFD
, 0);
1548 /* current implementation of fcntl in fcntl.mac.c simply returns old
1550 return fcntl (oldd
, F_DUPFD
);
1557 /* This is from the original sysdep.c. Emulate BSD dup2. First close
1558 newd if it already exists. Then, attempt to dup oldd. If not
1559 successful, call dup2 recursively until we are, then close the
1560 unsuccessful ones. */
1563 dup2 (int oldd
, int newd
)
1574 ret
= dup2 (oldd
, newd
);
1580 /* let it fail for now */
1597 ioctl (int d
, int request
, void *argp
)
1607 if (fildes
>=0 && fildes
<= 2)
1640 #endif /* __MRC__ */
1644 #if __MSL__ < 0x6000
1652 #endif /* __MWERKS__ */
1654 #endif /* ! MAC_OSX */
1657 /* Return the path to the directory in which Emacs can create
1658 temporary files. The MacOS "temporary items" directory cannot be
1659 used because it removes the file written by a process when it
1660 exits. In that sense it's more like "/dev/null" than "/tmp" (but
1661 again not exactly). And of course Emacs needs to read back the
1662 files written by its subprocesses. So here we write the files to a
1663 directory "Emacs" in the Preferences Folder. This directory is
1664 created if it does not exist. */
1667 get_temp_dir_name ()
1669 static char *temp_dir_name
= NULL
;
1673 Str255 dir_name
, full_path
;
1675 char unix_dir_name
[MAXPATHLEN
+1];
1678 /* Cache directory name with pointer temp_dir_name.
1679 Look for it only the first time. */
1682 err
= FindFolder (kOnSystemDisk
, kPreferencesFolderType
, kCreateFolder
,
1683 &vol_ref_num
, &dir_id
);
1687 if (!path_from_vol_dir_name (full_path
, 255, vol_ref_num
, dir_id
, "\p"))
1690 if (strlen (full_path
) + 6 <= MAXPATHLEN
)
1691 strcat (full_path
, "Emacs:");
1695 if (!mac_to_posix_pathname (full_path
, unix_dir_name
, MAXPATHLEN
+1))
1698 dir
= opendir (unix_dir_name
); /* check whether temp directory exists */
1701 else if (mkdir (unix_dir_name
, 0700) != 0) /* create it if not */
1704 temp_dir_name
= (char *) malloc (strlen (unix_dir_name
) + 1);
1705 strcpy (temp_dir_name
, unix_dir_name
);
1708 return temp_dir_name
;
1713 /* Allocate and construct an array of pointers to strings from a list
1714 of strings stored in a 'STR#' resource. The returned pointer array
1715 is stored in the style of argv and environ: if the 'STR#' resource
1716 contains numString strings, a pointer array with numString+1
1717 elements is returned in which the last entry contains a null
1718 pointer. The pointer to the pointer array is passed by pointer in
1719 parameter t. The resource ID of the 'STR#' resource is passed in
1720 parameter StringListID.
1724 get_string_list (char ***t
, short string_list_id
)
1730 h
= GetResource ('STR#', string_list_id
);
1735 num_strings
= * (short *) p
;
1737 *t
= (char **) malloc (sizeof (char *) * (num_strings
+ 1));
1738 for (i
= 0; i
< num_strings
; i
++)
1740 short length
= *p
++;
1741 (*t
)[i
] = (char *) malloc (length
+ 1);
1742 strncpy ((*t
)[i
], p
, length
);
1743 (*t
)[i
][length
] = '\0';
1746 (*t
)[num_strings
] = 0;
1751 /* Return no string in case GetResource fails. Bug fixed by
1752 Ikegami Tsutomu. Caused MPW build to crash without sym -on
1753 option (no sym -on implies -opt local). */
1754 *t
= (char **) malloc (sizeof (char *));
1761 get_path_to_system_folder ()
1766 Str255 dir_name
, full_path
;
1768 static char system_folder_unix_name
[MAXPATHLEN
+1];
1771 err
= FindFolder (kOnSystemDisk
, kSystemFolderType
, kDontCreateFolder
,
1772 &vol_ref_num
, &dir_id
);
1776 if (!path_from_vol_dir_name (full_path
, 255, vol_ref_num
, dir_id
, "\p"))
1779 if (!mac_to_posix_pathname (full_path
, system_folder_unix_name
,
1783 return system_folder_unix_name
;
1789 #define ENVIRON_STRING_LIST_ID 128
1791 /* Get environment variable definitions from STR# resource. */
1798 get_string_list (&environ
, ENVIRON_STRING_LIST_ID
);
1804 /* Make HOME directory the one Emacs starts up in if not specified
1806 if (getenv ("HOME") == NULL
)
1808 environ
= (char **) realloc (environ
, sizeof (char *) * (i
+ 2));
1811 environ
[i
] = (char *) malloc (strlen (my_passwd_dir
) + 6);
1814 strcpy (environ
[i
], "HOME=");
1815 strcat (environ
[i
], my_passwd_dir
);
1822 /* Make HOME directory the one Emacs starts up in if not specified
1824 if (getenv ("MAIL") == NULL
)
1826 environ
= (char **) realloc (environ
, sizeof (char *) * (i
+ 2));
1829 char * path_to_system_folder
= get_path_to_system_folder ();
1830 environ
[i
] = (char *) malloc (strlen (path_to_system_folder
) + 22);
1833 strcpy (environ
[i
], "MAIL=");
1834 strcat (environ
[i
], path_to_system_folder
);
1835 strcat (environ
[i
], "Eudora Folder/In");
1843 /* Return the value of the environment variable NAME. */
1846 getenv (const char *name
)
1848 int length
= strlen(name
);
1851 for (e
= environ
; *e
!= 0; e
++)
1852 if (strncmp(*e
, name
, length
) == 0 && (*e
)[length
] == '=')
1853 return &(*e
)[length
+ 1];
1855 if (strcmp (name
, "TMPDIR") == 0)
1856 return get_temp_dir_name ();
1863 /* see Interfaces&Libraries:Interfaces:CIncludes:signal.h */
1864 char *sys_siglist
[] =
1866 "Zero is not a signal!!!",
1868 "Interactive user interrupt", /* 2 */ "?",
1869 "Floating point exception", /* 4 */ "?", "?", "?",
1870 "Illegal instruction", /* 8 */ "?", "?", "?", "?", "?", "?", "?",
1871 "Segment violation", /* 16 */ "?", "?", "?", "?", "?", "?", "?",
1872 "?", "?", "?", "?", "?", "?", "?", "?",
1876 char *sys_siglist
[] =
1878 "Zero is not a signal!!!",
1880 "Floating point exception",
1881 "Illegal instruction",
1882 "Interactive user interrupt",
1883 "Segment violation",
1886 #else /* not __MRC__ and not __MWERKS__ */
1888 #endif /* not __MRC__ and not __MWERKS__ */
1891 #include <utsname.h>
1894 uname (struct utsname
*name
)
1897 system_name
= GetString (-16413); /* IM - Resource Manager Reference */
1900 BlockMove (*system_name
, name
->nodename
, (*system_name
)[0]+1);
1901 p2cstr (name
->nodename
);
1909 #include <Processes.h>
1912 /* Event class of HLE sent to subprocess. */
1913 const OSType kEmacsSubprocessSend
= 'ESND';
1915 /* Event class of HLE sent back from subprocess. */
1916 const OSType kEmacsSubprocessReply
= 'ERPY';
1920 mystrchr (char *s
, char c
)
1922 while (*s
&& *s
!= c
)
1950 mystrcpy (char *to
, char *from
)
1962 /* Start a Mac subprocess. Arguments for it is passed in argv (null
1963 terminated). The process should run with the default directory
1964 "workdir", read input from "infn", and write output and error to
1965 "outfn" and "errfn", resp. The Process Manager call
1966 LaunchApplication is used to start the subprocess. We use high
1967 level events as the mechanism to pass arguments to the subprocess
1968 and to make Emacs wait for the subprocess to terminate and pass
1969 back a result code. The bulk of the code here packs the arguments
1970 into one message to be passed together with the high level event.
1971 Emacs also sometimes starts a subprocess using a shell to perform
1972 wildcard filename expansion. Since we don't really have a shell on
1973 the Mac, this case is detected and the starting of the shell is
1974 by-passed. We really need to add code here to do filename
1975 expansion to support such functionality. */
1978 run_mac_command (argv
, workdir
, infn
, outfn
, errfn
)
1979 unsigned char **argv
;
1980 const char *workdir
;
1981 const char *infn
, *outfn
, *errfn
;
1983 #ifdef TARGET_API_MAC_CARBON
1985 #else /* not TARGET_API_MAC_CARBON */
1986 char macappname
[MAXPATHLEN
+1], macworkdir
[MAXPATHLEN
+1];
1987 char macinfn
[MAXPATHLEN
+1], macoutfn
[MAXPATHLEN
+1], macerrfn
[MAXPATHLEN
+1];
1988 int paramlen
, argc
, newargc
, j
, retries
;
1989 char **newargv
, *param
, *p
;
1992 LaunchParamBlockRec lpbr
;
1993 EventRecord send_event
, reply_event
;
1994 RgnHandle cursor_region_handle
;
1996 unsigned long ref_con
, len
;
1998 if (posix_to_mac_pathname (workdir
, macworkdir
, MAXPATHLEN
+1) == 0)
2000 if (posix_to_mac_pathname (infn
, macinfn
, MAXPATHLEN
+1) == 0)
2002 if (posix_to_mac_pathname (outfn
, macoutfn
, MAXPATHLEN
+1) == 0)
2004 if (posix_to_mac_pathname (errfn
, macerrfn
, MAXPATHLEN
+1) == 0)
2007 paramlen
= strlen (macworkdir
) + strlen (macinfn
) + strlen (macoutfn
)
2008 + strlen (macerrfn
) + 4; /* count nulls at end of strings */
2017 /* If a subprocess is invoked with a shell, we receive 3 arguments
2018 of the form: "<path to emacs bins>/sh" "-c" "<path to emacs
2019 bins>/<command> <command args>" */
2020 j
= strlen (argv
[0]);
2021 if (j
>= 3 && strcmp (argv
[0]+j
-3, "/sh") == 0
2022 && argc
== 3 && strcmp (argv
[1], "-c") == 0)
2024 char *command
, *t
, tempmacpathname
[MAXPATHLEN
+1];
2026 /* The arguments for the command in argv[2] are separated by
2027 spaces. Count them and put the count in newargc. */
2028 command
= (char *) alloca (strlen (argv
[2])+2);
2029 strcpy (command
, argv
[2]);
2030 if (command
[strlen (command
) - 1] != ' ')
2031 strcat (command
, " ");
2035 t
= mystrchr (t
, ' ');
2039 t
= mystrchr (t
+1, ' ');
2042 newargv
= (char **) alloca (sizeof (char *) * newargc
);
2045 for (j
= 0; j
< newargc
; j
++)
2047 newargv
[j
] = (char *) alloca (strlen (t
) + 1);
2048 mystrcpy (newargv
[j
], t
);
2051 paramlen
+= strlen (newargv
[j
]) + 1;
2054 if (strncmp (newargv
[0], "~emacs/", 7) == 0)
2056 if (posix_to_mac_pathname (newargv
[0], tempmacpathname
, MAXPATHLEN
+1)
2061 { /* sometimes Emacs call "sh" without a path for the command */
2063 char *t
= (char *) alloca (strlen (newargv
[0]) + 7 + 1);
2064 strcpy (t
, "~emacs/");
2065 strcat (t
, newargv
[0]);
2068 openp (Vexec_path
, build_string (newargv
[0]), EXEC_SUFFIXES
, &path
,
2069 make_number (X_OK
));
2073 if (posix_to_mac_pathname (SDATA (path
), tempmacpathname
,
2077 strcpy (macappname
, tempmacpathname
);
2081 if (posix_to_mac_pathname (argv
[0], macappname
, MAXPATHLEN
+1) == 0)
2084 newargv
= (char **) alloca (sizeof (char *) * argc
);
2086 for (j
= 1; j
< argc
; j
++)
2088 if (strncmp (argv
[j
], "~emacs/", 7) == 0)
2090 char *t
= strchr (argv
[j
], ' ');
2093 char tempcmdname
[MAXPATHLEN
+1], tempmaccmdname
[MAXPATHLEN
+1];
2094 strncpy (tempcmdname
, argv
[j
], t
-argv
[j
]);
2095 tempcmdname
[t
-argv
[j
]] = '\0';
2096 if (posix_to_mac_pathname (tempcmdname
, tempmaccmdname
,
2099 newargv
[j
] = (char *) alloca (strlen (tempmaccmdname
)
2101 strcpy (newargv
[j
], tempmaccmdname
);
2102 strcat (newargv
[j
], t
);
2106 char tempmaccmdname
[MAXPATHLEN
+1];
2107 if (posix_to_mac_pathname (argv
[j
], tempmaccmdname
,
2110 newargv
[j
] = (char *) alloca (strlen (tempmaccmdname
)+1);
2111 strcpy (newargv
[j
], tempmaccmdname
);
2115 newargv
[j
] = argv
[j
];
2116 paramlen
+= strlen (newargv
[j
]) + 1;
2120 /* After expanding all the arguments, we now know the length of the
2121 parameter block to be sent to the subprocess as a message
2122 attached to the HLE. */
2123 param
= (char *) malloc (paramlen
+ 1);
2129 /* first byte of message contains number of arguments for command */
2130 strcpy (p
, macworkdir
);
2131 p
+= strlen (macworkdir
);
2133 /* null terminate strings sent so it's possible to use strcpy over there */
2134 strcpy (p
, macinfn
);
2135 p
+= strlen (macinfn
);
2137 strcpy (p
, macoutfn
);
2138 p
+= strlen (macoutfn
);
2140 strcpy (p
, macerrfn
);
2141 p
+= strlen (macerrfn
);
2143 for (j
= 1; j
< newargc
; j
++)
2145 strcpy (p
, newargv
[j
]);
2146 p
+= strlen (newargv
[j
]);
2150 c2pstr (macappname
);
2152 iErr
= FSMakeFSSpec (0, 0, macappname
, &spec
);
2160 lpbr
.launchBlockID
= extendedBlock
;
2161 lpbr
.launchEPBLength
= extendedBlockLen
;
2162 lpbr
.launchControlFlags
= launchContinue
+ launchNoFileFlags
;
2163 lpbr
.launchAppSpec
= &spec
;
2164 lpbr
.launchAppParameters
= NULL
;
2166 iErr
= LaunchApplication (&lpbr
); /* call the subprocess */
2173 send_event
.what
= kHighLevelEvent
;
2174 send_event
.message
= kEmacsSubprocessSend
;
2175 /* Event ID stored in "where" unused */
2178 /* OS may think current subprocess has terminated if previous one
2179 terminated recently. */
2182 iErr
= PostHighLevelEvent (&send_event
, &lpbr
.launchProcessSN
, 0, param
,
2183 paramlen
+ 1, receiverIDisPSN
);
2185 while (iErr
== sessClosedErr
&& retries
-- > 0);
2193 cursor_region_handle
= NewRgn ();
2195 /* Wait for the subprocess to finish, when it will send us a ERPY
2196 high level event. */
2198 if (WaitNextEvent (highLevelEventMask
, &reply_event
, 180,
2199 cursor_region_handle
)
2200 && reply_event
.message
== kEmacsSubprocessReply
)
2203 /* The return code is sent through the refCon */
2204 iErr
= AcceptHighLevelEvent (&targ
, &ref_con
, NULL
, &len
);
2207 DisposeHandle ((Handle
) cursor_region_handle
);
2212 DisposeHandle ((Handle
) cursor_region_handle
);
2216 #endif /* not TARGET_API_MAC_CARBON */
2221 opendir (const char *dirname
)
2223 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
2224 char mac_pathname
[MAXPATHLEN
+1], vol_name
[MAXPATHLEN
+1];
2228 int len
, vol_name_len
;
2230 if (find_true_pathname (dirname
, true_pathname
, MAXPATHLEN
+1) == -1)
2233 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
2235 fully_resolved_name
[len
] = '\0';
2237 strcpy (fully_resolved_name
, true_pathname
);
2239 dirp
= (DIR *) malloc (sizeof(DIR));
2243 /* Handle special case when dirname is "/": sets up for readir to
2244 get all mount volumes. */
2245 if (strcmp (fully_resolved_name
, "/") == 0)
2247 dirp
->getting_volumes
= 1; /* special all mounted volumes DIR struct */
2248 dirp
->current_index
= 1; /* index for first volume */
2252 /* Handle typical cases: not accessing all mounted volumes. */
2253 if (!posix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
2256 /* Emacs calls opendir without the trailing '/', Mac needs trailing ':' */
2257 len
= strlen (mac_pathname
);
2258 if (mac_pathname
[len
- 1] != ':' && len
< MAXPATHLEN
)
2259 strcat (mac_pathname
, ":");
2261 /* Extract volume name */
2262 vol_name_len
= strchr (mac_pathname
, ':') - mac_pathname
;
2263 strncpy (vol_name
, mac_pathname
, vol_name_len
);
2264 vol_name
[vol_name_len
] = '\0';
2265 strcat (vol_name
, ":");
2267 c2pstr (mac_pathname
);
2268 cipb
.hFileInfo
.ioNamePtr
= mac_pathname
;
2269 /* using full pathname so vRefNum and DirID ignored */
2270 cipb
.hFileInfo
.ioVRefNum
= 0;
2271 cipb
.hFileInfo
.ioDirID
= 0;
2272 cipb
.hFileInfo
.ioFDirIndex
= 0;
2273 /* set to 0 to get information about specific dir or file */
2275 errno
= PBGetCatInfo (&cipb
, false);
2282 if (!(cipb
.hFileInfo
.ioFlAttrib
& 0x10)) /* bit 4 = 1 for directories */
2283 return 0; /* not a directory */
2285 dirp
->dir_id
= cipb
.dirInfo
.ioDrDirID
; /* used later in readdir */
2286 dirp
->getting_volumes
= 0;
2287 dirp
->current_index
= 1; /* index for first file/directory */
2290 vpb
.ioNamePtr
= vol_name
;
2291 /* using full pathname so vRefNum and DirID ignored */
2293 vpb
.ioVolIndex
= -1;
2294 errno
= PBHGetVInfo ((union HParamBlockRec
*) &vpb
, false);
2301 dirp
->vol_ref_num
= vpb
.ioVRefNum
;
2318 HParamBlockRec hpblock
;
2320 static struct dirent s_dirent
;
2321 static Str255 s_name
;
2325 /* Handle the root directory containing the mounted volumes. Call
2326 PBHGetVInfo specifying an index to obtain the info for a volume.
2327 PBHGetVInfo returns an error when it receives an index beyond the
2328 last volume, at which time we should return a nil dirent struct
2330 if (dp
->getting_volumes
)
2332 hpblock
.volumeParam
.ioNamePtr
= s_name
;
2333 hpblock
.volumeParam
.ioVRefNum
= 0;
2334 hpblock
.volumeParam
.ioVolIndex
= dp
->current_index
;
2336 errno
= PBHGetVInfo (&hpblock
, false);
2344 strcat (s_name
, "/"); /* need "/" for stat to work correctly */
2346 dp
->current_index
++;
2348 s_dirent
.d_ino
= hpblock
.volumeParam
.ioVRefNum
;
2349 s_dirent
.d_name
= s_name
;
2355 cipb
.hFileInfo
.ioVRefNum
= dp
->vol_ref_num
;
2356 cipb
.hFileInfo
.ioNamePtr
= s_name
;
2357 /* location to receive filename returned */
2359 /* return only visible files */
2363 cipb
.hFileInfo
.ioDirID
= dp
->dir_id
;
2364 /* directory ID found by opendir */
2365 cipb
.hFileInfo
.ioFDirIndex
= dp
->current_index
;
2367 errno
= PBGetCatInfo (&cipb
, false);
2374 /* insist on a visible entry */
2375 if (cipb
.hFileInfo
.ioFlAttrib
& 0x10) /* directory? */
2376 done
= !(cipb
.dirInfo
.ioDrUsrWds
.frFlags
& fInvisible
);
2378 done
= !(cipb
.hFileInfo
.ioFlFndrInfo
.fdFlags
& fInvisible
);
2380 dp
->current_index
++;
2393 s_dirent
.d_ino
= cipb
.dirInfo
.ioDrDirID
;
2394 /* value unimportant: non-zero for valid file */
2395 s_dirent
.d_name
= s_name
;
2405 char mac_pathname
[MAXPATHLEN
+1];
2406 Str255 directory_name
;
2410 if (path_from_vol_dir_name (mac_pathname
, 255, 0, 0, "\p") == 0)
2413 if (mac_to_posix_pathname (mac_pathname
, path
, MAXPATHLEN
+1) == 0)
2419 #endif /* ! MAC_OSX */
2423 initialize_applescript ()
2428 /* if open fails, as_scripting_component is set to NULL. Its
2429 subsequent use in OSA calls will fail with badComponentInstance
2431 as_scripting_component
= OpenDefaultComponent (kOSAComponentType
,
2432 kAppleScriptSubtype
);
2434 null_desc
.descriptorType
= typeNull
;
2435 null_desc
.dataHandle
= 0;
2436 osaerror
= OSAMakeContext (as_scripting_component
, &null_desc
,
2437 kOSANullScript
, &as_script_context
);
2439 as_script_context
= kOSANullScript
;
2440 /* use default context if create fails */
2444 void terminate_applescript()
2446 OSADispose (as_scripting_component
, as_script_context
);
2447 CloseComponent (as_scripting_component
);
2451 /* Compile and execute the AppleScript SCRIPT and return the error
2452 status as function value. A zero is returned if compilation and
2453 execution is successful, in which case RESULT returns a pointer to
2454 a string containing the resulting script value. Otherwise, the Mac
2455 error code is returned and RESULT returns a pointer to an error
2456 string. In both cases the caller should deallocate the storage
2457 used by the string pointed to by RESULT if it is non-NULL. For
2458 documentation on the MacOS scripting architecture, see Inside
2459 Macintosh - Interapplication Communications: Scripting Components. */
2462 do_applescript (char *script
, char **result
)
2464 AEDesc script_desc
, result_desc
, error_desc
;
2471 if (!as_scripting_component
)
2472 initialize_applescript();
2474 error
= AECreateDesc (typeChar
, script
, strlen(script
), &script_desc
);
2478 osaerror
= OSADoScript (as_scripting_component
, &script_desc
, kOSANullScript
,
2479 typeChar
, kOSAModeNull
, &result_desc
);
2481 if (osaerror
== errOSAScriptError
)
2483 /* error executing AppleScript: retrieve error message */
2484 if (!OSAScriptError (as_scripting_component
, kOSAErrorMessage
, typeChar
,
2487 #if TARGET_API_MAC_CARBON
2488 length
= AEGetDescDataSize (&error_desc
);
2489 *result
= (char *) xmalloc (length
+ 1);
2492 AEGetDescData (&error_desc
, *result
, length
);
2493 *(*result
+ length
) = '\0';
2495 #else /* not TARGET_API_MAC_CARBON */
2496 HLock (error_desc
.dataHandle
);
2497 length
= GetHandleSize(error_desc
.dataHandle
);
2498 *result
= (char *) xmalloc (length
+ 1);
2501 memcpy (*result
, *(error_desc
.dataHandle
), length
);
2502 *(*result
+ length
) = '\0';
2504 HUnlock (error_desc
.dataHandle
);
2505 #endif /* not TARGET_API_MAC_CARBON */
2506 AEDisposeDesc (&error_desc
);
2509 else if (osaerror
== noErr
) /* success: retrieve resulting script value */
2511 #if TARGET_API_MAC_CARBON
2512 length
= AEGetDescDataSize (&result_desc
);
2513 *result
= (char *) xmalloc (length
+ 1);
2516 AEGetDescData (&result_desc
, *result
, length
);
2517 *(*result
+ length
) = '\0';
2519 #else /* not TARGET_API_MAC_CARBON */
2520 HLock (result_desc
.dataHandle
);
2521 length
= GetHandleSize(result_desc
.dataHandle
);
2522 *result
= (char *) xmalloc (length
+ 1);
2525 memcpy (*result
, *(result_desc
.dataHandle
), length
);
2526 *(*result
+ length
) = '\0';
2528 HUnlock (result_desc
.dataHandle
);
2529 #endif /* not TARGET_API_MAC_CARBON */
2530 AEDisposeDesc (&result_desc
);
2533 AEDisposeDesc (&script_desc
);
2539 DEFUN ("do-applescript", Fdo_applescript
, Sdo_applescript
, 1, 1, 0,
2540 doc
: /* Compile and execute AppleScript SCRIPT and retrieve and return the result.
2541 If compilation and execution are successful, the resulting script
2542 value is returned as a string. Otherwise the function aborts and
2543 displays the error message returned by the AppleScript scripting
2548 char *result
, *temp
;
2549 Lisp_Object lisp_result
;
2552 CHECK_STRING (script
);
2555 status
= do_applescript (SDATA (script
), &result
);
2560 error ("AppleScript error %d", status
);
2563 /* Unfortunately only OSADoScript in do_applescript knows how
2564 how large the resulting script value or error message is
2565 going to be and therefore as caller memory must be
2566 deallocated here. It is necessary to free the error
2567 message before calling error to avoid a memory leak. */
2568 temp
= (char *) alloca (strlen (result
) + 1);
2569 strcpy (temp
, result
);
2576 lisp_result
= build_string (result
);
2583 DEFUN ("mac-file-name-to-posix", Fmac_file_name_to_posix
,
2584 Smac_file_name_to_posix
, 1, 1, 0,
2585 doc
: /* Convert Macintosh filename to Posix form. */)
2587 Lisp_Object mac_filename
;
2589 char posix_filename
[MAXPATHLEN
+1];
2591 CHECK_STRING (mac_filename
);
2593 if (mac_to_posix_pathname (SDATA (mac_filename
), posix_filename
,
2595 return build_string (posix_filename
);
2601 DEFUN ("posix-file-name-to-mac", Fposix_file_name_to_mac
,
2602 Sposix_file_name_to_mac
, 1, 1, 0,
2603 doc
: /* Convert Posix filename to Mac form. */)
2605 Lisp_Object posix_filename
;
2607 char mac_filename
[MAXPATHLEN
+1];
2609 CHECK_STRING (posix_filename
);
2611 if (posix_to_mac_pathname (SDATA (posix_filename
), mac_filename
,
2613 return build_string (mac_filename
);
2619 /* set interprogram-paste-function to mac-paste-function in mac-win.el
2620 to enable Emacs to obtain the contents of the Mac clipboard. */
2621 DEFUN ("mac-paste-function", Fmac_paste_function
, Smac_paste_function
, 0, 0, 0,
2622 doc
: /* Return the contents of the Mac clipboard as a string. */)
2625 #if TARGET_API_MAC_CARBON
2628 ScrapFlavorFlags sff
;
2634 err
= GetCurrentScrap (&scrap
);
2636 err
= GetScrapFlavorFlags (scrap
, kScrapFlavorTypeText
, &sff
);
2638 err
= GetScrapFlavorSize (scrap
, kScrapFlavorTypeText
, &s
);
2639 if (err
== noErr
&& (data
= (char*) alloca (s
)))
2640 err
= GetScrapFlavorData (scrap
, kScrapFlavorTypeText
, &s
, data
);
2642 if (err
!= noErr
|| s
== 0)
2645 /* Emacs expects clipboard contents have Unix-style eol's */
2646 for (i
= 0; i
< s
; i
++)
2647 if (data
[i
] == '\r')
2650 return make_string (data
, s
);
2651 #else /* not TARGET_API_MAC_CARBON */
2654 long scrap_offset
, rc
, i
;
2656 my_handle
= NewHandle (0); /* allocate 0-length data area */
2658 rc
= GetScrap (my_handle
, 'TEXT', &scrap_offset
);
2664 /* Emacs expects clipboard contents have Unix-style eol's */
2665 for (i
= 0; i
< rc
; i
++)
2666 if ((*my_handle
)[i
] == '\r')
2667 (*my_handle
)[i
] = '\n';
2669 value
= make_string (*my_handle
, rc
);
2671 HUnlock (my_handle
);
2673 DisposeHandle (my_handle
);
2676 #endif /* not TARGET_API_MAC_CARBON */
2680 /* set interprogram-cut-function to mac-cut-function in mac-win.el
2681 to enable Emacs to write the top of the kill-ring to the Mac clipboard. */
2682 DEFUN ("mac-cut-function", Fmac_cut_function
, Smac_cut_function
, 1, 2, 0,
2683 doc
: /* Put the value of the string parameter to the Mac clipboard. */)
2685 Lisp_Object value
, push
;
2690 /* fixme: ignore the push flag for now */
2692 CHECK_STRING (value
);
2694 len
= SCHARS (value
);
2695 buf
= (char *) alloca (len
+1);
2696 bcopy (SDATA (value
), buf
, len
);
2699 /* convert to Mac-style eol's before sending to clipboard */
2700 for (i
= 0; i
< len
; i
++)
2704 #if TARGET_API_MAC_CARBON
2709 ClearCurrentScrap ();
2710 if (GetCurrentScrap (&scrap
) != noErr
)
2713 error ("cannot get current scrap");
2716 if (PutScrapFlavor (scrap
, kScrapFlavorTypeText
, kScrapFlavorMaskNone
, len
,
2720 error ("cannot put to scrap");
2724 #else /* not TARGET_API_MAC_CARBON */
2726 PutScrap (len
, 'TEXT', buf
);
2727 #endif /* not TARGET_API_MAC_CARBON */
2733 DEFUN ("x-selection-exists-p", Fx_selection_exists_p
, Sx_selection_exists_p
,
2735 doc
: /* Whether there is an owner for the given X Selection.
2736 The arg should be the name of the selection in question, typically one of
2737 the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.
2738 \(Those are literal upper-case symbol names, since that's what X expects.)
2739 For convenience, the symbol nil is the same as `PRIMARY',
2740 and t is the same as `SECONDARY'. */)
2742 Lisp_Object selection
;
2744 CHECK_SYMBOL (selection
);
2746 /* Return nil for PRIMARY and SECONDARY selections; for CLIPBOARD, check
2747 if the clipboard currently has valid text format contents. */
2749 if (EQ (selection
, QCLIPBOARD
))
2751 Lisp_Object val
= Qnil
;
2753 #if TARGET_API_MAC_CARBON
2755 ScrapFlavorFlags sff
;
2758 if (GetCurrentScrap (&scrap
) == noErr
)
2759 if (GetScrapFlavorFlags (scrap
, kScrapFlavorTypeText
, &sff
) == noErr
)
2762 #else /* not TARGET_API_MAC_CARBON */
2764 long rc
, scrap_offset
;
2766 my_handle
= NewHandle (0);
2768 rc
= GetScrap (my_handle
, 'TEXT', &scrap_offset
);
2772 DisposeHandle (my_handle
);
2773 #endif /* not TARGET_API_MAC_CARBON */
2783 extern int inhibit_window_system
;
2784 extern int noninteractive
;
2786 /* When Emacs is started from the Finder, SELECT always immediately
2787 returns as if input is present when file descriptor 0 is polled for
2788 input. Strangely, when Emacs is run as a GUI application from the
2789 command line, it blocks in the same situation. This `wrapper' of
2790 the system call SELECT corrects this discrepancy. */
2792 sys_select (n
, rfds
, wfds
, efds
, timeout
)
2797 struct timeval
*timeout
;
2800 EMACS_TIME end_time
, now
, remaining_time
;
2802 if (inhibit_window_system
|| noninteractive
2803 || rfds
== NULL
|| !FD_ISSET (0, rfds
))
2804 return select (n
, rfds
, wfds
, efds
, timeout
);
2806 if (wfds
== NULL
&& efds
== NULL
)
2810 for (i
= 1; i
< n
; i
++)
2811 if (FD_ISSET (i
, rfds
))
2815 EventTimeout timeout_sec
=
2817 ? (EMACS_SECS (*timeout
) * kEventDurationSecond
2818 + EMACS_USECS (*timeout
) * kEventDurationMicrosecond
)
2819 : kEventDurationForever
);
2822 err
= ReceiveNextEvent (0, NULL
, timeout_sec
,
2823 kEventLeaveInQueue
, NULL
);
2838 remaining_time
= *timeout
;
2839 EMACS_GET_TIME (now
);
2840 EMACS_ADD_TIME (end_time
, now
, remaining_time
);
2845 EMACS_TIME select_timeout
;
2846 SELECT_TYPE orfds
= *rfds
;
2849 EMACS_SET_SECS_USECS (select_timeout
, 0, 20000);
2851 if (timeout
&& EMACS_TIME_LT (remaining_time
, select_timeout
))
2852 select_timeout
= remaining_time
;
2854 r
= select (n
, &orfds
, wfds
, efds
, &select_timeout
);
2856 err
= ReceiveNextEvent (0, NULL
, kEventDurationNoWait
,
2857 kEventLeaveInQueue
, NULL
);
2869 else if (err
== noErr
)
2878 EMACS_GET_TIME (now
);
2879 EMACS_SUB_TIME (remaining_time
, end_time
, now
);
2882 while (!timeout
|| EMACS_TIME_LT (now
, end_time
));
2887 /* Set up environment variables so that Emacs can correctly find its
2888 support files when packaged as an application bundle. Directories
2889 placed in /usr/local/share/emacs/<emacs-version>/, /usr/local/bin,
2890 and /usr/local/libexec/emacs/<emacs-version>/<system-configuration>
2891 by `make install' by default can instead be placed in
2892 .../Emacs.app/Contents/Resources/ and
2893 .../Emacs.app/Contents/MacOS/. Each of these environment variables
2894 is changed only if it is not already set. Presumably if the user
2895 sets an environment variable, he will want to use files in his path
2896 instead of ones in the application bundle. */
2898 init_mac_osx_environment ()
2902 CFStringRef cf_app_bundle_pathname
;
2903 int app_bundle_pathname_len
;
2904 char *app_bundle_pathname
;
2908 /* Fetch the pathname of the application bundle as a C string into
2909 app_bundle_pathname. */
2911 bundle
= CFBundleGetMainBundle ();
2915 bundleURL
= CFBundleCopyBundleURL (bundle
);
2919 cf_app_bundle_pathname
= CFURLCopyFileSystemPath (bundleURL
,
2920 kCFURLPOSIXPathStyle
);
2921 app_bundle_pathname_len
= CFStringGetLength (cf_app_bundle_pathname
);
2922 app_bundle_pathname
= (char *) alloca (app_bundle_pathname_len
+ 1);
2924 if (!CFStringGetCString (cf_app_bundle_pathname
,
2925 app_bundle_pathname
,
2926 app_bundle_pathname_len
+ 1,
2927 kCFStringEncodingISOLatin1
))
2929 CFRelease (cf_app_bundle_pathname
);
2933 CFRelease (cf_app_bundle_pathname
);
2935 /* P should have sufficient room for the pathname of the bundle plus
2936 the subpath in it leading to the respective directories. Q
2937 should have three times that much room because EMACSLOADPATH can
2938 have the value "<path to lisp dir>:<path to leim dir>:<path to
2940 p
= (char *) alloca (app_bundle_pathname_len
+ 50);
2941 q
= (char *) alloca (3 * app_bundle_pathname_len
+ 150);
2942 if (!getenv ("EMACSLOADPATH"))
2946 strcpy (p
, app_bundle_pathname
);
2947 strcat (p
, "/Contents/Resources/lisp");
2948 if (stat (p
, &st
) == 0 && (st
.st_mode
& S_IFMT
) == S_IFDIR
)
2951 strcpy (p
, app_bundle_pathname
);
2952 strcat (p
, "/Contents/Resources/leim");
2953 if (stat (p
, &st
) == 0 && (st
.st_mode
& S_IFMT
) == S_IFDIR
)
2960 strcpy (p
, app_bundle_pathname
);
2961 strcat (p
, "/Contents/Resources/site-lisp");
2962 if (stat (p
, &st
) == 0 && (st
.st_mode
& S_IFMT
) == S_IFDIR
)
2970 setenv ("EMACSLOADPATH", q
, 1);
2973 if (!getenv ("EMACSPATH"))
2977 strcpy (p
, app_bundle_pathname
);
2978 strcat (p
, "/Contents/MacOS/libexec");
2979 if (stat (p
, &st
) == 0 && (st
.st_mode
& S_IFMT
) == S_IFDIR
)
2982 strcpy (p
, app_bundle_pathname
);
2983 strcat (p
, "/Contents/MacOS/bin");
2984 if (stat (p
, &st
) == 0 && (st
.st_mode
& S_IFMT
) == S_IFDIR
)
2992 setenv ("EMACSPATH", q
, 1);
2995 if (!getenv ("EMACSDATA"))
2997 strcpy (p
, app_bundle_pathname
);
2998 strcat (p
, "/Contents/Resources/etc");
2999 if (stat (p
, &st
) == 0 && (st
.st_mode
& S_IFMT
) == S_IFDIR
)
3000 setenv ("EMACSDATA", p
, 1);
3003 if (!getenv ("EMACSDOC"))
3005 strcpy (p
, app_bundle_pathname
);
3006 strcat (p
, "/Contents/Resources/etc");
3007 if (stat (p
, &st
) == 0 && (st
.st_mode
& S_IFMT
) == S_IFDIR
)
3008 setenv ("EMACSDOC", p
, 1);
3011 if (!getenv ("INFOPATH"))
3013 strcpy (p
, app_bundle_pathname
);
3014 strcat (p
, "/Contents/Resources/info");
3015 if (stat (p
, &st
) == 0 && (st
.st_mode
& S_IFMT
) == S_IFDIR
)
3016 setenv ("INFOPATH", p
, 1);
3019 #endif /* MAC_OSX */
3024 QCLIPBOARD
= intern ("CLIPBOARD");
3025 staticpro (&QCLIPBOARD
);
3027 defsubr (&Smac_paste_function
);
3028 defsubr (&Smac_cut_function
);
3029 defsubr (&Sx_selection_exists_p
);
3031 defsubr (&Sdo_applescript
);
3032 defsubr (&Smac_file_name_to_posix
);
3033 defsubr (&Sposix_file_name_to_mac
);
3036 /* arch-tag: 29d30c1f-0c6b-4f88-8a6d-0558d7f9dbff
3037 (do not change this comment) */