1 /* Unix emulation routines for GNU Emacs on the Mac OS.
2 Copyright (C) 2000 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@users.sourceforge.net). */
32 #include <sys/param.h>
39 #include <TextUtils.h>
41 #include <Resources.h>
46 #include <AppleScript.h>
50 #include "sysselect.h"
53 Lisp_Object QCLIPBOARD
;
55 /* An instance of the AppleScript component. */
56 static ComponentInstance as_scripting_component
;
57 /* The single script context used for all script executions. */
58 static OSAID as_script_context
;
61 /* When converting from Mac to Unix pathnames, /'s in folder names are
62 converted to :'s. This function, used in copying folder names,
63 performs a strncat and converts all character a to b in the copy of
64 the string s2 appended to the end of s1. */
67 string_cat_and_replace (char *s1
, const char *s2
, int n
, char a
, char b
)
75 for (i
= 0; i
< l2
; i
++)
84 /* Convert a Mac pathname to Unix form. A Mac full pathname is one
85 that does not begin with a ':' and contains at least one ':'. A Mac
86 full pathname causes an '/' to be prepended to the Unix pathname.
87 The algorithm for the rest of the pathname is as follows:
88 For each segment between two ':',
89 if it is non-null, copy as is and then add a '/' at the end,
90 otherwise, insert a "../" into the Unix pathname.
91 Returns 1 if successful; 0 if fails. */
94 mac_to_unix_pathname (const char *mfn
, char *ufn
, int ufnbuflen
)
96 const char *p
, *q
, *pe
;
103 p
= strchr (mfn
, ':');
104 if (p
!= 0 && p
!= mfn
) /* full pathname */
111 pe
= mfn
+ strlen (mfn
);
118 { /* two consecutive ':' */
119 if (strlen (ufn
) + 3 >= ufnbuflen
)
125 if (strlen (ufn
) + (q
- p
) + 1 >= ufnbuflen
)
127 string_cat_and_replace (ufn
, p
, q
- p
, '/', ':');
134 if (strlen (ufn
) + (pe
- p
) >= ufnbuflen
)
136 string_cat_and_replace (ufn
, p
, pe
- p
, '/', ':');
137 /* no separator for last one */
146 extern char *get_temp_dir_name ();
149 /* Convert a Unix pathname to Mac form. Approximately reverse of the
150 above in algorithm. */
153 unix_to_mac_pathname (const char *ufn
, char *mfn
, int mfnbuflen
)
155 const char *p
, *q
, *pe
;
156 char expanded_pathname
[MAXPATHLEN
+1];
165 /* Check for and handle volume names. Last comparison: strangely
166 somewhere "/.emacs" is passed. A temporary fix for now. */
167 if (*p
== '/' && strchr (p
+1, '/') == NULL
&& strcmp (p
, "/.emacs") != 0)
169 if (strlen (p
) + 1 > mfnbuflen
)
176 /* expand to emacs dir found by init_emacs_passwd_dir */
177 if (strncmp (p
, "~emacs/", 7) == 0)
179 struct passwd
*pw
= getpwnam ("emacs");
181 if (strlen (pw
->pw_dir
) + strlen (p
) > MAXPATHLEN
)
183 strcpy (expanded_pathname
, pw
->pw_dir
);
184 strcat (expanded_pathname
, p
);
185 p
= expanded_pathname
;
186 /* now p points to the pathname with emacs dir prefix */
188 else if (strncmp (p
, "/tmp/", 5) == 0)
190 char *t
= get_temp_dir_name ();
192 if (strlen (t
) + strlen (p
) > MAXPATHLEN
)
194 strcpy (expanded_pathname
, t
);
195 strcat (expanded_pathname
, p
);
196 p
= expanded_pathname
;
197 /* now p points to the pathname with emacs dir prefix */
199 else if (*p
!= '/') /* relative pathname */
211 if (q
- p
== 2 && *p
== '.' && *(p
+1) == '.')
213 if (strlen (mfn
) + 1 >= mfnbuflen
)
219 if (strlen (mfn
) + (q
- p
) + 1 >= mfnbuflen
)
221 string_cat_and_replace (mfn
, p
, q
- p
, ':', '/');
228 if (strlen (mfn
) + (pe
- p
) >= mfnbuflen
)
230 string_cat_and_replace (mfn
, p
, pe
- p
, ':', '/');
239 /* The following functions with "sys_" prefix are stubs to Unix
240 functions that have already been implemented by CW or MPW. The
241 calls to them in Emacs source course are #define'd to call the sys_
242 versions by the header files s-mac.h. In these stubs pathnames are
243 converted between their Unix and Mac forms. */
246 /* Unix epoch is Jan 1, 1970 while Mac epoch is Jan 1, 1904: 66 years
247 + 17 leap days. These are for adjusting time values returned by
248 MacOS Toolbox functions. */
250 #define MAC_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
253 #ifndef CODEWARRIOR_VERSION_6
254 /* CW Pro 5 epoch is Jan 1, 1900 (aaarghhhhh!); remember, 1900 is not
255 a leap year! This is for adjusting time_t values returned by MSL
257 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 70 + 17) * 24 * 60 * 60)
259 /* CW changes Pro 6 to following Unix! */
260 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
263 /* MPW library functions follow Unix (confused?). */
264 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
270 /* Define our own stat function for both MrC and CW. The reason for
271 doing this: "stat" is both the name of a struct and function name:
272 can't use the same trick like that for sys_open, sys_close, etc. to
273 redirect Emacs's calls to our own version that converts Unix style
274 filenames to Mac style filename because all sorts of compilation
275 errors will be generated if stat is #define'd to be sys_stat. */
278 stat_noalias (const char *path
, struct stat
*buf
)
280 char mac_pathname
[MAXPATHLEN
+1];
283 if (unix_to_mac_pathname (path
, mac_pathname
, MAXPATHLEN
+1) == 0)
286 c2pstr (mac_pathname
);
287 cipb
.hFileInfo
.ioNamePtr
= mac_pathname
;
288 cipb
.hFileInfo
.ioVRefNum
= 0;
289 cipb
.hFileInfo
.ioDirID
= 0;
290 cipb
.hFileInfo
.ioFDirIndex
= 0;
291 /* set to 0 to get information about specific dir or file */
293 errno
= PBGetCatInfo (&cipb
, false);
294 if (errno
== -43) /* -43: fnfErr defined in Errors.h */
299 if (cipb
.hFileInfo
.ioFlAttrib
& 0x10) /* bit 4 = 1 for directories */
301 buf
->st_mode
= S_IFDIR
| S_IREAD
| S_IEXEC
;
303 if (!(cipb
.hFileInfo
.ioFlAttrib
& 0x1))
304 buf
->st_mode
|= S_IWRITE
; /* bit 1 = 1 for locked files/directories */
305 buf
->st_ino
= cipb
.dirInfo
.ioDrDirID
;
306 buf
->st_dev
= cipb
.dirInfo
.ioVRefNum
;
307 buf
->st_size
= cipb
.dirInfo
.ioDrNmFls
;
308 /* size of dir = number of files and dirs */
311 = cipb
.dirInfo
.ioDrMdDat
- MAC_UNIX_EPOCH_DIFF
;
312 buf
->st_ctime
= cipb
.dirInfo
.ioDrCrDat
- MAC_UNIX_EPOCH_DIFF
;
316 buf
->st_mode
= S_IFREG
| S_IREAD
;
317 if (!(cipb
.hFileInfo
.ioFlAttrib
& 0x1))
318 buf
->st_mode
|= S_IWRITE
; /* bit 1 = 1 for locked files/directories */
319 if (cipb
.hFileInfo
.ioFlFndrInfo
.fdType
== 'APPL')
320 buf
->st_mode
|= S_IEXEC
;
321 buf
->st_ino
= cipb
.hFileInfo
.ioDirID
;
322 buf
->st_dev
= cipb
.hFileInfo
.ioVRefNum
;
323 buf
->st_size
= cipb
.hFileInfo
.ioFlLgLen
;
326 = cipb
.hFileInfo
.ioFlMdDat
- MAC_UNIX_EPOCH_DIFF
;
327 buf
->st_ctime
= cipb
.hFileInfo
.ioFlCrDat
- MAC_UNIX_EPOCH_DIFF
;
330 if (cipb
.hFileInfo
.ioFlFndrInfo
.fdFlags
& 0x8000)
332 /* identify alias files as symlinks */
333 buf
->st_mode
|= S_IFLNK
;
334 buf
->st_mode
&= ~S_IFREG
;
338 buf
->st_uid
= getuid ();
339 buf
->st_gid
= getgid ();
347 lstat (const char *path
, struct stat
*buf
)
350 char true_pathname
[MAXPATHLEN
+1];
352 /* Try looking for the file without resolving aliases first. */
353 if ((result
= stat_noalias (path
, buf
)) >= 0)
356 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
359 return stat_noalias (true_pathname
, buf
);
364 stat (const char *path
, struct stat
*sb
)
367 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
370 if ((result
= stat_noalias (path
, sb
)) >= 0)
373 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
376 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
379 fully_resolved_name
[len
] = '\0';
380 /* in fact our readlink terminates strings */
381 return lstat (fully_resolved_name
, sb
);
384 return lstat (true_pathname
, sb
);
389 /* CW defines fstat in stat.mac.c while MPW does not provide this
390 function. Without the information of how to get from a file
391 descriptor in MPW StdCLib to a Mac OS file spec, it should be hard
392 to implement this function. Fortunately, there is only one place
393 where this function is called in our configuration: in fileio.c,
394 where only the st_dev and st_ino fields are used to determine
395 whether two fildes point to different i-nodes to prevent copying
396 a file onto itself equal. What we have here probably needs
400 fstat (int fildes
, struct stat
*buf
)
403 buf
->st_ino
= fildes
;
404 buf
->st_mode
= S_IFREG
; /* added by T.I. for the copy-file */
405 return 0; /* success */
410 /* Adapted from Think Reference code example. */
413 mkdir (const char *dirname
, int mode
)
418 char true_pathname
[MAXPATHLEN
+1], mac_pathname
[MAXPATHLEN
+1];
420 if (find_true_pathname (dirname
, true_pathname
, MAXPATHLEN
+1) == -1)
423 if (unix_to_mac_pathname (true_pathname
, mac_pathname
, MAXPATHLEN
+1) == 0)
426 c2pstr (mac_pathname
);
427 hfpb
.ioNamePtr
= mac_pathname
;
428 hfpb
.ioVRefNum
= 0; /* ignored unless name is invalid */
429 hfpb
.ioDirID
= 0; /* parent is the root */
431 errno
= PBDirCreate ((HParmBlkPtr
) &hfpb
, false);
432 /* just return the Mac OSErr code for now */
433 return errno
== noErr
? 0 : -1;
438 sys_rmdir (const char *dirname
)
441 char mac_pathname
[MAXPATHLEN
+1];
443 if (unix_to_mac_pathname (dirname
, mac_pathname
, MAXPATHLEN
+1) == 0)
446 c2pstr (mac_pathname
);
447 hfpb
.ioNamePtr
= mac_pathname
;
448 hfpb
.ioVRefNum
= 0; /* ignored unless name is invalid */
449 hfpb
.ioDirID
= 0; /* parent is the root */
451 errno
= PBHDelete ((HParmBlkPtr
) &hfpb
, false);
452 return errno
== noErr
? 0 : -1;
457 /* No implementation yet. */
459 execvp (const char *path
, ...)
467 utime (const char *path
, const struct utimbuf
*times
)
469 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
471 char mac_pathname
[MAXPATHLEN
+1];
474 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
477 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
479 fully_resolved_name
[len
] = '\0';
481 strcpy (fully_resolved_name
, true_pathname
);
483 if (!unix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
486 c2pstr (mac_pathname
);
487 cipb
.hFileInfo
.ioNamePtr
= mac_pathname
;
488 cipb
.hFileInfo
.ioVRefNum
= 0;
489 cipb
.hFileInfo
.ioDirID
= 0;
490 cipb
.hFileInfo
.ioFDirIndex
= 0;
491 /* set to 0 to get information about specific dir or file */
493 errno
= PBGetCatInfo (&cipb
, false);
497 if (cipb
.hFileInfo
.ioFlAttrib
& 0x10) /* bit 4 = 1 for directories */
500 cipb
.dirInfo
.ioDrMdDat
= times
->modtime
+ MAC_UNIX_EPOCH_DIFF
;
502 GetDateTime (&cipb
.dirInfo
.ioDrMdDat
);
507 cipb
.hFileInfo
.ioFlMdDat
= times
->modtime
+ MAC_UNIX_EPOCH_DIFF
;
509 GetDateTime (&cipb
.hFileInfo
.ioFlMdDat
);
512 errno
= PBSetCatInfo (&cipb
, false);
513 return errno
== noErr
? 0 : -1;
527 /* Like stat, but test for access mode in hfpb.ioFlAttrib */
529 access (const char *path
, int mode
)
531 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
533 char mac_pathname
[MAXPATHLEN
+1];
536 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
539 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
541 fully_resolved_name
[len
] = '\0';
543 strcpy (fully_resolved_name
, true_pathname
);
545 if (!unix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
548 c2pstr (mac_pathname
);
549 cipb
.hFileInfo
.ioNamePtr
= mac_pathname
;
550 cipb
.hFileInfo
.ioVRefNum
= 0;
551 cipb
.hFileInfo
.ioDirID
= 0;
552 cipb
.hFileInfo
.ioFDirIndex
= 0;
553 /* set to 0 to get information about specific dir or file */
555 errno
= PBGetCatInfo (&cipb
, false);
559 if (mode
== F_OK
) /* got this far, file exists */
563 if (cipb
.hFileInfo
.ioFlAttrib
& 0x10) /* path refers to a directory */
567 if (cipb
.hFileInfo
.ioFlFndrInfo
.fdType
== 'APPL')
574 return (cipb
.hFileInfo
.ioFlAttrib
& 0x1) ? -1 : 0;
575 /* don't allow if lock bit is on */
581 #define DEV_NULL_FD 0x10000
585 sys_open (const char *path
, int oflag
)
587 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
589 char mac_pathname
[MAXPATHLEN
+1];
591 if (strcmp (path
, "/dev/null") == 0)
592 return DEV_NULL_FD
; /* some bogus fd to be ignored in write */
594 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
597 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
599 fully_resolved_name
[len
] = '\0';
601 strcpy (fully_resolved_name
, true_pathname
);
603 if (!unix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
608 if (oflag
== O_WRONLY
|| oflag
== O_RDWR
)
609 fsetfileinfo (mac_pathname
, 'EMAx', 'TEXT');
611 return open (mac_pathname
, oflag
);
618 sys_creat (const char *path
, mode_t mode
)
620 char true_pathname
[MAXPATHLEN
+1];
622 char mac_pathname
[MAXPATHLEN
+1];
624 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
627 if (!unix_to_mac_pathname (true_pathname
, mac_pathname
, MAXPATHLEN
+1))
632 int result
= creat (mac_pathname
);
633 fsetfileinfo (mac_pathname
, 'EMAx', 'TEXT');
636 return creat (mac_pathname
, mode
);
644 sys_unlink (const char *path
)
646 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
648 char mac_pathname
[MAXPATHLEN
+1];
650 if (find_true_pathname (path
, true_pathname
, MAXPATHLEN
+1) == -1)
653 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
655 fully_resolved_name
[len
] = '\0';
657 strcpy (fully_resolved_name
, true_pathname
);
659 if (!unix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
662 return unlink (mac_pathname
);
668 sys_read (int fildes
, char *buf
, int count
)
670 if (fildes
== 0) /* this should not be used for console input */
673 #ifdef CODEWARRIOR_VERSION_6
674 return _read (fildes
, buf
, count
);
676 return read (fildes
, buf
, count
);
683 sys_write (int fildes
, const char *buf
, int count
)
685 if (fildes
== DEV_NULL_FD
)
688 #ifdef CODEWARRIOR_VERSION_6
689 return _write (fildes
, buf
, count
);
691 return write (fildes
, buf
, count
);
698 sys_rename (const char * old_name
, const char * new_name
)
700 char true_old_pathname
[MAXPATHLEN
+1], true_new_pathname
[MAXPATHLEN
+1];
701 char fully_resolved_old_name
[MAXPATHLEN
+1];
703 char mac_old_name
[MAXPATHLEN
+1], mac_new_name
[MAXPATHLEN
+1];
705 if (find_true_pathname (old_name
, true_old_pathname
, MAXPATHLEN
+1) == -1)
708 len
= readlink (true_old_pathname
, fully_resolved_old_name
, MAXPATHLEN
);
710 fully_resolved_old_name
[len
] = '\0';
712 strcpy (fully_resolved_old_name
, true_old_pathname
);
714 if (find_true_pathname (new_name
, true_new_pathname
, MAXPATHLEN
+1) == -1)
717 if (strcmp (fully_resolved_old_name
, true_new_pathname
) == 0)
720 if (!unix_to_mac_pathname (fully_resolved_old_name
,
725 if (!unix_to_mac_pathname(true_new_pathname
, mac_new_name
, MAXPATHLEN
+1))
728 /* If a file with new_name already exists, rename deletes the old
729 file in Unix. CW version fails in these situation. So we add a
730 call to unlink here. */
731 (void) unlink (mac_new_name
);
733 return rename (mac_old_name
, mac_new_name
);
738 extern FILE *fopen (const char *name
, const char *mode
);
740 sys_fopen (const char *name
, const char *mode
)
742 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
744 char mac_pathname
[MAXPATHLEN
+1];
746 if (find_true_pathname (name
, true_pathname
, MAXPATHLEN
+1) == -1)
749 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
751 fully_resolved_name
[len
] = '\0';
753 strcpy (fully_resolved_name
, true_pathname
);
755 if (!unix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
760 if (mode
[0] == 'w' || mode
[0] == 'a')
761 fsetfileinfo (mac_pathname
, 'EMAx', 'TEXT');
763 return fopen (mac_pathname
, mode
);
770 long target_ticks
= 0;
773 __sigfun alarm_signal_func
= (__sigfun
) 0;
775 __signal_func_ptr alarm_signal_func
= (__signal_func_ptr
) 0;
781 /* These functions simulate SIG_ALRM. The stub for function signal
782 stores the signal handler function in alarm_signal_func if a
783 SIG_ALRM is encountered. check_alarm is called in XTread_socket,
784 which emacs calls periodically. A pending alarm is represented by
785 a non-zero target_ticks value. check_alarm calls the handler
786 function pointed to by alarm_signal_func if one has been set up and
787 an alarm is pending. */
792 if (target_ticks
&& TickCount () > target_ticks
)
795 if (alarm_signal_func
)
796 (*alarm_signal_func
)(SIGALRM
);
802 select(n
, rfds
, wfds
, efds
, timeout
)
807 struct timeval
*timeout
;
809 EMACS_TIME end_time
, now
;
812 /* Can only handle wait for keyboard input. */
813 if (n
> 1 || wfds
|| efds
)
816 EMACS_GET_TIME (end_time
);
817 EMACS_ADD_TIME (end_time
, end_time
, *timeout
);
821 /* Also return true if an event other than a keyDown has
822 occurred. This causes kbd_buffer_get_event in keyboard.c to
823 call read_avail_input which in turn calls XTread_socket to
824 poll for these events. Otherwise these never get processed
825 except but a very slow poll timer. */
826 if (FD_ISSET (0, rfds
) && EventAvail (everyEvent
, &e
))
829 /* Also check movement of the mouse. */
832 static Point old_mouse_pos
= {-1, -1};
834 GetMouse (&mouse_pos
);
835 if (!EqualPt (mouse_pos
, old_mouse_pos
))
837 old_mouse_pos
= mouse_pos
;
842 WaitNextEvent (0, &e
, 1UL, NULL
); /* Accept no event; wait 1 tic. by T.I.*/
844 EMACS_GET_TIME (now
);
845 EMACS_SUB_TIME (now
, end_time
, now
);
847 while (!EMACS_TIME_NEG_P (now
));
853 /* Called in sys_select to wait for an alarm signal to arrive. */
861 if (!target_ticks
) /* no alarm pending */
864 if ( (tick
= TickCount ()) < target_ticks
)
865 WaitNextEvent (0, &e
, target_ticks
- tick
, NULL
); /* Accept no event; just wait. by T.I.*/
868 if (alarm_signal_func
)
869 (*alarm_signal_func
)(SIGALRM
);
878 long remaining
= target_ticks
? (TickCount () - target_ticks
) / 60 : 0;
880 target_ticks
= seconds
? TickCount () + 60 * seconds
: 0;
882 return (remaining
< 0) ? 0 : (unsigned int) remaining
;
888 extern __sigfun
signal (int signal
, __sigfun signal_func
);
890 sys_signal (int signal_num
, __sigfun signal_func
)
892 extern __signal_func_ptr
signal (int signal
, __signal_func_ptr signal_func
);
894 sys_signal (int signal_num
, __signal_func_ptr signal_func
)
899 if (signal_num
!= SIGALRM
)
900 return signal (signal_num
, signal_func
);
904 __sigfun old_signal_func
;
906 __signal_func_ptr old_signal_func
;
910 old_signal_func
= alarm_signal_func
;
911 alarm_signal_func
= signal_func
;
912 return old_signal_func
;
917 /* gettimeofday should return the amount of time (in a timeval
918 structure) since midnight today. The toolbox function Microseconds
919 returns the number of microseconds (in a UnsignedWide value) since
920 the machine was booted. Also making this complicated is WideAdd,
921 WideSubtract, etc. take wide values. */
928 static wide wall_clock_at_epoch
, clicks_at_epoch
;
929 UnsignedWide uw_microseconds
;
931 time_t sys_time (time_t *);
933 /* If this function is called for the first time, record the number
934 of seconds since midnight and the number of microseconds since
935 boot at the time of this first call. */
940 systime
= sys_time (NULL
);
941 /* Store microseconds since midnight in wall_clock_at_epoch. */
942 WideMultiply (systime
, 1000000L, &wall_clock_at_epoch
);
943 Microseconds (&uw_microseconds
);
944 /* Store microseconds since boot in clicks_at_epoch. */
945 clicks_at_epoch
.hi
= uw_microseconds
.hi
;
946 clicks_at_epoch
.lo
= uw_microseconds
.lo
;
949 /* Get time since boot */
950 Microseconds (&uw_microseconds
);
952 /* Convert to time since midnight*/
953 w_microseconds
.hi
= uw_microseconds
.hi
;
954 w_microseconds
.lo
= uw_microseconds
.lo
;
955 WideSubtract (&w_microseconds
, &clicks_at_epoch
);
956 WideAdd (&w_microseconds
, &wall_clock_at_epoch
);
957 tp
->tv_sec
= WideDivide (&w_microseconds
, 1000000L, &tp
->tv_usec
);
965 sleep (unsigned int seconds
)
969 WaitNextEvent (0, &e
, seconds
* 60UL, NULL
); /* Accept no event; just wait. by T.I.*/
976 /* The time functions adjust time values according to the difference
977 between the Unix and CW epoches. */
980 extern struct tm
*gmtime (const time_t *);
982 sys_gmtime (const time_t *timer
)
984 time_t unix_time
= *timer
+ CW_OR_MPW_UNIX_EPOCH_DIFF
;
986 return gmtime (&unix_time
);
991 extern struct tm
*localtime (const time_t *);
993 sys_localtime (const time_t *timer
)
995 #ifdef CODEWARRIOR_VERSION_6
996 time_t unix_time
= *timer
;
998 time_t unix_time
= *timer
+ CW_OR_MPW_UNIX_EPOCH_DIFF
;
1001 return localtime (&unix_time
);
1006 extern char *ctime (const time_t *);
1008 sys_ctime (const time_t *timer
)
1010 #ifdef CODEWARRIOR_VERSION_6
1011 time_t unix_time
= *timer
;
1013 time_t unix_time
= *timer
+ CW_OR_MPW_UNIX_EPOCH_DIFF
;
1016 return ctime (&unix_time
);
1021 extern time_t time (time_t *);
1023 sys_time (time_t *timer
)
1025 #ifdef CODEWARRIOR_VERSION_6
1026 time_t mac_time
= time (NULL
);
1028 time_t mac_time
= time (NULL
) - CW_OR_MPW_UNIX_EPOCH_DIFF
;
1038 /* MPW strftime broken for "%p" format */
1043 sys_strftime (char * s
, size_t maxsize
, const char * format
,
1044 const struct tm
* timeptr
)
1046 if (strcmp (format
, "%p") == 0)
1050 if (timeptr
->tm_hour
< 12)
1062 return strftime (s
, maxsize
, format
, timeptr
);
1064 #endif /* __MRC__ */
1067 /* no subprocesses, empty wait */
1077 croak (char *badfunc
)
1079 printf ("%s not yet implemented\r\n", badfunc
);
1085 index (const char * str
, int chr
)
1087 return strchr (str
, chr
);
1092 mktemp (char *template)
1097 len
= strlen (template);
1099 while (k
>= 0 && template[k
] == 'X')
1102 k
++; /* make k index of first 'X' */
1106 /* Zero filled, number of digits equal to the number of X's. */
1107 sprintf (&template[k
], "%0*d", len
-k
, seqnum
++);
1116 /* Emulate getpwuid, getpwnam and others. */
1118 #define PASSWD_FIELD_SIZE 256
1120 static char my_passwd_name
[PASSWD_FIELD_SIZE
];
1121 static char my_passwd_dir
[MAXPATHLEN
+1];
1123 static struct passwd my_passwd
=
1130 /* Initialized by main () in macterm.c to pathname of emacs directory. */
1132 char emacs_passwd_dir
[MAXPATHLEN
+1];
1138 init_emacs_passwd_dir ()
1142 if (getwd (emacs_passwd_dir
) && getwd (my_passwd_dir
))
1144 /* Need pathname of first ancestor that begins with "emacs"
1145 since Mac emacs application is somewhere in the emacs-*
1147 int len
= strlen (emacs_passwd_dir
);
1149 /* j points to the "/" following the directory name being
1152 while (i
>= 0 && !found
)
1154 while (i
>= 0 && emacs_passwd_dir
[i
] != '/')
1156 if (emacs_passwd_dir
[i
] == '/' && i
+5 < len
)
1157 found
= (strncmp (&(emacs_passwd_dir
[i
+1]), "emacs", 5) == 0);
1159 emacs_passwd_dir
[j
+1] = '\0';
1170 /* Setting to "/" probably won't work but set it to something
1172 strcpy (emacs_passwd_dir
, "/");
1173 strcpy (my_passwd_dir
, "/");
1178 static struct passwd emacs_passwd
=
1184 static int my_passwd_inited
= 0;
1192 /* Note: my_passwd_dir initialized in int_emacs_passwd_dir to
1193 directory where Emacs was started. */
1195 owner_name
= (char **) GetResource ('STR ',-16096);
1199 BlockMove ((unsigned char *) *owner_name
,
1200 (unsigned char *) my_passwd_name
,
1202 HUnlock (owner_name
);
1203 p2cstr ((unsigned char *) my_passwd_name
);
1206 my_passwd_name
[0] = 0;
1211 getpwuid (uid_t uid
)
1213 if (!my_passwd_inited
)
1216 my_passwd_inited
= 1;
1224 getpwnam (const char *name
)
1226 if (strcmp (name
, "emacs") == 0)
1227 return &emacs_passwd
;
1229 if (!my_passwd_inited
)
1232 my_passwd_inited
= 1;
1239 /* The functions fork, kill, sigsetmask, sigblock, request_sigio,
1240 setpgrp, setpriority, and unrequest_sigio are defined to be empty
1261 error ("Can't spawn subshell");
1280 request_sigio (void)
1286 unrequest_sigio (void)
1301 pipe (int _fildes
[2])
1308 /* Hard and symbolic links. */
1311 symlink (const char *name1
, const char *name2
)
1319 link (const char *name1
, const char *name2
)
1326 /* Determine the path name of the file specified by VREFNUM, DIRID,
1327 and NAME and place that in the buffer PATH of length
1330 path_from_vol_dir_name (char *path
, int man_path_len
, short vol_ref_num
,
1331 long dir_id
, ConstStr255Param name
)
1337 if (strlen (name
) > man_path_len
)
1340 memcpy (dir_name
, name
, name
[0]+1);
1341 memcpy (path
, name
, name
[0]+1);
1344 cipb
.dirInfo
.ioDrParID
= dir_id
;
1345 cipb
.dirInfo
.ioNamePtr
= dir_name
;
1349 cipb
.dirInfo
.ioVRefNum
= vol_ref_num
;
1350 cipb
.dirInfo
.ioFDirIndex
= -1;
1351 cipb
.dirInfo
.ioDrDirID
= cipb
.dirInfo
.ioDrParID
;
1352 /* go up to parent each time */
1354 err
= PBGetCatInfo (&cipb
, false);
1359 if (strlen (dir_name
) + strlen (path
) + 1 >= man_path_len
)
1362 strcat (dir_name
, ":");
1363 strcat (dir_name
, path
);
1364 /* attach to front since we're going up directory tree */
1365 strcpy (path
, dir_name
);
1367 while (cipb
.dirInfo
.ioDrDirID
!= fsRtDirID
);
1368 /* stop when we see the volume's root directory */
1370 return 1; /* success */
1375 readlink (const char *path
, char *buf
, int bufsiz
)
1377 char mac_sym_link_name
[MAXPATHLEN
+1];
1380 Boolean target_is_folder
, was_aliased
;
1381 Str255 directory_name
, mac_pathname
;
1384 if (unix_to_mac_pathname (path
, mac_sym_link_name
, MAXPATHLEN
+1) == 0)
1387 c2pstr (mac_sym_link_name
);
1388 err
= FSMakeFSSpec (0, 0, mac_sym_link_name
, &fsspec
);
1395 err
= ResolveAliasFile (&fsspec
, true, &target_is_folder
, &was_aliased
);
1396 if (err
!= noErr
|| !was_aliased
)
1402 if (path_from_vol_dir_name (mac_pathname
, 255, fsspec
.vRefNum
, fsspec
.parID
,
1409 if (mac_to_unix_pathname (mac_pathname
, buf
, bufsiz
) == 0)
1415 return strlen (buf
);
1419 /* Convert a path to one with aliases fully expanded. */
1422 find_true_pathname (const char *path
, char *buf
, int bufsiz
)
1424 char *q
, temp
[MAXPATHLEN
+1];
1428 if (bufsiz
<= 0 || path
== 0 || path
[0] == '\0')
1435 q
= strchr (p
+ 1, '/');
1437 q
= strchr (p
, '/');
1438 len
= 0; /* loop may not be entered, e.g., for "/" */
1443 strncat (temp
, p
, q
- p
);
1444 len
= readlink (temp
, buf
, bufsiz
);
1447 if (strlen (temp
) + 1 > bufsiz
)
1457 if (len
+ strlen (p
) + 1 >= bufsiz
)
1461 return len
+ strlen (p
);
1466 umask (mode_t numask
)
1468 static mode_t mask
= 022;
1469 mode_t oldmask
= mask
;
1476 chmod (const char *path
, mode_t mode
)
1478 /* say it always succeed for now */
1487 return fcntl (oldd
, F_DUPFD
, 0);
1489 /* current implementation of fcntl in fcntl.mac.c simply returns old
1491 return fcntl (oldd
, F_DUPFD
);
1498 /* This is from the original sysdep.c. Emulate BSD dup2. First close
1499 newd if it already exists. Then, attempt to dup oldd. If not
1500 successful, call dup2 recursively until we are, then close the
1501 unsuccessful ones. */
1504 dup2 (int oldd
, int newd
)
1515 ret
= dup2 (oldd
, newd
);
1521 /* let it fail for now */
1538 ioctl (int d
, int request
, void *argp
)
1548 if (fildes
>=0 && fildes
<= 2)
1581 #endif /* __MRC__ */
1585 #ifndef CODEWARRIOR_VERSION_6
1593 #endif /* __MWERKS__ */
1596 /* Return the path to the directory in which Emacs can create
1597 temporary files. The MacOS "temporary items" directory cannot be
1598 used because it removes the file written by a process when it
1599 exits. In that sense it's more like "/dev/null" than "/tmp" (but
1600 again not exactly). And of course Emacs needs to read back the
1601 files written by its subprocesses. So here we write the files to a
1602 directory "Emacs" in the Preferences Folder. This directory is
1603 created if it does not exist. */
1606 get_temp_dir_name ()
1608 static char *temp_dir_name
= NULL
;
1612 Str255 dir_name
, full_path
;
1614 char unix_dir_name
[MAXPATHLEN
+1];
1617 /* Cache directory name with pointer temp_dir_name.
1618 Look for it only the first time. */
1621 err
= FindFolder (kOnSystemDisk
, kPreferencesFolderType
, kCreateFolder
,
1622 &vol_ref_num
, &dir_id
);
1626 if (!path_from_vol_dir_name (full_path
, 255, vol_ref_num
, dir_id
, "\p"))
1629 if (strlen (full_path
) + 6 <= MAXPATHLEN
)
1630 strcat (full_path
, "Emacs:");
1634 if (!mac_to_unix_pathname (full_path
, unix_dir_name
, MAXPATHLEN
+1))
1637 dir
= opendir (unix_dir_name
); /* check whether temp directory exists */
1640 else if (mkdir (unix_dir_name
, 0700) != 0) /* create it if not */
1643 temp_dir_name
= (char *) malloc (strlen (unix_dir_name
) + 1);
1644 strcpy (temp_dir_name
, unix_dir_name
);
1647 return temp_dir_name
;
1651 /* Allocate and construct an array of pointers to strings from a list
1652 of strings stored in a 'STR#' resource. The returned pointer array
1653 is stored in the style of argv and environ: if the 'STR#' resource
1654 contains numString strings, an pointer array with numString+1
1655 elements is returned in which the last entry contains a null
1656 pointer. The pointer to the pointer array is passed by pointer in
1657 parameter t. The resource ID of the 'STR#' resource is passed in
1658 parameter StringListID.
1662 get_string_list (char ***t
, short string_list_id
)
1668 h
= GetResource ('STR#', string_list_id
);
1673 num_strings
= * (short *) p
;
1675 *t
= (char **) malloc (sizeof (char *) * (num_strings
+ 1));
1676 for (i
= 0; i
< num_strings
; i
++)
1678 short length
= *p
++;
1679 (*t
)[i
] = (char *) malloc (length
+ 1);
1680 strncpy ((*t
)[i
], p
, length
);
1681 (*t
)[i
][length
] = '\0';
1684 (*t
)[num_strings
] = 0;
1689 /* Return no string in case GetResource fails. Bug fixed by
1690 Ikegami Tsutomu. Caused MPW build to crash without sym -on
1691 option (no sym -on implies -opt local). */
1692 *t
= (char **) malloc (sizeof (char *));
1699 get_path_to_system_folder ()
1704 Str255 dir_name
, full_path
;
1706 static char system_folder_unix_name
[MAXPATHLEN
+1];
1709 err
= FindFolder (kOnSystemDisk
, kSystemFolderType
, kDontCreateFolder
,
1710 &vol_ref_num
, &dir_id
);
1714 if (!path_from_vol_dir_name (full_path
, 255, vol_ref_num
, dir_id
, "\p"))
1717 if (!mac_to_unix_pathname (full_path
, system_folder_unix_name
, MAXPATHLEN
+1))
1720 return system_folder_unix_name
;
1726 #define ENVIRON_STRING_LIST_ID 128
1728 /* Get environment variable definitions from STR# resource. */
1735 get_string_list (&environ
, ENVIRON_STRING_LIST_ID
);
1741 /* Make HOME directory the one Emacs starts up in if not specified
1743 if (getenv ("HOME") == NULL
)
1745 environ
= (char **) realloc (environ
, sizeof (char *) * (i
+ 2));
1748 environ
[i
] = (char *) malloc (strlen (my_passwd_dir
) + 6);
1751 strcpy (environ
[i
], "HOME=");
1752 strcat (environ
[i
], my_passwd_dir
);
1759 /* Make HOME directory the one Emacs starts up in if not specified
1761 if (getenv ("MAIL") == NULL
)
1763 environ
= (char **) realloc (environ
, sizeof (char *) * (i
+ 2));
1766 char * path_to_system_folder
= get_path_to_system_folder ();
1767 environ
[i
] = (char *) malloc (strlen (path_to_system_folder
) + 22);
1770 strcpy (environ
[i
], "MAIL=");
1771 strcat (environ
[i
], path_to_system_folder
);
1772 strcat (environ
[i
], "Eudora Folder/In");
1780 /* Return the value of the environment variable NAME. */
1783 getenv (const char *name
)
1785 int length
= strlen(name
);
1788 for (e
= environ
; *e
!= 0; e
++)
1789 if (strncmp(*e
, name
, length
) == 0 && (*e
)[length
] == '=')
1790 return &(*e
)[length
+ 1];
1792 if (strcmp (name
, "TMPDIR") == 0)
1793 return get_temp_dir_name ();
1800 /* see Interfaces&Libraries:Interfaces:CIncludes:signal.h */
1801 char *sys_siglist
[] =
1803 "Zero is not a signal!!!",
1805 "Interactive user interrupt", /* 2 */ "?",
1806 "Floating point exception", /* 4 */ "?", "?", "?",
1807 "Illegal instruction", /* 8 */ "?", "?", "?", "?", "?", "?", "?",
1808 "Segment violation", /* 16 */ "?", "?", "?", "?", "?", "?", "?",
1809 "?", "?", "?", "?", "?", "?", "?", "?",
1813 char *sys_siglist
[] =
1815 "Zero is not a signal!!!",
1817 "Floating point exception",
1818 "Illegal instruction",
1819 "Interactive user interrupt",
1820 "Segment violation",
1828 #include <utsname.h>
1831 uname (struct utsname
*name
)
1834 system_name
= GetString (-16413); /* IM - Resource Manager Reference */
1837 BlockMove (*system_name
, name
->nodename
, (*system_name
)[0]+1);
1838 p2cstr (name
->nodename
);
1846 #include <Processes.h>
1849 /* Event class of HLE sent to subprocess. */
1850 const OSType kEmacsSubprocessSend
= 'ESND';
1852 /* Event class of HLE sent back from subprocess. */
1853 const OSType kEmacsSubprocessReply
= 'ERPY';
1857 mystrchr (char *s
, char c
)
1859 while (*s
&& *s
!= c
)
1887 mystrcpy (char *to
, char *from
)
1899 /* Start a Mac subprocess. Arguments for it is passed in argv (null
1900 terminated). The process should run with the default directory
1901 "workdir", read input from "infn", and write output and error to
1902 "outfn" and "errfn", resp. The Process Manager call
1903 LaunchApplication is used to start the subprocess. We use high
1904 level events as the mechanism to pass arguments to the subprocess
1905 and to make Emacs wait for the subprocess to terminate and pass
1906 back a result code. The bulk of the code here packs the arguments
1907 into one message to be passed together with the high level event.
1908 Emacs also sometimes starts a subprocess using a shell to perform
1909 wildcard filename expansion. Since we don't really have a shell on
1910 the Mac, this case is detected and the starting of the shell is
1911 by-passed. We really need to add code here to do filename
1912 expansion to support such functionality. */
1915 run_mac_command (argv
, workdir
, infn
, outfn
, errfn
)
1916 unsigned char **argv
;
1917 const char *workdir
;
1918 const char *infn
, *outfn
, *errfn
;
1920 char macappname
[MAXPATHLEN
+1], macworkdir
[MAXPATHLEN
+1];
1921 char macinfn
[MAXPATHLEN
+1], macoutfn
[MAXPATHLEN
+1], macerrfn
[MAXPATHLEN
+1];
1922 int paramlen
, argc
, newargc
, j
, retries
;
1923 char **newargv
, *param
, *p
;
1926 LaunchParamBlockRec lpbr
;
1927 EventRecord send_event
, reply_event
;
1928 RgnHandle cursor_region_handle
;
1930 unsigned long ref_con
, len
;
1932 if (unix_to_mac_pathname (workdir
, macworkdir
, MAXPATHLEN
+1) == 0)
1934 if (unix_to_mac_pathname (infn
, macinfn
, MAXPATHLEN
+1) == 0)
1936 if (unix_to_mac_pathname (outfn
, macoutfn
, MAXPATHLEN
+1) == 0)
1938 if (unix_to_mac_pathname (errfn
, macerrfn
, MAXPATHLEN
+1) == 0)
1941 paramlen
= strlen (macworkdir
) + strlen (macinfn
) + strlen (macoutfn
)
1942 + strlen (macerrfn
) + 4; /* count nulls at end of strings */
1951 /* If a subprocess is invoked with a shell, we receive 3 arguments
1952 of the form: "<path to emacs bins>/sh" "-c" "<path to emacs
1953 bins>/<command> <command args>" */
1954 j
= strlen (argv
[0]);
1955 if (j
>= 3 && strcmp (argv
[0]+j
-3, "/sh") == 0
1956 && argc
== 3 && strcmp (argv
[1], "-c") == 0)
1958 char *command
, *t
, tempmacpathname
[MAXPATHLEN
+1];
1960 /* The arguments for the command in argv[2] are separated by
1961 spaces. Count them and put the count in newargc. */
1962 command
= (char *) alloca (strlen (argv
[2])+2);
1963 strcpy (command
, argv
[2]);
1964 if (command
[strlen (command
) - 1] != ' ')
1965 strcat (command
, " ");
1969 t
= mystrchr (t
, ' ');
1973 t
= mystrchr (t
+1, ' ');
1976 newargv
= (char **) alloca (sizeof (char *) * newargc
);
1979 for (j
= 0; j
< newargc
; j
++)
1981 newargv
[j
] = (char *) alloca (strlen (t
) + 1);
1982 mystrcpy (newargv
[j
], t
);
1985 paramlen
+= strlen (newargv
[j
]) + 1;
1988 if (strncmp (newargv
[0], "~emacs/", 7) == 0)
1990 if (unix_to_mac_pathname (newargv
[0], tempmacpathname
, MAXPATHLEN
+1)
1995 { /* sometimes Emacs call "sh" without a path for the command */
1997 char *t
= (char *) alloca (strlen (newargv
[0]) + 7 + 1);
1998 strcpy (t
, "~emacs/");
1999 strcat (t
, newargv
[0]);
2002 openp (Vexec_path
, build_string (newargv
[0]), EXEC_SUFFIXES
, &path
,
2007 if (unix_to_mac_pathname (XSTRING (path
)->data
, tempmacpathname
,
2011 strcpy (macappname
, tempmacpathname
);
2015 if (unix_to_mac_pathname (argv
[0], macappname
, MAXPATHLEN
+1) == 0)
2018 newargv
= (char **) alloca (sizeof (char *) * argc
);
2020 for (j
= 1; j
< argc
; j
++)
2022 if (strncmp (argv
[j
], "~emacs/", 7) == 0)
2024 char *t
= strchr (argv
[j
], ' ');
2027 char tempcmdname
[MAXPATHLEN
+1], tempmaccmdname
[MAXPATHLEN
+1];
2028 strncpy (tempcmdname
, argv
[j
], t
-argv
[j
]);
2029 tempcmdname
[t
-argv
[j
]] = '\0';
2030 if (unix_to_mac_pathname (tempcmdname
, tempmaccmdname
,
2033 newargv
[j
] = (char *) alloca (strlen (tempmaccmdname
)
2035 strcpy (newargv
[j
], tempmaccmdname
);
2036 strcat (newargv
[j
], t
);
2040 char tempmaccmdname
[MAXPATHLEN
+1];
2041 if (unix_to_mac_pathname (argv
[j
], tempmaccmdname
,
2044 newargv
[j
] = (char *) alloca (strlen (tempmaccmdname
)+1);
2045 strcpy (newargv
[j
], tempmaccmdname
);
2049 newargv
[j
] = argv
[j
];
2050 paramlen
+= strlen (newargv
[j
]) + 1;
2054 /* After expanding all the arguments, we now know the length of the
2055 parameter block to be sent to the subprocess as a message
2056 attached to the HLE. */
2057 param
= (char *) malloc (paramlen
+ 1);
2063 /* first byte of message contains number of arguments for command */
2064 strcpy (p
, macworkdir
);
2065 p
+= strlen (macworkdir
);
2067 /* null terminate strings sent so it's possible to use strcpy over there */
2068 strcpy (p
, macinfn
);
2069 p
+= strlen (macinfn
);
2071 strcpy (p
, macoutfn
);
2072 p
+= strlen (macoutfn
);
2074 strcpy (p
, macerrfn
);
2075 p
+= strlen (macerrfn
);
2077 for (j
= 1; j
< newargc
; j
++)
2079 strcpy (p
, newargv
[j
]);
2080 p
+= strlen (newargv
[j
]);
2084 c2pstr (macappname
);
2086 iErr
= FSMakeFSSpec (0, 0, macappname
, &spec
);
2094 lpbr
.launchBlockID
= extendedBlock
;
2095 lpbr
.launchEPBLength
= extendedBlockLen
;
2096 lpbr
.launchControlFlags
= launchContinue
+ launchNoFileFlags
;
2097 lpbr
.launchAppSpec
= &spec
;
2098 lpbr
.launchAppParameters
= NULL
;
2100 iErr
= LaunchApplication (&lpbr
); /* call the subprocess */
2107 send_event
.what
= kHighLevelEvent
;
2108 send_event
.message
= kEmacsSubprocessSend
;
2109 /* Event ID stored in "where" unused */
2112 /* OS may think current subprocess has terminated if previous one
2113 terminated recently. */
2116 iErr
= PostHighLevelEvent (&send_event
, &lpbr
.launchProcessSN
, 0, param
,
2117 paramlen
+ 1, receiverIDisPSN
);
2119 while (iErr
== sessClosedErr
&& retries
-- > 0);
2127 cursor_region_handle
= NewRgn ();
2129 /* Wait for the subprocess to finish, when it will send us a ERPY
2130 high level event. */
2132 if (WaitNextEvent (highLevelEventMask
, &reply_event
, 180,
2133 cursor_region_handle
)
2134 && reply_event
.message
== kEmacsSubprocessReply
)
2137 /* The return code is sent through the refCon */
2138 iErr
= AcceptHighLevelEvent (&targ
, &ref_con
, NULL
, &len
);
2141 DisposeHandle ((Handle
) cursor_region_handle
);
2146 DisposeHandle ((Handle
) cursor_region_handle
);
2154 opendir (const char *dirname
)
2156 char true_pathname
[MAXPATHLEN
+1], fully_resolved_name
[MAXPATHLEN
+1];
2157 char mac_pathname
[MAXPATHLEN
+1], vol_name
[MAXPATHLEN
+1];
2161 int len
, vol_name_len
;
2163 if (find_true_pathname (dirname
, true_pathname
, MAXPATHLEN
+1) == -1)
2166 len
= readlink (true_pathname
, fully_resolved_name
, MAXPATHLEN
);
2168 fully_resolved_name
[len
] = '\0';
2170 strcpy (fully_resolved_name
, true_pathname
);
2172 dirp
= (DIR *) malloc (sizeof(DIR));
2176 /* Handle special case when dirname is "/": sets up for readir to
2177 get all mount volumes. */
2178 if (strcmp (fully_resolved_name
, "/") == 0)
2180 dirp
->getting_volumes
= 1; /* special all mounted volumes DIR struct */
2181 dirp
->current_index
= 1; /* index for first volume */
2185 /* Handle typical cases: not accessing all mounted volumes. */
2186 if (!unix_to_mac_pathname (fully_resolved_name
, mac_pathname
, MAXPATHLEN
+1))
2189 /* Emacs calls opendir without the trailing '/', Mac needs trailing ':' */
2190 len
= strlen (mac_pathname
);
2191 if (mac_pathname
[len
- 1] != ':' && len
< MAXPATHLEN
)
2192 strcat (mac_pathname
, ":");
2194 /* Extract volume name */
2195 vol_name_len
= strchr (mac_pathname
, ':') - mac_pathname
;
2196 strncpy (vol_name
, mac_pathname
, vol_name_len
);
2197 vol_name
[vol_name_len
] = '\0';
2198 strcat (vol_name
, ":");
2200 c2pstr (mac_pathname
);
2201 cipb
.hFileInfo
.ioNamePtr
= mac_pathname
;
2202 /* using full pathname so vRefNum and DirID ignored */
2203 cipb
.hFileInfo
.ioVRefNum
= 0;
2204 cipb
.hFileInfo
.ioDirID
= 0;
2205 cipb
.hFileInfo
.ioFDirIndex
= 0;
2206 /* set to 0 to get information about specific dir or file */
2208 errno
= PBGetCatInfo (&cipb
, false);
2215 if (!(cipb
.hFileInfo
.ioFlAttrib
& 0x10)) /* bit 4 = 1 for directories */
2216 return 0; /* not a directory */
2218 dirp
->dir_id
= cipb
.dirInfo
.ioDrDirID
; /* used later in readdir */
2219 dirp
->getting_volumes
= 0;
2220 dirp
->current_index
= 1; /* index for first file/directory */
2223 vpb
.ioNamePtr
= vol_name
;
2224 /* using full pathname so vRefNum and DirID ignored */
2226 vpb
.ioVolIndex
= -1;
2227 errno
= PBHGetVInfo ((union HParamBlockRec
*) &vpb
, false);
2234 dirp
->vol_ref_num
= vpb
.ioVRefNum
;
2251 HParamBlockRec hpblock
;
2253 static struct dirent s_dirent
;
2254 static Str255 s_name
;
2258 /* Handle the root directory containing the mounted volumes. Call
2259 PBHGetVInfo specifying an index to obtain the info for a volume.
2260 PBHGetVInfo returns an error when it receives an index beyond the
2261 last volume, at which time we should return a nil dirent struct
2263 if (dp
->getting_volumes
)
2265 hpblock
.volumeParam
.ioNamePtr
= s_name
;
2266 hpblock
.volumeParam
.ioVRefNum
= 0;
2267 hpblock
.volumeParam
.ioVolIndex
= dp
->current_index
;
2269 errno
= PBHGetVInfo (&hpblock
, false);
2277 strcat (s_name
, "/"); /* need "/" for stat to work correctly */
2279 dp
->current_index
++;
2281 s_dirent
.d_ino
= hpblock
.volumeParam
.ioVRefNum
;
2282 s_dirent
.d_name
= s_name
;
2288 cipb
.hFileInfo
.ioVRefNum
= dp
->vol_ref_num
;
2289 cipb
.hFileInfo
.ioNamePtr
= s_name
;
2290 /* location to receive filename returned */
2292 /* return only visible files */
2296 cipb
.hFileInfo
.ioDirID
= dp
->dir_id
;
2297 /* directory ID found by opendir */
2298 cipb
.hFileInfo
.ioFDirIndex
= dp
->current_index
;
2300 errno
= PBGetCatInfo (&cipb
, false);
2307 /* insist on an visibile entry */
2308 if (cipb
.hFileInfo
.ioFlAttrib
& 0x10) /* directory? */
2309 done
= !(cipb
.dirInfo
.ioDrUsrWds
.frFlags
& fInvisible
);
2311 done
= !(cipb
.hFileInfo
.ioFlFndrInfo
.fdFlags
& fInvisible
);
2313 dp
->current_index
++;
2326 s_dirent
.d_ino
= cipb
.dirInfo
.ioDrDirID
;
2327 /* value unimportant: non-zero for valid file */
2328 s_dirent
.d_name
= s_name
;
2338 char mac_pathname
[MAXPATHLEN
+1];
2339 Str255 directory_name
;
2343 if (path_from_vol_dir_name (mac_pathname
, 255, 0, 0, "\p") == 0)
2346 if (mac_to_unix_pathname (mac_pathname
, path
, MAXPATHLEN
+1) == 0)
2354 initialize_applescript ()
2359 /* if open fails, as_scripting_component is set to NULL. Its
2360 subsequent use in OSA calls will fail with badComponentInstance
2362 as_scripting_component
= OpenDefaultComponent (kOSAComponentType
,
2363 kAppleScriptSubtype
);
2365 null_desc
.descriptorType
= typeNull
;
2366 null_desc
.dataHandle
= 0;
2367 osaerror
= OSAMakeContext (as_scripting_component
, &null_desc
,
2368 kOSANullScript
, &as_script_context
);
2370 as_script_context
= kOSANullScript
;
2371 /* use default context if create fails */
2375 void terminate_applescript()
2377 OSADispose (as_scripting_component
, as_script_context
);
2378 CloseComponent (as_scripting_component
);
2382 /* Compile and execute the AppleScript SCRIPT and return the error
2383 status as function value. A zero is returned if compilation and
2384 execution is successful, in which case RESULT returns a pointer to
2385 a string containing the resulting script value. Otherwise, the Mac
2386 error code is returned and RESULT returns a pointer to an error
2387 string. In both cases the caller should deallocate the storage
2388 used by the string pointed to by RESULT if it is non-NULL. For
2389 documentation on the MacOS scripting architecture, see Inside
2390 Macintosh - Interapplication Communications: Scripting Components. */
2393 do_applescript (char *script
, char **result
)
2395 AEDesc script_desc
, result_desc
, error_desc
;
2402 error
= AECreateDesc (typeChar
, script
, strlen(script
), &script_desc
);
2406 osaerror
= OSADoScript (as_scripting_component
, &script_desc
, kOSANullScript
,
2407 typeChar
, kOSAModeNull
, &result_desc
);
2409 if (osaerror
== errOSAScriptError
)
2411 /* error executing AppleScript: retrieve error message */
2412 if (!OSAScriptError (as_scripting_component
, kOSAErrorMessage
, typeChar
,
2415 HLock (error_desc
.dataHandle
);
2416 length
= GetHandleSize(error_desc
.dataHandle
);
2417 *result
= (char *) xmalloc (length
+ 1);
2420 memcpy (*result
, *(error_desc
.dataHandle
), length
);
2421 *(*result
+ length
) = '\0';
2423 HUnlock (error_desc
.dataHandle
);
2424 AEDisposeDesc (&error_desc
);
2427 else if (osaerror
== noErr
) /* success: retrieve resulting script value */
2429 HLock (result_desc
.dataHandle
);
2430 length
= GetHandleSize(result_desc
.dataHandle
);
2431 *result
= (char *) xmalloc (length
+ 1);
2434 memcpy (*result
, *(result_desc
.dataHandle
), length
);
2435 *(*result
+ length
) = '\0';
2437 HUnlock (result_desc
.dataHandle
);
2440 AEDisposeDesc (&script_desc
);
2441 AEDisposeDesc (&result_desc
);
2447 DEFUN ("do-applescript", Fdo_applescript
, Sdo_applescript
, 1, 1, 0,
2448 "Compile and execute AppleScript SCRIPT and retrieve and return the\n\
2449 result. If compilation and execution are successful, the resulting script\n\
2450 value is returned as a string. Otherwise the function aborts and\n\
2451 displays the error message returned by the AppleScript scripting\n\
2456 char *result
, *temp
;
2457 Lisp_Object lisp_result
;
2460 CHECK_STRING (script
, 0);
2462 status
= do_applescript (XSTRING (script
)->data
, &result
);
2466 error ("AppleScript error %ld", status
);
2469 /* Unfortunately only OSADoScript in do_applescript knows how
2470 how large the resulting script value or error message is
2471 going to be and therefore as caller memory must be
2472 deallocated here. It is necessary to free the error
2473 message before calling error to avoid a memory leak. */
2474 temp
= (char *) alloca (strlen (result
) + 1);
2475 strcpy (temp
, result
);
2482 lisp_result
= build_string (result
);
2489 DEFUN ("mac-filename-to-unix", Fmac_filename_to_unix
, Smac_filename_to_unix
, 1,
2491 "Convert Macintosh filename to Unix form.")
2493 Lisp_Object mac_filename
;
2495 char unix_filename
[MAXPATHLEN
+1];
2497 CHECK_STRING (mac_filename
, 0);
2499 if (mac_to_unix_pathname(XSTRING (mac_filename
)->data
, unix_filename
,
2501 return build_string (unix_filename
);
2507 DEFUN ("unix-filename-to-mac", Funix_filename_to_mac
, Sunix_filename_to_mac
, 1,
2509 "Convert Unix filename to Mac form.")
2511 Lisp_Object unix_filename
;
2513 char mac_filename
[MAXPATHLEN
+1];
2515 CHECK_STRING (unix_filename
, 0);
2517 if (unix_to_mac_pathname(XSTRING (unix_filename
)->data
, mac_filename
,
2519 return build_string (mac_filename
);
2525 /* set interprogram-paste-function to mac-paste-function in mac-win.el
2526 to enable Emacs to obtain the contents of the Mac clipboard. */
2527 DEFUN ("mac-paste-function", Fmac_paste_function
, Smac_paste_function
, 0, 0, 0,
2528 "Return the contents of the Mac clipboard as a string.")
2533 long scrap_offset
, rc
, i
;
2535 my_handle
= NewHandle (0); /* allocate 0-length data area */
2537 rc
= GetScrap (my_handle
, 'TEXT', &scrap_offset
);
2543 /* Emacs expects clipboard contents have Unix-style eol's */
2544 for (i
= 0; i
< rc
; i
++)
2545 if ((*my_handle
)[i
] == '\r')
2546 (*my_handle
)[i
] = '\n';
2548 value
= make_string (*my_handle
, rc
);
2550 HUnlock (my_handle
);
2552 DisposeHandle (my_handle
);
2558 /* set interprogram-cut-function to mac-cut-function in mac-win.el
2559 to enable Emacs to write the top of the kill-ring to the Mac clipboard. */
2560 DEFUN ("mac-cut-function", Fmac_cut_function
, Smac_cut_function
, 1, 2, 0,
2561 "Put the value of the string parameter to the Mac clipboard.")
2563 Lisp_Object value
, push
;
2568 /* fixme: ignore the push flag for now */
2570 CHECK_STRING (value
, 0);
2572 len
= XSTRING (value
)->size
;
2573 buf
= (char *) alloca (len
);
2574 bcopy(XSTRING (value
)->data
, buf
, len
);
2576 /* convert to Mac-style eol's before sending to clipboard */
2577 for (i
= 0; i
< len
; i
++)
2582 PutScrap (len
, 'TEXT', buf
);
2588 DEFUN ("x-selection-exists-p", Fx_selection_exists_p
, Sx_selection_exists_p
,
2590 "Whether there is an owner for the given X Selection.\n\
2591 The arg should be the name of the selection in question, typically one of\n\
2592 the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.\n\
2593 \(Those are literal upper-case symbol names, since that's what X expects.)\n\
2594 For convenience, the symbol nil is the same as `PRIMARY',\n\
2595 and t is the same as `SECONDARY'.")
2597 Lisp_Object selection
;
2599 CHECK_SYMBOL (selection
, 0);
2601 /* Return nil for PRIMARY and SECONDARY selections; for CLIPBOARD, check
2602 if the clipboard currently has valid text format contents. */
2604 if (EQ (selection
, QCLIPBOARD
))
2606 Lisp_Object val
= Qnil
;
2609 long rc
, scrap_offset
;
2611 my_handle
= NewHandle (0);
2613 rc
= GetScrap (my_handle
, 'TEXT', &scrap_offset
);
2617 DisposeHandle (my_handle
);
2628 QCLIPBOARD
= intern ("CLIPBOARD");
2629 staticpro (&QCLIPBOARD
);
2631 defsubr (&Smac_paste_function
);
2632 defsubr (&Smac_cut_function
);
2633 defsubr (&Sx_selection_exists_p
);
2635 defsubr (&Sdo_applescript
);
2636 defsubr (&Smac_filename_to_unix
);
2637 defsubr (&Sunix_filename_to_mac
);