*** empty log message ***
[emacs.git] / src / mac.c
blob99e0d44830ba06353efdb21cf3a20ed9870a8cfa
1 /* Unix emulation routines for GNU Emacs on the Mac OS.
2 Copyright (C) 2000, 2001 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
21 /* Contributed by Andrew Choi (akochoi@mac.com). */
23 #include <config.h>
25 #include <stdio.h>
26 #include <errno.h>
27 #include <time.h>
28 #include <utime.h>
29 #include <dirent.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <string.h>
33 #include <pwd.h>
34 #include <grp.h>
35 #include <sys/param.h>
36 #include <stdlib.h>
37 #include <fcntl.h>
38 #if __MWERKS__
39 #include <unistd.h>
40 #endif
42 #ifdef MAC_OSX
43 #undef mktime
44 #undef DEBUG
45 #undef free
46 #undef malloc
47 #undef realloc
48 #undef init_process
49 #include <Carbon/Carbon.h>
50 #undef mktime
51 #define mktime emacs_mktime
52 #undef free
53 #define free unexec_free
54 #undef malloc
55 #define malloc unexec_malloc
56 #undef realloc
57 #define realloc unexec_realloc
58 #undef init_process
59 #define init_process emacs_init_process
60 #else /* not MAC_OSX */
61 #include <Files.h>
62 #include <MacTypes.h>
63 #include <TextUtils.h>
64 #include <Folders.h>
65 #include <Resources.h>
66 #include <Aliases.h>
67 #include <FixMath.h>
68 #include <Timer.h>
69 #include <OSA.h>
70 #include <AppleScript.h>
71 #include <Scrap.h>
72 #endif /* not MAC_OSX */
74 #include "lisp.h"
75 #include "process.h"
76 #include "sysselect.h"
77 #include "systime.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. */
93 void
94 string_cat_and_replace (char *s1, const char *s2, int n, char a, char b)
96 int l1 = strlen (s1);
97 int l2 = strlen (s2);
98 char *p = s1 + l1;
99 int i;
101 strncat (s1, s2, n);
102 for (i = 0; i < l2; i++)
104 if (*p == a)
105 *p = b;
106 p++;
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;
125 strcpy (ufn, "");
127 if (*mfn == '\0')
128 return 1;
130 p = strchr (mfn, ':');
131 if (p != 0 && p != mfn) /* full pathname */
132 strcat (ufn, "/");
134 p = mfn;
135 if (*p == ':')
136 p++;
138 pe = mfn + strlen (mfn);
139 while (p < pe)
141 q = strchr (p, ':');
142 if (q)
144 if (q == p)
145 { /* two consecutive ':' */
146 if (strlen (ufn) + 3 >= ufnbuflen)
147 return 0;
148 strcat (ufn, "../");
150 else
152 if (strlen (ufn) + (q - p) + 1 >= ufnbuflen)
153 return 0;
154 string_cat_and_replace (ufn, p, q - p, '/', ':');
155 strcat (ufn, "/");
157 p = q + 1;
159 else
161 if (strlen (ufn) + (pe - p) >= ufnbuflen)
162 return 0;
163 string_cat_and_replace (ufn, p, pe - p, '/', ':');
164 /* no separator for last one */
165 p = pe;
169 return 1;
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];
185 strcpy (mfn, "");
187 if (*ufn == '\0')
188 return 1;
190 p = ufn;
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)
197 return 0;
198 strcpy (mfn, p+1);
199 strcat (mfn, ":");
200 return 1;
203 /* expand to emacs dir found by init_emacs_passwd_dir */
204 if (strncmp (p, "~emacs/", 7) == 0)
206 struct passwd *pw = getpwnam ("emacs");
207 p += 7;
208 if (strlen (pw->pw_dir) + strlen (p) > MAXPATHLEN)
209 return 0;
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 ();
218 p += 5;
219 if (strlen (t) + strlen (p) > MAXPATHLEN)
220 return 0;
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 */
227 strcat (mfn, ":");
229 if (*p == '/')
230 p++;
232 pe = p + strlen (p);
233 while (p < pe)
235 q = strchr (p, '/');
236 if (q)
238 if (q - p == 2 && *p == '.' && *(p+1) == '.')
240 if (strlen (mfn) + 1 >= mfnbuflen)
241 return 0;
242 strcat (mfn, ":");
244 else
246 if (strlen (mfn) + (q - p) + 1 >= mfnbuflen)
247 return 0;
248 string_cat_and_replace (mfn, p, q - p, ':', '/');
249 strcat (mfn, ":");
251 p = q + 1;
253 else
255 if (strlen (mfn) + (pe - p) >= mfnbuflen)
256 return 0;
257 string_cat_and_replace (mfn, p, pe - p, ':', '/');
258 p = pe;
262 return 1;
265 #if TARGET_API_MAC_CARBON
266 CFStringRef
267 cfstring_create_with_utf8_cstring (c_str)
268 const char *c_str;
270 CFStringRef str;
272 str = CFStringCreateWithCString (NULL, c_str, kCFStringEncodingUTF8);
273 if (str == NULL)
274 /* Failed to interpret as UTF 8. Fall back on Mac Roman. */
275 str = CFStringCreateWithCString (NULL, c_str, kCFStringEncodingMacRoman);
277 return str;
279 #endif
281 #ifndef MAC_OSX
283 /* The following functions with "sys_" prefix are stubs to Unix
284 functions that have already been implemented by CW or MPW. The
285 calls to them in Emacs source course are #define'd to call the sys_
286 versions by the header files s-mac.h. In these stubs pathnames are
287 converted between their Unix and Mac forms. */
290 /* Unix epoch is Jan 1, 1970 while Mac epoch is Jan 1, 1904: 66 years
291 + 17 leap days. These are for adjusting time values returned by
292 MacOS Toolbox functions. */
294 #define MAC_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
296 #ifdef __MWERKS__
297 #if __MSL__ < 0x6000
298 /* CW Pro 5 epoch is Jan 1, 1900 (aaarghhhhh!); remember, 1900 is not
299 a leap year! This is for adjusting time_t values returned by MSL
300 functions. */
301 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 70 + 17) * 24 * 60 * 60)
302 #else /* __MSL__ >= 0x6000 */
303 /* CW changes Pro 6 to follow Unix! */
304 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
305 #endif /* __MSL__ >= 0x6000 */
306 #elif __MRC__
307 /* MPW library functions follow Unix (confused?). */
308 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
309 #else /* not __MRC__ */
310 You lose!!!
311 #endif /* not __MRC__ */
314 /* Define our own stat function for both MrC and CW. The reason for
315 doing this: "stat" is both the name of a struct and function name:
316 can't use the same trick like that for sys_open, sys_close, etc. to
317 redirect Emacs's calls to our own version that converts Unix style
318 filenames to Mac style filename because all sorts of compilation
319 errors will be generated if stat is #define'd to be sys_stat. */
322 stat_noalias (const char *path, struct stat *buf)
324 char mac_pathname[MAXPATHLEN+1];
325 CInfoPBRec cipb;
327 if (posix_to_mac_pathname (path, mac_pathname, MAXPATHLEN+1) == 0)
328 return -1;
330 c2pstr (mac_pathname);
331 cipb.hFileInfo.ioNamePtr = mac_pathname;
332 cipb.hFileInfo.ioVRefNum = 0;
333 cipb.hFileInfo.ioDirID = 0;
334 cipb.hFileInfo.ioFDirIndex = 0;
335 /* set to 0 to get information about specific dir or file */
337 errno = PBGetCatInfo (&cipb, false);
338 if (errno == -43) /* -43: fnfErr defined in Errors.h */
339 errno = ENOENT;
340 if (errno != noErr)
341 return -1;
343 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* bit 4 = 1 for directories */
345 buf->st_mode = S_IFDIR | S_IREAD | S_IEXEC;
347 if (!(cipb.hFileInfo.ioFlAttrib & 0x1))
348 buf->st_mode |= S_IWRITE; /* bit 1 = 1 for locked files/directories */
349 buf->st_ino = cipb.dirInfo.ioDrDirID;
350 buf->st_dev = cipb.dirInfo.ioVRefNum;
351 buf->st_size = cipb.dirInfo.ioDrNmFls;
352 /* size of dir = number of files and dirs */
353 buf->st_atime
354 = buf->st_mtime
355 = cipb.dirInfo.ioDrMdDat - MAC_UNIX_EPOCH_DIFF;
356 buf->st_ctime = cipb.dirInfo.ioDrCrDat - MAC_UNIX_EPOCH_DIFF;
358 else
360 buf->st_mode = S_IFREG | S_IREAD;
361 if (!(cipb.hFileInfo.ioFlAttrib & 0x1))
362 buf->st_mode |= S_IWRITE; /* bit 1 = 1 for locked files/directories */
363 if (cipb.hFileInfo.ioFlFndrInfo.fdType == 'APPL')
364 buf->st_mode |= S_IEXEC;
365 buf->st_ino = cipb.hFileInfo.ioDirID;
366 buf->st_dev = cipb.hFileInfo.ioVRefNum;
367 buf->st_size = cipb.hFileInfo.ioFlLgLen;
368 buf->st_atime
369 = buf->st_mtime
370 = cipb.hFileInfo.ioFlMdDat - MAC_UNIX_EPOCH_DIFF;
371 buf->st_ctime = cipb.hFileInfo.ioFlCrDat - MAC_UNIX_EPOCH_DIFF;
374 if (cipb.hFileInfo.ioFlFndrInfo.fdFlags & 0x8000)
376 /* identify alias files as symlinks */
377 buf->st_mode &= ~S_IFREG;
378 buf->st_mode |= S_IFLNK;
381 buf->st_nlink = 1;
382 buf->st_uid = getuid ();
383 buf->st_gid = getgid ();
384 buf->st_rdev = 0;
386 return 0;
391 lstat (const char *path, struct stat *buf)
393 int result;
394 char true_pathname[MAXPATHLEN+1];
396 /* Try looking for the file without resolving aliases first. */
397 if ((result = stat_noalias (path, buf)) >= 0)
398 return result;
400 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
401 return -1;
403 return stat_noalias (true_pathname, buf);
408 stat (const char *path, struct stat *sb)
410 int result;
411 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
412 int len;
414 if ((result = stat_noalias (path, sb)) >= 0 &&
415 ! (sb->st_mode & S_IFLNK))
416 return result;
418 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
419 return -1;
421 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
422 if (len > -1)
424 fully_resolved_name[len] = '\0';
425 /* in fact our readlink terminates strings */
426 return lstat (fully_resolved_name, sb);
428 else
429 return lstat (true_pathname, sb);
433 #if __MRC__
434 /* CW defines fstat in stat.mac.c while MPW does not provide this
435 function. Without the information of how to get from a file
436 descriptor in MPW StdCLib to a Mac OS file spec, it should be hard
437 to implement this function. Fortunately, there is only one place
438 where this function is called in our configuration: in fileio.c,
439 where only the st_dev and st_ino fields are used to determine
440 whether two fildes point to different i-nodes to prevent copying
441 a file onto itself equal. What we have here probably needs
442 improvement. */
445 fstat (int fildes, struct stat *buf)
447 buf->st_dev = 0;
448 buf->st_ino = fildes;
449 buf->st_mode = S_IFREG; /* added by T.I. for the copy-file */
450 return 0; /* success */
452 #endif /* __MRC__ */
456 mkdir (const char *dirname, int mode)
458 #pragma unused(mode)
460 HFileParam hfpb;
461 char true_pathname[MAXPATHLEN+1], mac_pathname[MAXPATHLEN+1];
463 if (find_true_pathname (dirname, true_pathname, MAXPATHLEN+1) == -1)
464 return -1;
466 if (posix_to_mac_pathname (true_pathname, mac_pathname, MAXPATHLEN+1) == 0)
467 return -1;
469 c2pstr (mac_pathname);
470 hfpb.ioNamePtr = mac_pathname;
471 hfpb.ioVRefNum = 0; /* ignored unless name is invalid */
472 hfpb.ioDirID = 0; /* parent is the root */
474 errno = PBDirCreate ((HParmBlkPtr) &hfpb, false);
475 /* just return the Mac OSErr code for now */
476 return errno == noErr ? 0 : -1;
480 #undef rmdir
481 sys_rmdir (const char *dirname)
483 HFileParam hfpb;
484 char mac_pathname[MAXPATHLEN+1];
486 if (posix_to_mac_pathname (dirname, mac_pathname, MAXPATHLEN+1) == 0)
487 return -1;
489 c2pstr (mac_pathname);
490 hfpb.ioNamePtr = mac_pathname;
491 hfpb.ioVRefNum = 0; /* ignored unless name is invalid */
492 hfpb.ioDirID = 0; /* parent is the root */
494 errno = PBHDelete ((HParmBlkPtr) &hfpb, false);
495 return errno == noErr ? 0 : -1;
499 #ifdef __MRC__
500 /* No implementation yet. */
502 execvp (const char *path, ...)
504 return -1;
506 #endif /* __MRC__ */
510 utime (const char *path, const struct utimbuf *times)
512 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
513 int len;
514 char mac_pathname[MAXPATHLEN+1];
515 CInfoPBRec cipb;
517 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
518 return -1;
520 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
521 if (len > -1)
522 fully_resolved_name[len] = '\0';
523 else
524 strcpy (fully_resolved_name, true_pathname);
526 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
527 return -1;
529 c2pstr (mac_pathname);
530 cipb.hFileInfo.ioNamePtr = mac_pathname;
531 cipb.hFileInfo.ioVRefNum = 0;
532 cipb.hFileInfo.ioDirID = 0;
533 cipb.hFileInfo.ioFDirIndex = 0;
534 /* set to 0 to get information about specific dir or file */
536 errno = PBGetCatInfo (&cipb, false);
537 if (errno != noErr)
538 return -1;
540 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* bit 4 = 1 for directories */
542 if (times)
543 cipb.dirInfo.ioDrMdDat = times->modtime + MAC_UNIX_EPOCH_DIFF;
544 else
545 GetDateTime (&cipb.dirInfo.ioDrMdDat);
547 else
549 if (times)
550 cipb.hFileInfo.ioFlMdDat = times->modtime + MAC_UNIX_EPOCH_DIFF;
551 else
552 GetDateTime (&cipb.hFileInfo.ioFlMdDat);
555 errno = PBSetCatInfo (&cipb, false);
556 return errno == noErr ? 0 : -1;
560 #ifndef F_OK
561 #define F_OK 0
562 #endif
563 #ifndef X_OK
564 #define X_OK 1
565 #endif
566 #ifndef W_OK
567 #define W_OK 2
568 #endif
570 /* Like stat, but test for access mode in hfpb.ioFlAttrib */
572 access (const char *path, int mode)
574 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
575 int len;
576 char mac_pathname[MAXPATHLEN+1];
577 CInfoPBRec cipb;
579 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
580 return -1;
582 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
583 if (len > -1)
584 fully_resolved_name[len] = '\0';
585 else
586 strcpy (fully_resolved_name, true_pathname);
588 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
589 return -1;
591 c2pstr (mac_pathname);
592 cipb.hFileInfo.ioNamePtr = mac_pathname;
593 cipb.hFileInfo.ioVRefNum = 0;
594 cipb.hFileInfo.ioDirID = 0;
595 cipb.hFileInfo.ioFDirIndex = 0;
596 /* set to 0 to get information about specific dir or file */
598 errno = PBGetCatInfo (&cipb, false);
599 if (errno != noErr)
600 return -1;
602 if (mode == F_OK) /* got this far, file exists */
603 return 0;
605 if (mode & X_OK)
606 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* path refers to a directory */
607 return 0;
608 else
610 if (cipb.hFileInfo.ioFlFndrInfo.fdType == 'APPL')
611 return 0;
612 else
613 return -1;
616 if (mode & W_OK)
617 return (cipb.hFileInfo.ioFlAttrib & 0x1) ? -1 : 0;
618 /* don't allow if lock bit is on */
620 return -1;
624 #define DEV_NULL_FD 0x10000
626 #undef open
628 sys_open (const char *path, int oflag)
630 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
631 int len;
632 char mac_pathname[MAXPATHLEN+1];
634 if (strcmp (path, "/dev/null") == 0)
635 return DEV_NULL_FD; /* some bogus fd to be ignored in write */
637 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
638 return -1;
640 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
641 if (len > -1)
642 fully_resolved_name[len] = '\0';
643 else
644 strcpy (fully_resolved_name, true_pathname);
646 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
647 return -1;
648 else
650 #ifdef __MRC__
651 int res = open (mac_pathname, oflag);
652 /* if (oflag == O_WRONLY || oflag == O_RDWR) */
653 if (oflag & O_CREAT)
654 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
655 return res;
656 #else /* not __MRC__ */
657 return open (mac_pathname, oflag);
658 #endif /* not __MRC__ */
663 #undef creat
665 sys_creat (const char *path, mode_t mode)
667 char true_pathname[MAXPATHLEN+1];
668 int len;
669 char mac_pathname[MAXPATHLEN+1];
671 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
672 return -1;
674 if (!posix_to_mac_pathname (true_pathname, mac_pathname, MAXPATHLEN+1))
675 return -1;
676 else
678 #ifdef __MRC__
679 int result = creat (mac_pathname);
680 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
681 return result;
682 #else /* not __MRC__ */
683 return creat (mac_pathname, mode);
684 #endif /* not __MRC__ */
689 #undef unlink
691 sys_unlink (const char *path)
693 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
694 int len;
695 char mac_pathname[MAXPATHLEN+1];
697 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
698 return -1;
700 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
701 if (len > -1)
702 fully_resolved_name[len] = '\0';
703 else
704 strcpy (fully_resolved_name, true_pathname);
706 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
707 return -1;
708 else
709 return unlink (mac_pathname);
713 #undef read
715 sys_read (int fildes, char *buf, int count)
717 if (fildes == 0) /* this should not be used for console input */
718 return -1;
719 else
720 #if __MSL__ >= 0x6000
721 return _read (fildes, buf, count);
722 #else
723 return read (fildes, buf, count);
724 #endif
728 #undef write
730 sys_write (int fildes, const char *buf, int count)
732 if (fildes == DEV_NULL_FD)
733 return count;
734 else
735 #if __MSL__ >= 0x6000
736 return _write (fildes, buf, count);
737 #else
738 return write (fildes, buf, count);
739 #endif
743 #undef rename
745 sys_rename (const char * old_name, const char * new_name)
747 char true_old_pathname[MAXPATHLEN+1], true_new_pathname[MAXPATHLEN+1];
748 char fully_resolved_old_name[MAXPATHLEN+1];
749 int len;
750 char mac_old_name[MAXPATHLEN+1], mac_new_name[MAXPATHLEN+1];
752 if (find_true_pathname (old_name, true_old_pathname, MAXPATHLEN+1) == -1)
753 return -1;
755 len = readlink (true_old_pathname, fully_resolved_old_name, MAXPATHLEN);
756 if (len > -1)
757 fully_resolved_old_name[len] = '\0';
758 else
759 strcpy (fully_resolved_old_name, true_old_pathname);
761 if (find_true_pathname (new_name, true_new_pathname, MAXPATHLEN+1) == -1)
762 return -1;
764 if (strcmp (fully_resolved_old_name, true_new_pathname) == 0)
765 return 0;
767 if (!posix_to_mac_pathname (fully_resolved_old_name,
768 mac_old_name,
769 MAXPATHLEN+1))
770 return -1;
772 if (!posix_to_mac_pathname(true_new_pathname, mac_new_name, MAXPATHLEN+1))
773 return -1;
775 /* If a file with new_name already exists, rename deletes the old
776 file in Unix. CW version fails in these situation. So we add a
777 call to unlink here. */
778 (void) unlink (mac_new_name);
780 return rename (mac_old_name, mac_new_name);
784 #undef fopen
785 extern FILE *fopen (const char *name, const char *mode);
786 FILE *
787 sys_fopen (const char *name, const char *mode)
789 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
790 int len;
791 char mac_pathname[MAXPATHLEN+1];
793 if (find_true_pathname (name, true_pathname, MAXPATHLEN+1) == -1)
794 return 0;
796 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
797 if (len > -1)
798 fully_resolved_name[len] = '\0';
799 else
800 strcpy (fully_resolved_name, true_pathname);
802 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
803 return 0;
804 else
806 #ifdef __MRC__
807 if (mode[0] == 'w' || mode[0] == 'a')
808 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
809 #endif /* not __MRC__ */
810 return fopen (mac_pathname, mode);
815 #include <Events.h>
817 long target_ticks = 0;
819 #ifdef __MRC__
820 __sigfun alarm_signal_func = (__sigfun) 0;
821 #elif __MWERKS__
822 __signal_func_ptr alarm_signal_func = (__signal_func_ptr) 0;
823 #else /* not __MRC__ and not __MWERKS__ */
824 You lose!!!
825 #endif /* not __MRC__ and not __MWERKS__ */
828 /* These functions simulate SIG_ALRM. The stub for function signal
829 stores the signal handler function in alarm_signal_func if a
830 SIG_ALRM is encountered. check_alarm is called in XTread_socket,
831 which emacs calls periodically. A pending alarm is represented by
832 a non-zero target_ticks value. check_alarm calls the handler
833 function pointed to by alarm_signal_func if one has been set up and
834 an alarm is pending. */
836 void
837 check_alarm ()
839 if (target_ticks && TickCount () > target_ticks)
841 target_ticks = 0;
842 if (alarm_signal_func)
843 (*alarm_signal_func)(SIGALRM);
848 extern Boolean mac_wait_next_event (EventRecord *, UInt32, Boolean);
851 select (n, rfds, wfds, efds, timeout)
852 int n;
853 SELECT_TYPE *rfds;
854 SELECT_TYPE *wfds;
855 SELECT_TYPE *efds;
856 struct timeval *timeout;
858 #if TARGET_API_MAC_CARBON
859 return 1;
860 #else /* not TARGET_API_MAC_CARBON */
861 EventRecord e;
862 UInt32 sleep_time = EMACS_SECS (*timeout) * 60 +
863 ((EMACS_USECS (*timeout) * 60) / 1000000);
865 /* Can only handle wait for keyboard input. */
866 if (n > 1 || wfds || efds)
867 return -1;
869 /* Also return true if an event other than a keyDown has occurred.
870 This causes kbd_buffer_get_event in keyboard.c to call
871 read_avail_input which in turn calls XTread_socket to poll for
872 these events. Otherwise these never get processed except but a
873 very slow poll timer. */
874 if (FD_ISSET (0, rfds) && mac_wait_next_event (&e, sleep_time, false))
875 return 1;
877 return 0;
878 #endif /* not TARGET_API_MAC_CARBON */
882 /* Called in sys_select to wait for an alarm signal to arrive. */
885 pause ()
887 EventRecord e;
888 unsigned long tick;
890 if (!target_ticks) /* no alarm pending */
891 return -1;
893 if ((tick = TickCount ()) < target_ticks)
894 WaitNextEvent (0, &e, target_ticks - tick, NULL); /* Accept no event;
895 just wait. by T.I. */
897 target_ticks = 0;
898 if (alarm_signal_func)
899 (*alarm_signal_func)(SIGALRM);
901 return 0;
906 alarm (int seconds)
908 long remaining = target_ticks ? (TickCount () - target_ticks) / 60 : 0;
910 target_ticks = seconds ? TickCount () + 60 * seconds : 0;
912 return (remaining < 0) ? 0 : (unsigned int) remaining;
916 #undef signal
917 #ifdef __MRC__
918 extern __sigfun signal (int signal, __sigfun signal_func);
919 __sigfun
920 sys_signal (int signal_num, __sigfun signal_func)
921 #elif __MWERKS__
922 extern __signal_func_ptr signal (int signal, __signal_func_ptr signal_func);
923 __signal_func_ptr
924 sys_signal (int signal_num, __signal_func_ptr signal_func)
925 #else /* not __MRC__ and not __MWERKS__ */
926 You lose!!!
927 #endif /* not __MRC__ and not __MWERKS__ */
929 if (signal_num != SIGALRM)
930 return signal (signal_num, signal_func);
931 else
933 #ifdef __MRC__
934 __sigfun old_signal_func;
935 #elif __MWERKS__
936 __signal_func_ptr old_signal_func;
937 #else
938 You lose!!!
939 #endif
940 old_signal_func = alarm_signal_func;
941 alarm_signal_func = signal_func;
942 return old_signal_func;
947 /* gettimeofday should return the amount of time (in a timeval
948 structure) since midnight today. The toolbox function Microseconds
949 returns the number of microseconds (in a UnsignedWide value) since
950 the machine was booted. Also making this complicated is WideAdd,
951 WideSubtract, etc. take wide values. */
954 gettimeofday (tp)
955 struct timeval *tp;
957 static inited = 0;
958 static wide wall_clock_at_epoch, clicks_at_epoch;
959 UnsignedWide uw_microseconds;
960 wide w_microseconds;
961 time_t sys_time (time_t *);
963 /* If this function is called for the first time, record the number
964 of seconds since midnight and the number of microseconds since
965 boot at the time of this first call. */
966 if (!inited)
968 time_t systime;
969 inited = 1;
970 systime = sys_time (NULL);
971 /* Store microseconds since midnight in wall_clock_at_epoch. */
972 WideMultiply (systime, 1000000L, &wall_clock_at_epoch);
973 Microseconds (&uw_microseconds);
974 /* Store microseconds since boot in clicks_at_epoch. */
975 clicks_at_epoch.hi = uw_microseconds.hi;
976 clicks_at_epoch.lo = uw_microseconds.lo;
979 /* Get time since boot */
980 Microseconds (&uw_microseconds);
982 /* Convert to time since midnight*/
983 w_microseconds.hi = uw_microseconds.hi;
984 w_microseconds.lo = uw_microseconds.lo;
985 WideSubtract (&w_microseconds, &clicks_at_epoch);
986 WideAdd (&w_microseconds, &wall_clock_at_epoch);
987 tp->tv_sec = WideDivide (&w_microseconds, 1000000L, &tp->tv_usec);
989 return 0;
993 #ifdef __MRC__
994 unsigned int
995 sleep (unsigned int seconds)
997 unsigned long time_up;
998 EventRecord e;
1000 time_up = TickCount () + seconds * 60;
1001 while (TickCount () < time_up)
1003 /* Accept no event; just wait. by T.I. */
1004 WaitNextEvent (0, &e, 30, NULL);
1007 return (0);
1009 #endif /* __MRC__ */
1012 /* The time functions adjust time values according to the difference
1013 between the Unix and CW epoches. */
1015 #undef gmtime
1016 extern struct tm *gmtime (const time_t *);
1017 struct tm *
1018 sys_gmtime (const time_t *timer)
1020 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
1022 return gmtime (&unix_time);
1026 #undef localtime
1027 extern struct tm *localtime (const time_t *);
1028 struct tm *
1029 sys_localtime (const time_t *timer)
1031 #if __MSL__ >= 0x6000
1032 time_t unix_time = *timer;
1033 #else
1034 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
1035 #endif
1037 return localtime (&unix_time);
1041 #undef ctime
1042 extern char *ctime (const time_t *);
1043 char *
1044 sys_ctime (const time_t *timer)
1046 #if __MSL__ >= 0x6000
1047 time_t unix_time = *timer;
1048 #else
1049 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
1050 #endif
1052 return ctime (&unix_time);
1056 #undef time
1057 extern time_t time (time_t *);
1058 time_t
1059 sys_time (time_t *timer)
1061 #if __MSL__ >= 0x6000
1062 time_t mac_time = time (NULL);
1063 #else
1064 time_t mac_time = time (NULL) - CW_OR_MPW_UNIX_EPOCH_DIFF;
1065 #endif
1067 if (timer)
1068 *timer = mac_time;
1070 return mac_time;
1074 /* MPW strftime broken for "%p" format */
1075 #ifdef __MRC__
1076 #undef strftime
1077 #include <time.h>
1078 size_t
1079 sys_strftime (char * s, size_t maxsize, const char * format,
1080 const struct tm * timeptr)
1082 if (strcmp (format, "%p") == 0)
1084 if (maxsize < 3)
1085 return 0;
1086 if (timeptr->tm_hour < 12)
1088 strcpy (s, "AM");
1089 return 2;
1091 else
1093 strcpy (s, "PM");
1094 return 2;
1097 else
1098 return strftime (s, maxsize, format, timeptr);
1100 #endif /* __MRC__ */
1103 /* no subprocesses, empty wait */
1106 wait (int pid)
1108 return 0;
1112 void
1113 croak (char *badfunc)
1115 printf ("%s not yet implemented\r\n", badfunc);
1116 exit (1);
1120 char *
1121 index (const char * str, int chr)
1123 return strchr (str, chr);
1127 char *
1128 mktemp (char *template)
1130 int len, k;
1131 static seqnum = 0;
1133 len = strlen (template);
1134 k = len - 1;
1135 while (k >= 0 && template[k] == 'X')
1136 k--;
1138 k++; /* make k index of first 'X' */
1140 if (k < len)
1142 /* Zero filled, number of digits equal to the number of X's. */
1143 sprintf (&template[k], "%0*d", len-k, seqnum++);
1145 return template;
1147 else
1148 return 0;
1152 /* Emulate getpwuid, getpwnam and others. */
1154 #define PASSWD_FIELD_SIZE 256
1156 static char my_passwd_name[PASSWD_FIELD_SIZE];
1157 static char my_passwd_dir[MAXPATHLEN+1];
1159 static struct passwd my_passwd =
1161 my_passwd_name,
1162 my_passwd_dir,
1165 static struct group my_group =
1167 /* There are no groups on the mac, so we just return "root" as the
1168 group name. */
1169 "root",
1173 /* Initialized by main () in macterm.c to pathname of emacs directory. */
1175 char emacs_passwd_dir[MAXPATHLEN+1];
1177 char *
1178 getwd (char *);
1180 void
1181 init_emacs_passwd_dir ()
1183 int found = false;
1185 if (getwd (emacs_passwd_dir) && getwd (my_passwd_dir))
1187 /* Need pathname of first ancestor that begins with "emacs"
1188 since Mac emacs application is somewhere in the emacs-*
1189 tree. */
1190 int len = strlen (emacs_passwd_dir);
1191 int j = len - 1;
1192 /* j points to the "/" following the directory name being
1193 compared. */
1194 int i = j - 1;
1195 while (i >= 0 && !found)
1197 while (i >= 0 && emacs_passwd_dir[i] != '/')
1198 i--;
1199 if (emacs_passwd_dir[i] == '/' && i+5 < len)
1200 found = (strncmp (&(emacs_passwd_dir[i+1]), "emacs", 5) == 0);
1201 if (found)
1202 emacs_passwd_dir[j+1] = '\0';
1203 else
1205 j = i;
1206 i = j - 1;
1211 if (!found)
1213 /* Setting to "/" probably won't work but set it to something
1214 anyway. */
1215 strcpy (emacs_passwd_dir, "/");
1216 strcpy (my_passwd_dir, "/");
1221 static struct passwd emacs_passwd =
1223 "emacs",
1224 emacs_passwd_dir,
1227 static int my_passwd_inited = 0;
1230 static void
1231 init_my_passwd ()
1233 char **owner_name;
1235 /* Note: my_passwd_dir initialized in int_emacs_passwd_dir to
1236 directory where Emacs was started. */
1238 owner_name = (char **) GetResource ('STR ',-16096);
1239 if (owner_name)
1241 HLock (owner_name);
1242 BlockMove ((unsigned char *) *owner_name,
1243 (unsigned char *) my_passwd_name,
1244 *owner_name[0]+1);
1245 HUnlock (owner_name);
1246 p2cstr ((unsigned char *) my_passwd_name);
1248 else
1249 my_passwd_name[0] = 0;
1253 struct passwd *
1254 getpwuid (uid_t uid)
1256 if (!my_passwd_inited)
1258 init_my_passwd ();
1259 my_passwd_inited = 1;
1262 return &my_passwd;
1266 struct group *
1267 getgrgid (gid_t gid)
1269 return &my_group;
1273 struct passwd *
1274 getpwnam (const char *name)
1276 if (strcmp (name, "emacs") == 0)
1277 return &emacs_passwd;
1279 if (!my_passwd_inited)
1281 init_my_passwd ();
1282 my_passwd_inited = 1;
1285 return &my_passwd;
1289 /* The functions fork, kill, sigsetmask, sigblock, request_sigio,
1290 setpgrp, setpriority, and unrequest_sigio are defined to be empty
1291 as in msdos.c. */
1295 fork ()
1297 return -1;
1302 kill (int x, int y)
1304 return -1;
1308 void
1309 sys_subshell ()
1311 error ("Can't spawn subshell");
1316 sigsetmask (int x)
1318 return 0;
1323 sigblock (int mask)
1325 return 0;
1329 void
1330 request_sigio (void)
1335 void
1336 unrequest_sigio (void)
1342 setpgrp ()
1344 return 0;
1348 /* No pipes yet. */
1351 pipe (int _fildes[2])
1353 errno = EACCES;
1354 return -1;
1358 /* Hard and symbolic links. */
1361 symlink (const char *name1, const char *name2)
1363 errno = ENOENT;
1364 return -1;
1369 link (const char *name1, const char *name2)
1371 errno = ENOENT;
1372 return -1;
1375 #endif /* ! MAC_OSX */
1377 /* Determine the path name of the file specified by VREFNUM, DIRID,
1378 and NAME and place that in the buffer PATH of length
1379 MAXPATHLEN. */
1381 path_from_vol_dir_name (char *path, int man_path_len, short vol_ref_num,
1382 long dir_id, ConstStr255Param name)
1384 Str255 dir_name;
1385 CInfoPBRec cipb;
1386 OSErr err;
1388 if (strlen (name) > man_path_len)
1389 return 0;
1391 memcpy (dir_name, name, name[0]+1);
1392 memcpy (path, name, name[0]+1);
1393 p2cstr (path);
1395 cipb.dirInfo.ioDrParID = dir_id;
1396 cipb.dirInfo.ioNamePtr = dir_name;
1400 cipb.dirInfo.ioVRefNum = vol_ref_num;
1401 cipb.dirInfo.ioFDirIndex = -1;
1402 cipb.dirInfo.ioDrDirID = cipb.dirInfo.ioDrParID;
1403 /* go up to parent each time */
1405 err = PBGetCatInfo (&cipb, false);
1406 if (err != noErr)
1407 return 0;
1409 p2cstr (dir_name);
1410 if (strlen (dir_name) + strlen (path) + 1 >= man_path_len)
1411 return 0;
1413 strcat (dir_name, ":");
1414 strcat (dir_name, path);
1415 /* attach to front since we're going up directory tree */
1416 strcpy (path, dir_name);
1418 while (cipb.dirInfo.ioDrDirID != fsRtDirID);
1419 /* stop when we see the volume's root directory */
1421 return 1; /* success */
1424 #ifndef MAC_OSX
1427 readlink (const char *path, char *buf, int bufsiz)
1429 char mac_sym_link_name[MAXPATHLEN+1];
1430 OSErr err;
1431 FSSpec fsspec;
1432 Boolean target_is_folder, was_aliased;
1433 Str255 directory_name, mac_pathname;
1434 CInfoPBRec cipb;
1436 if (posix_to_mac_pathname (path, mac_sym_link_name, MAXPATHLEN+1) == 0)
1437 return -1;
1439 c2pstr (mac_sym_link_name);
1440 err = FSMakeFSSpec (0, 0, mac_sym_link_name, &fsspec);
1441 if (err != noErr)
1443 errno = ENOENT;
1444 return -1;
1447 err = ResolveAliasFile (&fsspec, true, &target_is_folder, &was_aliased);
1448 if (err != noErr || !was_aliased)
1450 errno = ENOENT;
1451 return -1;
1454 if (path_from_vol_dir_name (mac_pathname, 255, fsspec.vRefNum, fsspec.parID,
1455 fsspec.name) == 0)
1457 errno = ENOENT;
1458 return -1;
1461 if (mac_to_posix_pathname (mac_pathname, buf, bufsiz) == 0)
1463 errno = ENOENT;
1464 return -1;
1467 return strlen (buf);
1471 /* Convert a path to one with aliases fully expanded. */
1473 static int
1474 find_true_pathname (const char *path, char *buf, int bufsiz)
1476 char *q, temp[MAXPATHLEN+1];
1477 const char *p;
1478 int len;
1480 if (bufsiz <= 0 || path == 0 || path[0] == '\0')
1481 return -1;
1483 buf[0] = '\0';
1485 p = path;
1486 if (*p == '/')
1487 q = strchr (p + 1, '/');
1488 else
1489 q = strchr (p, '/');
1490 len = 0; /* loop may not be entered, e.g., for "/" */
1492 while (q)
1494 strcpy (temp, buf);
1495 strncat (temp, p, q - p);
1496 len = readlink (temp, buf, bufsiz);
1497 if (len <= -1)
1499 if (strlen (temp) + 1 > bufsiz)
1500 return -1;
1501 strcpy (buf, temp);
1503 strcat (buf, "/");
1504 len++;
1505 p = q + 1;
1506 q = strchr(p, '/');
1509 if (len + strlen (p) + 1 >= bufsiz)
1510 return -1;
1512 strcat (buf, p);
1513 return len + strlen (p);
1517 mode_t
1518 umask (mode_t numask)
1520 static mode_t mask = 022;
1521 mode_t oldmask = mask;
1522 mask = numask;
1523 return oldmask;
1528 chmod (const char *path, mode_t mode)
1530 /* say it always succeed for now */
1531 return 0;
1536 dup (int oldd)
1538 #ifdef __MRC__
1539 return fcntl (oldd, F_DUPFD, 0);
1540 #elif __MWERKS__
1541 /* current implementation of fcntl in fcntl.mac.c simply returns old
1542 descriptor */
1543 return fcntl (oldd, F_DUPFD);
1544 #else
1545 You lose!!!
1546 #endif
1550 /* This is from the original sysdep.c. Emulate BSD dup2. First close
1551 newd if it already exists. Then, attempt to dup oldd. If not
1552 successful, call dup2 recursively until we are, then close the
1553 unsuccessful ones. */
1556 dup2 (int oldd, int newd)
1558 int fd, ret;
1560 close (newd);
1562 fd = dup (oldd);
1563 if (fd == -1)
1564 return -1;
1565 if (fd == newd)
1566 return newd;
1567 ret = dup2 (oldd, newd);
1568 close (fd);
1569 return ret;
1573 /* let it fail for now */
1575 char *
1576 sbrk (int incr)
1578 return (char *) -1;
1583 fsync (int fd)
1585 return 0;
1590 ioctl (int d, int request, void *argp)
1592 return -1;
1596 #ifdef __MRC__
1598 isatty (int fildes)
1600 if (fildes >=0 && fildes <= 2)
1601 return 1;
1602 else
1603 return 0;
1608 getgid ()
1610 return 100;
1615 getegid ()
1617 return 100;
1622 getuid ()
1624 return 200;
1629 geteuid ()
1631 return 200;
1633 #endif /* __MRC__ */
1636 #ifdef __MWERKS__
1637 #if __MSL__ < 0x6000
1638 #undef getpid
1640 getpid ()
1642 return 9999;
1644 #endif
1645 #endif /* __MWERKS__ */
1647 #endif /* ! MAC_OSX */
1650 /* Return the path to the directory in which Emacs can create
1651 temporary files. The MacOS "temporary items" directory cannot be
1652 used because it removes the file written by a process when it
1653 exits. In that sense it's more like "/dev/null" than "/tmp" (but
1654 again not exactly). And of course Emacs needs to read back the
1655 files written by its subprocesses. So here we write the files to a
1656 directory "Emacs" in the Preferences Folder. This directory is
1657 created if it does not exist. */
1659 char *
1660 get_temp_dir_name ()
1662 static char *temp_dir_name = NULL;
1663 short vol_ref_num;
1664 long dir_id;
1665 OSErr err;
1666 Str255 dir_name, full_path;
1667 CInfoPBRec cpb;
1668 char unix_dir_name[MAXPATHLEN+1];
1669 DIR *dir;
1671 /* Cache directory name with pointer temp_dir_name.
1672 Look for it only the first time. */
1673 if (!temp_dir_name)
1675 err = FindFolder (kOnSystemDisk, kPreferencesFolderType, kCreateFolder,
1676 &vol_ref_num, &dir_id);
1677 if (err != noErr)
1678 return NULL;
1680 if (!path_from_vol_dir_name (full_path, 255, vol_ref_num, dir_id, "\p"))
1681 return NULL;
1683 if (strlen (full_path) + 6 <= MAXPATHLEN)
1684 strcat (full_path, "Emacs:");
1685 else
1686 return NULL;
1688 if (!mac_to_posix_pathname (full_path, unix_dir_name, MAXPATHLEN+1))
1689 return NULL;
1691 dir = opendir (unix_dir_name); /* check whether temp directory exists */
1692 if (dir)
1693 closedir (dir);
1694 else if (mkdir (unix_dir_name, 0700) != 0) /* create it if not */
1695 return NULL;
1697 temp_dir_name = (char *) malloc (strlen (unix_dir_name) + 1);
1698 strcpy (temp_dir_name, unix_dir_name);
1701 return temp_dir_name;
1704 #ifndef MAC_OSX
1706 /* Allocate and construct an array of pointers to strings from a list
1707 of strings stored in a 'STR#' resource. The returned pointer array
1708 is stored in the style of argv and environ: if the 'STR#' resource
1709 contains numString strings, a pointer array with numString+1
1710 elements is returned in which the last entry contains a null
1711 pointer. The pointer to the pointer array is passed by pointer in
1712 parameter t. The resource ID of the 'STR#' resource is passed in
1713 parameter StringListID.
1716 void
1717 get_string_list (char ***t, short string_list_id)
1719 Handle h;
1720 Ptr p;
1721 int i, num_strings;
1723 h = GetResource ('STR#', string_list_id);
1724 if (h)
1726 HLock (h);
1727 p = *h;
1728 num_strings = * (short *) p;
1729 p += sizeof(short);
1730 *t = (char **) malloc (sizeof (char *) * (num_strings + 1));
1731 for (i = 0; i < num_strings; i++)
1733 short length = *p++;
1734 (*t)[i] = (char *) malloc (length + 1);
1735 strncpy ((*t)[i], p, length);
1736 (*t)[i][length] = '\0';
1737 p += length;
1739 (*t)[num_strings] = 0;
1740 HUnlock (h);
1742 else
1744 /* Return no string in case GetResource fails. Bug fixed by
1745 Ikegami Tsutomu. Caused MPW build to crash without sym -on
1746 option (no sym -on implies -opt local). */
1747 *t = (char **) malloc (sizeof (char *));
1748 (*t)[0] = 0;
1753 static char *
1754 get_path_to_system_folder ()
1756 short vol_ref_num;
1757 long dir_id;
1758 OSErr err;
1759 Str255 dir_name, full_path;
1760 CInfoPBRec cpb;
1761 static char system_folder_unix_name[MAXPATHLEN+1];
1762 DIR *dir;
1764 err = FindFolder (kOnSystemDisk, kSystemFolderType, kDontCreateFolder,
1765 &vol_ref_num, &dir_id);
1766 if (err != noErr)
1767 return NULL;
1769 if (!path_from_vol_dir_name (full_path, 255, vol_ref_num, dir_id, "\p"))
1770 return NULL;
1772 if (!mac_to_posix_pathname (full_path, system_folder_unix_name,
1773 MAXPATHLEN+1))
1774 return NULL;
1776 return system_folder_unix_name;
1780 char **environ;
1782 #define ENVIRON_STRING_LIST_ID 128
1784 /* Get environment variable definitions from STR# resource. */
1786 void
1787 init_environ ()
1789 int i;
1791 get_string_list (&environ, ENVIRON_STRING_LIST_ID);
1793 i = 0;
1794 while (environ[i])
1795 i++;
1797 /* Make HOME directory the one Emacs starts up in if not specified
1798 by resource. */
1799 if (getenv ("HOME") == NULL)
1801 environ = (char **) realloc (environ, sizeof (char *) * (i + 2));
1802 if (environ)
1804 environ[i] = (char *) malloc (strlen (my_passwd_dir) + 6);
1805 if (environ[i])
1807 strcpy (environ[i], "HOME=");
1808 strcat (environ[i], my_passwd_dir);
1810 environ[i+1] = 0;
1811 i++;
1815 /* Make HOME directory the one Emacs starts up in if not specified
1816 by resource. */
1817 if (getenv ("MAIL") == NULL)
1819 environ = (char **) realloc (environ, sizeof (char *) * (i + 2));
1820 if (environ)
1822 char * path_to_system_folder = get_path_to_system_folder ();
1823 environ[i] = (char *) malloc (strlen (path_to_system_folder) + 22);
1824 if (environ[i])
1826 strcpy (environ[i], "MAIL=");
1827 strcat (environ[i], path_to_system_folder);
1828 strcat (environ[i], "Eudora Folder/In");
1830 environ[i+1] = 0;
1836 /* Return the value of the environment variable NAME. */
1838 char *
1839 getenv (const char *name)
1841 int length = strlen(name);
1842 char **e;
1844 for (e = environ; *e != 0; e++)
1845 if (strncmp(*e, name, length) == 0 && (*e)[length] == '=')
1846 return &(*e)[length + 1];
1848 if (strcmp (name, "TMPDIR") == 0)
1849 return get_temp_dir_name ();
1851 return 0;
1855 #ifdef __MRC__
1856 /* see Interfaces&Libraries:Interfaces:CIncludes:signal.h */
1857 char *sys_siglist[] =
1859 "Zero is not a signal!!!",
1860 "Abort", /* 1 */
1861 "Interactive user interrupt", /* 2 */ "?",
1862 "Floating point exception", /* 4 */ "?", "?", "?",
1863 "Illegal instruction", /* 8 */ "?", "?", "?", "?", "?", "?", "?",
1864 "Segment violation", /* 16 */ "?", "?", "?", "?", "?", "?", "?",
1865 "?", "?", "?", "?", "?", "?", "?", "?",
1866 "Terminal" /* 32 */
1868 #elif __MWERKS__
1869 char *sys_siglist[] =
1871 "Zero is not a signal!!!",
1872 "Abort",
1873 "Floating point exception",
1874 "Illegal instruction",
1875 "Interactive user interrupt",
1876 "Segment violation",
1877 "Terminal"
1879 #else /* not __MRC__ and not __MWERKS__ */
1880 You lose!!!
1881 #endif /* not __MRC__ and not __MWERKS__ */
1884 #include <utsname.h>
1887 uname (struct utsname *name)
1889 char **system_name;
1890 system_name = GetString (-16413); /* IM - Resource Manager Reference */
1891 if (system_name)
1893 BlockMove (*system_name, name->nodename, (*system_name)[0]+1);
1894 p2cstr (name->nodename);
1895 return 0;
1897 else
1898 return -1;
1902 #include <Processes.h>
1903 #include <EPPC.h>
1905 /* Event class of HLE sent to subprocess. */
1906 const OSType kEmacsSubprocessSend = 'ESND';
1908 /* Event class of HLE sent back from subprocess. */
1909 const OSType kEmacsSubprocessReply = 'ERPY';
1912 char *
1913 mystrchr (char *s, char c)
1915 while (*s && *s != c)
1917 if (*s == '\\')
1918 s++;
1919 s++;
1922 if (*s)
1924 *s = '\0';
1925 return s;
1927 else
1928 return NULL;
1932 char *
1933 mystrtok (char *s)
1935 while (*s)
1936 s++;
1938 return s + 1;
1942 void
1943 mystrcpy (char *to, char *from)
1945 while (*from)
1947 if (*from == '\\')
1948 from++;
1949 *to++ = *from++;
1951 *to = '\0';
1955 /* Start a Mac subprocess. Arguments for it is passed in argv (null
1956 terminated). The process should run with the default directory
1957 "workdir", read input from "infn", and write output and error to
1958 "outfn" and "errfn", resp. The Process Manager call
1959 LaunchApplication is used to start the subprocess. We use high
1960 level events as the mechanism to pass arguments to the subprocess
1961 and to make Emacs wait for the subprocess to terminate and pass
1962 back a result code. The bulk of the code here packs the arguments
1963 into one message to be passed together with the high level event.
1964 Emacs also sometimes starts a subprocess using a shell to perform
1965 wildcard filename expansion. Since we don't really have a shell on
1966 the Mac, this case is detected and the starting of the shell is
1967 by-passed. We really need to add code here to do filename
1968 expansion to support such functionality. */
1971 run_mac_command (argv, workdir, infn, outfn, errfn)
1972 unsigned char **argv;
1973 const char *workdir;
1974 const char *infn, *outfn, *errfn;
1976 #if TARGET_API_MAC_CARBON
1977 return -1;
1978 #else /* not TARGET_API_MAC_CARBON */
1979 char macappname[MAXPATHLEN+1], macworkdir[MAXPATHLEN+1];
1980 char macinfn[MAXPATHLEN+1], macoutfn[MAXPATHLEN+1], macerrfn[MAXPATHLEN+1];
1981 int paramlen, argc, newargc, j, retries;
1982 char **newargv, *param, *p;
1983 OSErr iErr;
1984 FSSpec spec;
1985 LaunchParamBlockRec lpbr;
1986 EventRecord send_event, reply_event;
1987 RgnHandle cursor_region_handle;
1988 TargetID targ;
1989 unsigned long ref_con, len;
1991 if (posix_to_mac_pathname (workdir, macworkdir, MAXPATHLEN+1) == 0)
1992 return -1;
1993 if (posix_to_mac_pathname (infn, macinfn, MAXPATHLEN+1) == 0)
1994 return -1;
1995 if (posix_to_mac_pathname (outfn, macoutfn, MAXPATHLEN+1) == 0)
1996 return -1;
1997 if (posix_to_mac_pathname (errfn, macerrfn, MAXPATHLEN+1) == 0)
1998 return -1;
2000 paramlen = strlen (macworkdir) + strlen (macinfn) + strlen (macoutfn)
2001 + strlen (macerrfn) + 4; /* count nulls at end of strings */
2003 argc = 0;
2004 while (argv[argc])
2005 argc++;
2007 if (argc == 0)
2008 return -1;
2010 /* If a subprocess is invoked with a shell, we receive 3 arguments
2011 of the form: "<path to emacs bins>/sh" "-c" "<path to emacs
2012 bins>/<command> <command args>" */
2013 j = strlen (argv[0]);
2014 if (j >= 3 && strcmp (argv[0]+j-3, "/sh") == 0
2015 && argc == 3 && strcmp (argv[1], "-c") == 0)
2017 char *command, *t, tempmacpathname[MAXPATHLEN+1];
2019 /* The arguments for the command in argv[2] are separated by
2020 spaces. Count them and put the count in newargc. */
2021 command = (char *) alloca (strlen (argv[2])+2);
2022 strcpy (command, argv[2]);
2023 if (command[strlen (command) - 1] != ' ')
2024 strcat (command, " ");
2026 t = command;
2027 newargc = 0;
2028 t = mystrchr (t, ' ');
2029 while (t)
2031 newargc++;
2032 t = mystrchr (t+1, ' ');
2035 newargv = (char **) alloca (sizeof (char *) * newargc);
2037 t = command;
2038 for (j = 0; j < newargc; j++)
2040 newargv[j] = (char *) alloca (strlen (t) + 1);
2041 mystrcpy (newargv[j], t);
2043 t = mystrtok (t);
2044 paramlen += strlen (newargv[j]) + 1;
2047 if (strncmp (newargv[0], "~emacs/", 7) == 0)
2049 if (posix_to_mac_pathname (newargv[0], tempmacpathname, MAXPATHLEN+1)
2050 == 0)
2051 return -1;
2053 else
2054 { /* sometimes Emacs call "sh" without a path for the command */
2055 #if 0
2056 char *t = (char *) alloca (strlen (newargv[0]) + 7 + 1);
2057 strcpy (t, "~emacs/");
2058 strcat (t, newargv[0]);
2059 #endif /* 0 */
2060 Lisp_Object path;
2061 openp (Vexec_path, build_string (newargv[0]), Vexec_suffixes, &path,
2062 make_number (X_OK));
2064 if (NILP (path))
2065 return -1;
2066 if (posix_to_mac_pathname (SDATA (path), tempmacpathname,
2067 MAXPATHLEN+1) == 0)
2068 return -1;
2070 strcpy (macappname, tempmacpathname);
2072 else
2074 if (posix_to_mac_pathname (argv[0], macappname, MAXPATHLEN+1) == 0)
2075 return -1;
2077 newargv = (char **) alloca (sizeof (char *) * argc);
2078 newargc = argc;
2079 for (j = 1; j < argc; j++)
2081 if (strncmp (argv[j], "~emacs/", 7) == 0)
2083 char *t = strchr (argv[j], ' ');
2084 if (t)
2086 char tempcmdname[MAXPATHLEN+1], tempmaccmdname[MAXPATHLEN+1];
2087 strncpy (tempcmdname, argv[j], t-argv[j]);
2088 tempcmdname[t-argv[j]] = '\0';
2089 if (posix_to_mac_pathname (tempcmdname, tempmaccmdname,
2090 MAXPATHLEN+1) == 0)
2091 return -1;
2092 newargv[j] = (char *) alloca (strlen (tempmaccmdname)
2093 + strlen (t) + 1);
2094 strcpy (newargv[j], tempmaccmdname);
2095 strcat (newargv[j], t);
2097 else
2099 char tempmaccmdname[MAXPATHLEN+1];
2100 if (posix_to_mac_pathname (argv[j], tempmaccmdname,
2101 MAXPATHLEN+1) == 0)
2102 return -1;
2103 newargv[j] = (char *) alloca (strlen (tempmaccmdname)+1);
2104 strcpy (newargv[j], tempmaccmdname);
2107 else
2108 newargv[j] = argv[j];
2109 paramlen += strlen (newargv[j]) + 1;
2113 /* After expanding all the arguments, we now know the length of the
2114 parameter block to be sent to the subprocess as a message
2115 attached to the HLE. */
2116 param = (char *) malloc (paramlen + 1);
2117 if (!param)
2118 return -1;
2120 p = param;
2121 *p++ = newargc;
2122 /* first byte of message contains number of arguments for command */
2123 strcpy (p, macworkdir);
2124 p += strlen (macworkdir);
2125 *p++ = '\0';
2126 /* null terminate strings sent so it's possible to use strcpy over there */
2127 strcpy (p, macinfn);
2128 p += strlen (macinfn);
2129 *p++ = '\0';
2130 strcpy (p, macoutfn);
2131 p += strlen (macoutfn);
2132 *p++ = '\0';
2133 strcpy (p, macerrfn);
2134 p += strlen (macerrfn);
2135 *p++ = '\0';
2136 for (j = 1; j < newargc; j++)
2138 strcpy (p, newargv[j]);
2139 p += strlen (newargv[j]);
2140 *p++ = '\0';
2143 c2pstr (macappname);
2145 iErr = FSMakeFSSpec (0, 0, macappname, &spec);
2147 if (iErr != noErr)
2149 free (param);
2150 return -1;
2153 lpbr.launchBlockID = extendedBlock;
2154 lpbr.launchEPBLength = extendedBlockLen;
2155 lpbr.launchControlFlags = launchContinue + launchNoFileFlags;
2156 lpbr.launchAppSpec = &spec;
2157 lpbr.launchAppParameters = NULL;
2159 iErr = LaunchApplication (&lpbr); /* call the subprocess */
2160 if (iErr != noErr)
2162 free (param);
2163 return -1;
2166 send_event.what = kHighLevelEvent;
2167 send_event.message = kEmacsSubprocessSend;
2168 /* Event ID stored in "where" unused */
2170 retries = 3;
2171 /* OS may think current subprocess has terminated if previous one
2172 terminated recently. */
2175 iErr = PostHighLevelEvent (&send_event, &lpbr.launchProcessSN, 0, param,
2176 paramlen + 1, receiverIDisPSN);
2178 while (iErr == sessClosedErr && retries-- > 0);
2180 if (iErr != noErr)
2182 free (param);
2183 return -1;
2186 cursor_region_handle = NewRgn ();
2188 /* Wait for the subprocess to finish, when it will send us a ERPY
2189 high level event. */
2190 while (1)
2191 if (WaitNextEvent (highLevelEventMask, &reply_event, 180,
2192 cursor_region_handle)
2193 && reply_event.message == kEmacsSubprocessReply)
2194 break;
2196 /* The return code is sent through the refCon */
2197 iErr = AcceptHighLevelEvent (&targ, &ref_con, NULL, &len);
2198 if (iErr != noErr)
2200 DisposeHandle ((Handle) cursor_region_handle);
2201 free (param);
2202 return -1;
2205 DisposeHandle ((Handle) cursor_region_handle);
2206 free (param);
2208 return ref_con;
2209 #endif /* not TARGET_API_MAC_CARBON */
2213 DIR *
2214 opendir (const char *dirname)
2216 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
2217 char mac_pathname[MAXPATHLEN+1], vol_name[MAXPATHLEN+1];
2218 DIR *dirp;
2219 CInfoPBRec cipb;
2220 HVolumeParam vpb;
2221 int len, vol_name_len;
2223 if (find_true_pathname (dirname, true_pathname, MAXPATHLEN+1) == -1)
2224 return 0;
2226 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
2227 if (len > -1)
2228 fully_resolved_name[len] = '\0';
2229 else
2230 strcpy (fully_resolved_name, true_pathname);
2232 dirp = (DIR *) malloc (sizeof(DIR));
2233 if (!dirp)
2234 return 0;
2236 /* Handle special case when dirname is "/": sets up for readir to
2237 get all mount volumes. */
2238 if (strcmp (fully_resolved_name, "/") == 0)
2240 dirp->getting_volumes = 1; /* special all mounted volumes DIR struct */
2241 dirp->current_index = 1; /* index for first volume */
2242 return dirp;
2245 /* Handle typical cases: not accessing all mounted volumes. */
2246 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
2247 return 0;
2249 /* Emacs calls opendir without the trailing '/', Mac needs trailing ':' */
2250 len = strlen (mac_pathname);
2251 if (mac_pathname[len - 1] != ':' && len < MAXPATHLEN)
2252 strcat (mac_pathname, ":");
2254 /* Extract volume name */
2255 vol_name_len = strchr (mac_pathname, ':') - mac_pathname;
2256 strncpy (vol_name, mac_pathname, vol_name_len);
2257 vol_name[vol_name_len] = '\0';
2258 strcat (vol_name, ":");
2260 c2pstr (mac_pathname);
2261 cipb.hFileInfo.ioNamePtr = mac_pathname;
2262 /* using full pathname so vRefNum and DirID ignored */
2263 cipb.hFileInfo.ioVRefNum = 0;
2264 cipb.hFileInfo.ioDirID = 0;
2265 cipb.hFileInfo.ioFDirIndex = 0;
2266 /* set to 0 to get information about specific dir or file */
2268 errno = PBGetCatInfo (&cipb, false);
2269 if (errno != noErr)
2271 errno = ENOENT;
2272 return 0;
2275 if (!(cipb.hFileInfo.ioFlAttrib & 0x10)) /* bit 4 = 1 for directories */
2276 return 0; /* not a directory */
2278 dirp->dir_id = cipb.dirInfo.ioDrDirID; /* used later in readdir */
2279 dirp->getting_volumes = 0;
2280 dirp->current_index = 1; /* index for first file/directory */
2282 c2pstr (vol_name);
2283 vpb.ioNamePtr = vol_name;
2284 /* using full pathname so vRefNum and DirID ignored */
2285 vpb.ioVRefNum = 0;
2286 vpb.ioVolIndex = -1;
2287 errno = PBHGetVInfo ((union HParamBlockRec *) &vpb, false);
2288 if (errno != noErr)
2290 errno = ENOENT;
2291 return 0;
2294 dirp->vol_ref_num = vpb.ioVRefNum;
2296 return dirp;
2300 closedir (DIR *dp)
2302 free (dp);
2304 return 0;
2308 struct dirent *
2309 readdir (DIR *dp)
2311 HParamBlockRec hpblock;
2312 CInfoPBRec cipb;
2313 static struct dirent s_dirent;
2314 static Str255 s_name;
2315 int done;
2316 char *p;
2318 /* Handle the root directory containing the mounted volumes. Call
2319 PBHGetVInfo specifying an index to obtain the info for a volume.
2320 PBHGetVInfo returns an error when it receives an index beyond the
2321 last volume, at which time we should return a nil dirent struct
2322 pointer. */
2323 if (dp->getting_volumes)
2325 hpblock.volumeParam.ioNamePtr = s_name;
2326 hpblock.volumeParam.ioVRefNum = 0;
2327 hpblock.volumeParam.ioVolIndex = dp->current_index;
2329 errno = PBHGetVInfo (&hpblock, false);
2330 if (errno != noErr)
2332 errno = ENOENT;
2333 return 0;
2336 p2cstr (s_name);
2337 strcat (s_name, "/"); /* need "/" for stat to work correctly */
2339 dp->current_index++;
2341 s_dirent.d_ino = hpblock.volumeParam.ioVRefNum;
2342 s_dirent.d_name = s_name;
2344 return &s_dirent;
2346 else
2348 cipb.hFileInfo.ioVRefNum = dp->vol_ref_num;
2349 cipb.hFileInfo.ioNamePtr = s_name;
2350 /* location to receive filename returned */
2352 /* return only visible files */
2353 done = false;
2354 while (!done)
2356 cipb.hFileInfo.ioDirID = dp->dir_id;
2357 /* directory ID found by opendir */
2358 cipb.hFileInfo.ioFDirIndex = dp->current_index;
2360 errno = PBGetCatInfo (&cipb, false);
2361 if (errno != noErr)
2363 errno = ENOENT;
2364 return 0;
2367 /* insist on a visible entry */
2368 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* directory? */
2369 done = !(cipb.dirInfo.ioDrUsrWds.frFlags & fInvisible);
2370 else
2371 done = !(cipb.hFileInfo.ioFlFndrInfo.fdFlags & fInvisible);
2373 dp->current_index++;
2376 p2cstr (s_name);
2378 p = s_name;
2379 while (*p)
2381 if (*p == '/')
2382 *p = ':';
2383 p++;
2386 s_dirent.d_ino = cipb.dirInfo.ioDrDirID;
2387 /* value unimportant: non-zero for valid file */
2388 s_dirent.d_name = s_name;
2390 return &s_dirent;
2395 char *
2396 getwd (char *path)
2398 char mac_pathname[MAXPATHLEN+1];
2399 Str255 directory_name;
2400 OSErr errno;
2401 CInfoPBRec cipb;
2403 if (path_from_vol_dir_name (mac_pathname, 255, 0, 0, "\p") == 0)
2404 return NULL;
2406 if (mac_to_posix_pathname (mac_pathname, path, MAXPATHLEN+1) == 0)
2407 return 0;
2408 else
2409 return path;
2412 #endif /* ! MAC_OSX */
2415 void
2416 initialize_applescript ()
2418 AEDesc null_desc;
2419 OSAError osaerror;
2421 /* if open fails, as_scripting_component is set to NULL. Its
2422 subsequent use in OSA calls will fail with badComponentInstance
2423 error. */
2424 as_scripting_component = OpenDefaultComponent (kOSAComponentType,
2425 kAppleScriptSubtype);
2427 null_desc.descriptorType = typeNull;
2428 null_desc.dataHandle = 0;
2429 osaerror = OSAMakeContext (as_scripting_component, &null_desc,
2430 kOSANullScript, &as_script_context);
2431 if (osaerror)
2432 as_script_context = kOSANullScript;
2433 /* use default context if create fails */
2437 void terminate_applescript()
2439 OSADispose (as_scripting_component, as_script_context);
2440 CloseComponent (as_scripting_component);
2444 /* Compile and execute the AppleScript SCRIPT and return the error
2445 status as function value. A zero is returned if compilation and
2446 execution is successful, in which case RESULT returns a pointer to
2447 a string containing the resulting script value. Otherwise, the Mac
2448 error code is returned and RESULT returns a pointer to an error
2449 string. In both cases the caller should deallocate the storage
2450 used by the string pointed to by RESULT if it is non-NULL. For
2451 documentation on the MacOS scripting architecture, see Inside
2452 Macintosh - Interapplication Communications: Scripting Components. */
2454 static long
2455 do_applescript (char *script, char **result)
2457 AEDesc script_desc, result_desc, error_desc;
2458 OSErr error;
2459 OSAError osaerror;
2460 long length;
2462 *result = 0;
2464 if (!as_scripting_component)
2465 initialize_applescript();
2467 error = AECreateDesc (typeChar, script, strlen(script), &script_desc);
2468 if (error)
2469 return error;
2471 osaerror = OSADoScript (as_scripting_component, &script_desc, kOSANullScript,
2472 typeChar, kOSAModeNull, &result_desc);
2474 if (osaerror == errOSAScriptError)
2476 /* error executing AppleScript: retrieve error message */
2477 if (!OSAScriptError (as_scripting_component, kOSAErrorMessage, typeChar,
2478 &error_desc))
2480 #if TARGET_API_MAC_CARBON
2481 length = AEGetDescDataSize (&error_desc);
2482 *result = (char *) xmalloc (length + 1);
2483 if (*result)
2485 AEGetDescData (&error_desc, *result, length);
2486 *(*result + length) = '\0';
2488 #else /* not TARGET_API_MAC_CARBON */
2489 HLock (error_desc.dataHandle);
2490 length = GetHandleSize(error_desc.dataHandle);
2491 *result = (char *) xmalloc (length + 1);
2492 if (*result)
2494 memcpy (*result, *(error_desc.dataHandle), length);
2495 *(*result + length) = '\0';
2497 HUnlock (error_desc.dataHandle);
2498 #endif /* not TARGET_API_MAC_CARBON */
2499 AEDisposeDesc (&error_desc);
2502 else if (osaerror == noErr) /* success: retrieve resulting script value */
2504 #if TARGET_API_MAC_CARBON
2505 length = AEGetDescDataSize (&result_desc);
2506 *result = (char *) xmalloc (length + 1);
2507 if (*result)
2509 AEGetDescData (&result_desc, *result, length);
2510 *(*result + length) = '\0';
2512 #else /* not TARGET_API_MAC_CARBON */
2513 HLock (result_desc.dataHandle);
2514 length = GetHandleSize(result_desc.dataHandle);
2515 *result = (char *) xmalloc (length + 1);
2516 if (*result)
2518 memcpy (*result, *(result_desc.dataHandle), length);
2519 *(*result + length) = '\0';
2521 HUnlock (result_desc.dataHandle);
2522 #endif /* not TARGET_API_MAC_CARBON */
2523 AEDisposeDesc (&result_desc);
2526 AEDisposeDesc (&script_desc);
2528 return osaerror;
2532 DEFUN ("do-applescript", Fdo_applescript, Sdo_applescript, 1, 1, 0,
2533 doc: /* Compile and execute AppleScript SCRIPT and retrieve and return the result.
2534 If compilation and execution are successful, the resulting script
2535 value is returned as a string. Otherwise the function aborts and
2536 displays the error message returned by the AppleScript scripting
2537 component. */)
2538 (script)
2539 Lisp_Object script;
2541 char *result, *temp;
2542 Lisp_Object lisp_result;
2543 long status;
2545 CHECK_STRING (script);
2547 BLOCK_INPUT;
2548 status = do_applescript (SDATA (script), &result);
2549 UNBLOCK_INPUT;
2550 if (status)
2552 if (!result)
2553 error ("AppleScript error %d", status);
2554 else
2556 /* Unfortunately only OSADoScript in do_applescript knows how
2557 how large the resulting script value or error message is
2558 going to be and therefore as caller memory must be
2559 deallocated here. It is necessary to free the error
2560 message before calling error to avoid a memory leak. */
2561 temp = (char *) alloca (strlen (result) + 1);
2562 strcpy (temp, result);
2563 xfree (result);
2564 error (temp);
2567 else
2569 lisp_result = build_string (result);
2570 xfree (result);
2571 return lisp_result;
2576 DEFUN ("mac-file-name-to-posix", Fmac_file_name_to_posix,
2577 Smac_file_name_to_posix, 1, 1, 0,
2578 doc: /* Convert Macintosh filename to Posix form. */)
2579 (mac_filename)
2580 Lisp_Object mac_filename;
2582 char posix_filename[MAXPATHLEN+1];
2584 CHECK_STRING (mac_filename);
2586 if (mac_to_posix_pathname (SDATA (mac_filename), posix_filename,
2587 MAXPATHLEN))
2588 return build_string (posix_filename);
2589 else
2590 return Qnil;
2594 DEFUN ("posix-file-name-to-mac", Fposix_file_name_to_mac,
2595 Sposix_file_name_to_mac, 1, 1, 0,
2596 doc: /* Convert Posix filename to Mac form. */)
2597 (posix_filename)
2598 Lisp_Object posix_filename;
2600 char mac_filename[MAXPATHLEN+1];
2602 CHECK_STRING (posix_filename);
2604 if (posix_to_mac_pathname (SDATA (posix_filename), mac_filename,
2605 MAXPATHLEN))
2606 return build_string (mac_filename);
2607 else
2608 return Qnil;
2612 /* set interprogram-paste-function to mac-paste-function in mac-win.el
2613 to enable Emacs to obtain the contents of the Mac clipboard. */
2614 DEFUN ("mac-paste-function", Fmac_paste_function, Smac_paste_function, 0, 0, 0,
2615 doc: /* Return the contents of the Mac clipboard as a string. */)
2618 #if TARGET_API_MAC_CARBON
2619 OSStatus err;
2620 ScrapRef scrap;
2621 ScrapFlavorFlags sff;
2622 Size s;
2623 int i;
2624 char *data;
2626 BLOCK_INPUT;
2627 err = GetCurrentScrap (&scrap);
2628 if (err == noErr)
2629 err = GetScrapFlavorFlags (scrap, kScrapFlavorTypeText, &sff);
2630 if (err == noErr)
2631 err = GetScrapFlavorSize (scrap, kScrapFlavorTypeText, &s);
2632 if (err == noErr && (data = (char*) alloca (s)))
2633 err = GetScrapFlavorData (scrap, kScrapFlavorTypeText, &s, data);
2634 UNBLOCK_INPUT;
2635 if (err != noErr || s == 0)
2636 return Qnil;
2638 /* Emacs expects clipboard contents have Unix-style eol's */
2639 for (i = 0; i < s; i++)
2640 if (data[i] == '\r')
2641 data[i] = '\n';
2643 return make_string (data, s);
2644 #else /* not TARGET_API_MAC_CARBON */
2645 Lisp_Object value;
2646 Handle my_handle;
2647 long scrap_offset, rc, i;
2649 my_handle = NewHandle (0); /* allocate 0-length data area */
2651 rc = GetScrap (my_handle, 'TEXT', &scrap_offset);
2652 if (rc < 0)
2653 return Qnil;
2655 HLock (my_handle);
2657 /* Emacs expects clipboard contents have Unix-style eol's */
2658 for (i = 0; i < rc; i++)
2659 if ((*my_handle)[i] == '\r')
2660 (*my_handle)[i] = '\n';
2662 value = make_string (*my_handle, rc);
2664 HUnlock (my_handle);
2666 DisposeHandle (my_handle);
2668 return value;
2669 #endif /* not TARGET_API_MAC_CARBON */
2673 /* set interprogram-cut-function to mac-cut-function in mac-win.el
2674 to enable Emacs to write the top of the kill-ring to the Mac clipboard. */
2675 DEFUN ("mac-cut-function", Fmac_cut_function, Smac_cut_function, 1, 2, 0,
2676 doc: /* Put the value of the string parameter to the Mac clipboard. */)
2677 (value, push)
2678 Lisp_Object value, push;
2680 char *buf;
2681 int len, i;
2683 /* fixme: ignore the push flag for now */
2685 CHECK_STRING (value);
2687 len = SCHARS (value);
2688 buf = (char *) alloca (len+1);
2689 bcopy (SDATA (value), buf, len);
2690 buf[len] = '\0';
2692 /* convert to Mac-style eol's before sending to clipboard */
2693 for (i = 0; i < len; i++)
2694 if (buf[i] == '\n')
2695 buf[i] = '\r';
2697 #if TARGET_API_MAC_CARBON
2699 ScrapRef scrap;
2701 BLOCK_INPUT;
2702 ClearCurrentScrap ();
2703 if (GetCurrentScrap (&scrap) != noErr)
2705 UNBLOCK_INPUT;
2706 error ("cannot get current scrap");
2709 if (PutScrapFlavor (scrap, kScrapFlavorTypeText, kScrapFlavorMaskNone, len,
2710 buf) != noErr)
2712 UNBLOCK_INPUT;
2713 error ("cannot put to scrap");
2715 UNBLOCK_INPUT;
2717 #else /* not TARGET_API_MAC_CARBON */
2718 ZeroScrap ();
2719 PutScrap (len, 'TEXT', buf);
2720 #endif /* not TARGET_API_MAC_CARBON */
2722 return Qnil;
2726 DEFUN ("x-selection-exists-p", Fx_selection_exists_p, Sx_selection_exists_p,
2727 0, 1, 0,
2728 doc: /* Whether there is an owner for the given X Selection.
2729 The arg should be the name of the selection in question, typically one of
2730 the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.
2731 \(Those are literal upper-case symbol names, since that's what X expects.)
2732 For convenience, the symbol nil is the same as `PRIMARY',
2733 and t is the same as `SECONDARY'. */)
2734 (selection)
2735 Lisp_Object selection;
2737 CHECK_SYMBOL (selection);
2739 /* Return nil for PRIMARY and SECONDARY selections; for CLIPBOARD, check
2740 if the clipboard currently has valid text format contents. */
2742 if (EQ (selection, QCLIPBOARD))
2744 Lisp_Object val = Qnil;
2746 #if TARGET_API_MAC_CARBON
2747 ScrapRef scrap;
2748 ScrapFlavorFlags sff;
2750 BLOCK_INPUT;
2751 if (GetCurrentScrap (&scrap) == noErr)
2752 if (GetScrapFlavorFlags (scrap, kScrapFlavorTypeText, &sff) == noErr)
2753 val = Qt;
2754 UNBLOCK_INPUT;
2755 #else /* not TARGET_API_MAC_CARBON */
2756 Handle my_handle;
2757 long rc, scrap_offset;
2759 my_handle = NewHandle (0);
2761 rc = GetScrap (my_handle, 'TEXT', &scrap_offset);
2762 if (rc >= 0)
2763 val = Qt;
2765 DisposeHandle (my_handle);
2766 #endif /* not TARGET_API_MAC_CARBON */
2768 return val;
2770 return Qnil;
2773 extern void mac_clear_font_name_table P_ ((void));
2775 DEFUN ("mac-clear-font-name-table", Fmac_clear_font_name_table, Smac_clear_font_name_table, 0, 0, 0,
2776 doc: /* Clear the font name table. */)
2779 check_mac ();
2780 mac_clear_font_name_table ();
2781 return Qnil;
2784 #ifdef MAC_OSX
2785 #undef select
2787 extern int inhibit_window_system;
2788 extern int noninteractive;
2790 /* Unlike in X11, window events in Carbon do not come from sockets.
2791 So we cannot simply use `select' to monitor two kinds of inputs:
2792 window events and process outputs. We emulate such functionality
2793 by regarding fd 0 as the window event channel and simultaneously
2794 monitoring both kinds of input channels. It is implemented by
2795 dividing into some cases:
2796 1. The window event channel is not involved.
2797 -> Use `select'.
2798 2. Sockets are not involved.
2799 -> Use ReceiveNextEvent.
2800 3. [If SELECT_USE_CFSOCKET is defined]
2801 Only the window event channel and socket read channels are
2802 involved, and timeout is not too short (greater than
2803 SELECT_TIMEOUT_THRESHHOLD_RUNLOOP seconds).
2804 -> Create CFSocket for each socket and add it into the current
2805 event RunLoop so that an `ready-to-read' event can be posted
2806 to the event queue that is also used for window events. Then
2807 ReceiveNextEvent can wait for both kinds of inputs.
2808 4. Otherwise.
2809 -> Periodically poll the window input channel while repeatedly
2810 executing `select' with a short timeout
2811 (SELECT_POLLING_PERIOD_USEC microseconds). */
2813 #define SELECT_POLLING_PERIOD_USEC 20000
2814 #ifdef SELECT_USE_CFSOCKET
2815 #define SELECT_TIMEOUT_THRESHOLD_RUNLOOP 0.2
2816 #define EVENT_CLASS_SOCK 'Sock'
2818 static void
2819 socket_callback (s, type, address, data, info)
2820 CFSocketRef s;
2821 CFSocketCallBackType type;
2822 CFDataRef address;
2823 const void *data;
2824 void *info;
2826 EventRef event;
2828 CreateEvent (NULL, EVENT_CLASS_SOCK, 0, 0, kEventAttributeNone, &event);
2829 PostEventToQueue (GetCurrentEventQueue (), event, kEventPriorityStandard);
2830 ReleaseEvent (event);
2832 #endif /* SELECT_USE_CFSOCKET */
2834 static int
2835 select_and_poll_event (n, rfds, wfds, efds, timeout)
2836 int n;
2837 SELECT_TYPE *rfds;
2838 SELECT_TYPE *wfds;
2839 SELECT_TYPE *efds;
2840 struct timeval *timeout;
2842 int r;
2843 OSErr err;
2845 r = select (n, rfds, wfds, efds, timeout);
2846 if (r != -1)
2848 BLOCK_INPUT;
2849 err = ReceiveNextEvent (0, NULL, kEventDurationNoWait,
2850 kEventLeaveInQueue, NULL);
2851 UNBLOCK_INPUT;
2852 if (err == noErr)
2854 FD_SET (0, rfds);
2855 r++;
2858 return r;
2861 #ifndef MAC_OS_X_VERSION_10_2
2862 #undef SELECT_INVALIDATE_CFSOCKET
2863 #endif
2866 sys_select (n, rfds, wfds, efds, timeout)
2867 int n;
2868 SELECT_TYPE *rfds;
2869 SELECT_TYPE *wfds;
2870 SELECT_TYPE *efds;
2871 struct timeval *timeout;
2873 OSErr err;
2874 int i, r;
2875 EMACS_TIME select_timeout;
2877 if (inhibit_window_system || noninteractive
2878 || rfds == NULL || !FD_ISSET (0, rfds))
2879 return select (n, rfds, wfds, efds, timeout);
2881 FD_CLR (0, rfds);
2883 if (wfds == NULL && efds == NULL)
2885 int nsocks = 0;
2886 SELECT_TYPE orfds = *rfds;
2888 EventTimeout timeout_sec =
2889 (timeout
2890 ? (EMACS_SECS (*timeout) * kEventDurationSecond
2891 + EMACS_USECS (*timeout) * kEventDurationMicrosecond)
2892 : kEventDurationForever);
2894 for (i = 1; i < n; i++)
2895 if (FD_ISSET (i, rfds))
2896 nsocks++;
2898 if (nsocks == 0)
2900 BLOCK_INPUT;
2901 err = ReceiveNextEvent (0, NULL, timeout_sec,
2902 kEventLeaveInQueue, NULL);
2903 UNBLOCK_INPUT;
2904 if (err == noErr)
2906 FD_SET (0, rfds);
2907 return 1;
2909 else
2910 return 0;
2913 /* Avoid initial overhead of RunLoop setup for the case that
2914 some input is already available. */
2915 EMACS_SET_SECS_USECS (select_timeout, 0, 0);
2916 r = select_and_poll_event (n, rfds, wfds, efds, &select_timeout);
2917 if (r != 0 || timeout_sec == 0.0)
2918 return r;
2920 *rfds = orfds;
2922 #ifdef SELECT_USE_CFSOCKET
2923 if (timeout_sec > 0 && timeout_sec <= SELECT_TIMEOUT_THRESHOLD_RUNLOOP)
2924 goto poll_periodically;
2927 CFRunLoopRef runloop =
2928 (CFRunLoopRef) GetCFRunLoopFromEventLoop (GetCurrentEventLoop ());
2929 EventTypeSpec specs[] = {{EVENT_CLASS_SOCK, 0}};
2930 #ifdef SELECT_INVALIDATE_CFSOCKET
2931 CFSocketRef *shead, *s;
2932 #else
2933 CFRunLoopSourceRef *shead, *s;
2934 #endif
2936 BLOCK_INPUT;
2938 #ifdef SELECT_INVALIDATE_CFSOCKET
2939 shead = xmalloc (sizeof (CFSocketRef) * nsocks);
2940 #else
2941 shead = xmalloc (sizeof (CFRunLoopSourceRef) * nsocks);
2942 #endif
2943 s = shead;
2944 for (i = 1; i < n; i++)
2945 if (FD_ISSET (i, rfds))
2947 CFSocketRef socket =
2948 CFSocketCreateWithNative (NULL, i, kCFSocketReadCallBack,
2949 socket_callback, NULL);
2950 CFRunLoopSourceRef source =
2951 CFSocketCreateRunLoopSource (NULL, socket, 0);
2953 #ifdef SELECT_INVALIDATE_CFSOCKET
2954 CFSocketSetSocketFlags (socket, 0);
2955 #endif
2956 CFRunLoopAddSource (runloop, source, kCFRunLoopDefaultMode);
2957 #ifdef SELECT_INVALIDATE_CFSOCKET
2958 CFRelease (source);
2959 *s = socket;
2960 #else
2961 CFRelease (socket);
2962 *s = source;
2963 #endif
2964 s++;
2967 err = ReceiveNextEvent (0, NULL, timeout_sec, kEventLeaveInQueue, NULL);
2971 --s;
2972 #ifdef SELECT_INVALIDATE_CFSOCKET
2973 CFSocketInvalidate (*s);
2974 #else
2975 CFRunLoopRemoveSource (runloop, *s, kCFRunLoopDefaultMode);
2976 #endif
2977 CFRelease (*s);
2979 while (s != shead);
2981 xfree (shead);
2983 if (err)
2985 FD_ZERO (rfds);
2986 r = 0;
2988 else
2990 FlushEventsMatchingListFromQueue (GetCurrentEventQueue (),
2991 GetEventTypeCount (specs),
2992 specs);
2993 EMACS_SET_SECS_USECS (select_timeout, 0, 0);
2994 r = select_and_poll_event (n, rfds, wfds, efds, &select_timeout);
2997 UNBLOCK_INPUT;
2999 return r;
3001 #endif /* SELECT_USE_CFSOCKET */
3004 poll_periodically:
3006 EMACS_TIME end_time, now, remaining_time;
3007 SELECT_TYPE orfds = *rfds, owfds, oefds;
3009 if (wfds)
3010 owfds = *wfds;
3011 if (efds)
3012 oefds = *efds;
3013 if (timeout)
3015 remaining_time = *timeout;
3016 EMACS_GET_TIME (now);
3017 EMACS_ADD_TIME (end_time, now, remaining_time);
3022 EMACS_SET_SECS_USECS (select_timeout, 0, SELECT_POLLING_PERIOD_USEC);
3023 if (timeout && EMACS_TIME_LT (remaining_time, select_timeout))
3024 select_timeout = remaining_time;
3025 r = select_and_poll_event (n, rfds, wfds, efds, &select_timeout);
3026 if (r != 0)
3027 return r;
3029 *rfds = orfds;
3030 if (wfds)
3031 *wfds = owfds;
3032 if (efds)
3033 *efds = oefds;
3035 if (timeout)
3037 EMACS_GET_TIME (now);
3038 EMACS_SUB_TIME (remaining_time, end_time, now);
3041 while (!timeout || EMACS_TIME_LT (now, end_time));
3043 FD_ZERO (rfds);
3044 if (wfds)
3045 FD_ZERO (wfds);
3046 if (efds)
3047 FD_ZERO (efds);
3048 return 0;
3052 /* Set up environment variables so that Emacs can correctly find its
3053 support files when packaged as an application bundle. Directories
3054 placed in /usr/local/share/emacs/<emacs-version>/, /usr/local/bin,
3055 and /usr/local/libexec/emacs/<emacs-version>/<system-configuration>
3056 by `make install' by default can instead be placed in
3057 .../Emacs.app/Contents/Resources/ and
3058 .../Emacs.app/Contents/MacOS/. Each of these environment variables
3059 is changed only if it is not already set. Presumably if the user
3060 sets an environment variable, he will want to use files in his path
3061 instead of ones in the application bundle. */
3062 void
3063 init_mac_osx_environment ()
3065 CFBundleRef bundle;
3066 CFURLRef bundleURL;
3067 CFStringRef cf_app_bundle_pathname;
3068 int app_bundle_pathname_len;
3069 char *app_bundle_pathname;
3070 char *p, *q;
3071 struct stat st;
3073 /* Fetch the pathname of the application bundle as a C string into
3074 app_bundle_pathname. */
3076 bundle = CFBundleGetMainBundle ();
3077 if (!bundle)
3078 return;
3080 bundleURL = CFBundleCopyBundleURL (bundle);
3081 if (!bundleURL)
3082 return;
3084 cf_app_bundle_pathname = CFURLCopyFileSystemPath (bundleURL,
3085 kCFURLPOSIXPathStyle);
3086 app_bundle_pathname_len = CFStringGetLength (cf_app_bundle_pathname);
3087 app_bundle_pathname = (char *) alloca (app_bundle_pathname_len + 1);
3089 if (!CFStringGetCString (cf_app_bundle_pathname,
3090 app_bundle_pathname,
3091 app_bundle_pathname_len + 1,
3092 kCFStringEncodingISOLatin1))
3094 CFRelease (cf_app_bundle_pathname);
3095 return;
3098 CFRelease (cf_app_bundle_pathname);
3100 /* P should have sufficient room for the pathname of the bundle plus
3101 the subpath in it leading to the respective directories. Q
3102 should have three times that much room because EMACSLOADPATH can
3103 have the value "<path to lisp dir>:<path to leim dir>:<path to
3104 site-lisp dir>". */
3105 p = (char *) alloca (app_bundle_pathname_len + 50);
3106 q = (char *) alloca (3 * app_bundle_pathname_len + 150);
3107 if (!getenv ("EMACSLOADPATH"))
3109 q[0] = '\0';
3111 strcpy (p, app_bundle_pathname);
3112 strcat (p, "/Contents/Resources/lisp");
3113 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
3114 strcat (q, p);
3116 strcpy (p, app_bundle_pathname);
3117 strcat (p, "/Contents/Resources/leim");
3118 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
3120 if (q[0] != '\0')
3121 strcat (q, ":");
3122 strcat (q, p);
3125 strcpy (p, app_bundle_pathname);
3126 strcat (p, "/Contents/Resources/site-lisp");
3127 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
3129 if (q[0] != '\0')
3130 strcat (q, ":");
3131 strcat (q, p);
3134 if (q[0] != '\0')
3135 setenv ("EMACSLOADPATH", q, 1);
3138 if (!getenv ("EMACSPATH"))
3140 q[0] = '\0';
3142 strcpy (p, app_bundle_pathname);
3143 strcat (p, "/Contents/MacOS/libexec");
3144 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
3145 strcat (q, p);
3147 strcpy (p, app_bundle_pathname);
3148 strcat (p, "/Contents/MacOS/bin");
3149 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
3151 if (q[0] != '\0')
3152 strcat (q, ":");
3153 strcat (q, p);
3156 if (q[0] != '\0')
3157 setenv ("EMACSPATH", q, 1);
3160 if (!getenv ("EMACSDATA"))
3162 strcpy (p, app_bundle_pathname);
3163 strcat (p, "/Contents/Resources/etc");
3164 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
3165 setenv ("EMACSDATA", p, 1);
3168 if (!getenv ("EMACSDOC"))
3170 strcpy (p, app_bundle_pathname);
3171 strcat (p, "/Contents/Resources/etc");
3172 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
3173 setenv ("EMACSDOC", p, 1);
3176 if (!getenv ("INFOPATH"))
3178 strcpy (p, app_bundle_pathname);
3179 strcat (p, "/Contents/Resources/info");
3180 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
3181 setenv ("INFOPATH", p, 1);
3184 #endif /* MAC_OSX */
3186 void
3187 syms_of_mac ()
3189 QCLIPBOARD = intern ("CLIPBOARD");
3190 staticpro (&QCLIPBOARD);
3192 defsubr (&Smac_paste_function);
3193 defsubr (&Smac_cut_function);
3194 defsubr (&Sx_selection_exists_p);
3195 defsubr (&Smac_clear_font_name_table);
3197 defsubr (&Sdo_applescript);
3198 defsubr (&Smac_file_name_to_posix);
3199 defsubr (&Sposix_file_name_to_mac);
3202 /* arch-tag: 29d30c1f-0c6b-4f88-8a6d-0558d7f9dbff
3203 (do not change this comment) */