Fix ediff problems. (Reported by Dan Nicolaescu.)
[emacs.git] / src / mac.c
bloba71194f9aeece9f3c8e560d792e95367cbd19742
1 /* Unix emulation routines for GNU Emacs on the Mac OS.
2 Copyright (C) 2000, 2001, 2002, 2003, 2004,
3 2005, 2006 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA. */
22 /* Contributed by Andrew Choi (akochoi@mac.com). */
24 #include <config.h>
26 #include <stdio.h>
27 #include <errno.h>
29 #include "lisp.h"
30 #include "process.h"
31 #undef init_process
32 #include "systime.h"
33 #include "sysselect.h"
34 #include "blockinput.h"
36 #include "macterm.h"
38 #include "charset.h"
39 #include "coding.h"
40 #if !TARGET_API_MAC_CARBON
41 #include <Files.h>
42 #include <MacTypes.h>
43 #include <TextUtils.h>
44 #include <Folders.h>
45 #include <Resources.h>
46 #include <Aliases.h>
47 #include <FixMath.h>
48 #include <Timer.h>
49 #include <OSA.h>
50 #include <AppleScript.h>
51 #include <Scrap.h>
52 #include <Events.h>
53 #include <Processes.h>
54 #include <EPPC.h>
55 #include <MacLocales.h>
56 #include <Endian.h>
57 #endif /* not TARGET_API_MAC_CARBON */
59 #include <utime.h>
60 #include <dirent.h>
61 #include <sys/types.h>
62 #include <sys/stat.h>
63 #include <pwd.h>
64 #include <grp.h>
65 #include <sys/param.h>
66 #include <fcntl.h>
67 #if __MWERKS__
68 #include <unistd.h>
69 #endif
71 /* The system script code. */
72 static int mac_system_script_code;
74 /* The system locale identifier string. */
75 static Lisp_Object Vmac_system_locale;
77 /* An instance of the AppleScript component. */
78 static ComponentInstance as_scripting_component;
79 /* The single script context used for all script executions. */
80 static OSAID as_script_context;
82 static OSErr posix_pathname_to_fsspec P_ ((const char *, FSSpec *));
83 static OSErr fsspec_to_posix_pathname P_ ((const FSSpec *, char *, int));
85 /* When converting from Mac to Unix pathnames, /'s in folder names are
86 converted to :'s. This function, used in copying folder names,
87 performs a strncat and converts all character a to b in the copy of
88 the string s2 appended to the end of s1. */
90 void
91 string_cat_and_replace (char *s1, const char *s2, int n, char a, char b)
93 int l1 = strlen (s1);
94 int l2 = strlen (s2);
95 char *p = s1 + l1;
96 int i;
98 strncat (s1, s2, n);
99 for (i = 0; i < l2; i++)
101 if (*p == a)
102 *p = b;
103 p++;
108 /* Convert a Mac pathname to Posix form. A Mac full pathname is one
109 that does not begin with a ':' and contains at least one ':'. A Mac
110 full pathname causes a '/' to be prepended to the Posix pathname.
111 The algorithm for the rest of the pathname is as follows:
112 For each segment between two ':',
113 if it is non-null, copy as is and then add a '/' at the end,
114 otherwise, insert a "../" into the Posix pathname.
115 Returns 1 if successful; 0 if fails. */
118 mac_to_posix_pathname (const char *mfn, char *ufn, int ufnbuflen)
120 const char *p, *q, *pe;
122 strcpy (ufn, "");
124 if (*mfn == '\0')
125 return 1;
127 p = strchr (mfn, ':');
128 if (p != 0 && p != mfn) /* full pathname */
129 strcat (ufn, "/");
131 p = mfn;
132 if (*p == ':')
133 p++;
135 pe = mfn + strlen (mfn);
136 while (p < pe)
138 q = strchr (p, ':');
139 if (q)
141 if (q == p)
142 { /* two consecutive ':' */
143 if (strlen (ufn) + 3 >= ufnbuflen)
144 return 0;
145 strcat (ufn, "../");
147 else
149 if (strlen (ufn) + (q - p) + 1 >= ufnbuflen)
150 return 0;
151 string_cat_and_replace (ufn, p, q - p, '/', ':');
152 strcat (ufn, "/");
154 p = q + 1;
156 else
158 if (strlen (ufn) + (pe - p) >= ufnbuflen)
159 return 0;
160 string_cat_and_replace (ufn, p, pe - p, '/', ':');
161 /* no separator for last one */
162 p = pe;
166 return 1;
170 extern char *get_temp_dir_name ();
173 /* Convert a Posix pathname to Mac form. Approximately reverse of the
174 above in algorithm. */
177 posix_to_mac_pathname (const char *ufn, char *mfn, int mfnbuflen)
179 const char *p, *q, *pe;
180 char expanded_pathname[MAXPATHLEN+1];
182 strcpy (mfn, "");
184 if (*ufn == '\0')
185 return 1;
187 p = ufn;
189 /* Check for and handle volume names. Last comparison: strangely
190 somewhere "/.emacs" is passed. A temporary fix for now. */
191 if (*p == '/' && strchr (p+1, '/') == NULL && strcmp (p, "/.emacs") != 0)
193 if (strlen (p) + 1 > mfnbuflen)
194 return 0;
195 strcpy (mfn, p+1);
196 strcat (mfn, ":");
197 return 1;
200 /* expand to emacs dir found by init_emacs_passwd_dir */
201 if (strncmp (p, "~emacs/", 7) == 0)
203 struct passwd *pw = getpwnam ("emacs");
204 p += 7;
205 if (strlen (pw->pw_dir) + strlen (p) > MAXPATHLEN)
206 return 0;
207 strcpy (expanded_pathname, pw->pw_dir);
208 strcat (expanded_pathname, p);
209 p = expanded_pathname;
210 /* now p points to the pathname with emacs dir prefix */
212 else if (strncmp (p, "/tmp/", 5) == 0)
214 char *t = get_temp_dir_name ();
215 p += 5;
216 if (strlen (t) + strlen (p) > MAXPATHLEN)
217 return 0;
218 strcpy (expanded_pathname, t);
219 strcat (expanded_pathname, p);
220 p = expanded_pathname;
221 /* now p points to the pathname with emacs dir prefix */
223 else if (*p != '/') /* relative pathname */
224 strcat (mfn, ":");
226 if (*p == '/')
227 p++;
229 pe = p + strlen (p);
230 while (p < pe)
232 q = strchr (p, '/');
233 if (q)
235 if (q - p == 2 && *p == '.' && *(p+1) == '.')
237 if (strlen (mfn) + 1 >= mfnbuflen)
238 return 0;
239 strcat (mfn, ":");
241 else
243 if (strlen (mfn) + (q - p) + 1 >= mfnbuflen)
244 return 0;
245 string_cat_and_replace (mfn, p, q - p, ':', '/');
246 strcat (mfn, ":");
248 p = q + 1;
250 else
252 if (strlen (mfn) + (pe - p) >= mfnbuflen)
253 return 0;
254 string_cat_and_replace (mfn, p, pe - p, ':', '/');
255 p = pe;
259 return 1;
263 /***********************************************************************
264 Conversions on Apple event objects
265 ***********************************************************************/
267 static Lisp_Object Qundecoded_file_name;
269 static Lisp_Object
270 mac_aelist_to_lisp (desc_list)
271 AEDescList *desc_list;
273 OSErr err;
274 long count;
275 Lisp_Object result, elem;
276 DescType desc_type;
277 Size size;
278 AEKeyword keyword;
279 AEDesc desc;
281 err = AECountItems (desc_list, &count);
282 if (err != noErr)
283 return Qnil;
284 result = Qnil;
285 while (count > 0)
287 err = AESizeOfNthItem (desc_list, count, &desc_type, &size);
288 if (err == noErr)
289 switch (desc_type)
291 case typeAEList:
292 case typeAERecord:
293 case typeAppleEvent:
294 err = AEGetNthDesc (desc_list, count, typeWildCard,
295 &keyword, &desc);
296 if (err != noErr)
297 break;
298 elem = mac_aelist_to_lisp (&desc);
299 AEDisposeDesc (&desc);
300 break;
302 default:
303 if (desc_type == typeNull)
304 elem = Qnil;
305 else
307 elem = make_uninit_string (size);
308 err = AEGetNthPtr (desc_list, count, typeWildCard, &keyword,
309 &desc_type, SDATA (elem), size, &size);
311 if (err != noErr)
312 break;
313 desc_type = EndianU32_NtoB (desc_type);
314 elem = Fcons (make_unibyte_string ((char *) &desc_type, 4), elem);
315 break;
318 if (err != noErr)
319 elem = Qnil;
320 else if (desc_list->descriptorType != typeAEList)
322 keyword = EndianU32_NtoB (keyword);
323 elem = Fcons (make_unibyte_string ((char *) &keyword, 4), elem);
326 result = Fcons (elem, result);
327 count--;
330 desc_type = EndianU32_NtoB (desc_list->descriptorType);
331 return Fcons (make_unibyte_string ((char *) &desc_type, 4), result);
334 Lisp_Object
335 mac_aedesc_to_lisp (desc)
336 AEDesc *desc;
338 OSErr err = noErr;
339 DescType desc_type = desc->descriptorType;
340 Lisp_Object result;
342 switch (desc_type)
344 case typeNull:
345 result = Qnil;
346 break;
348 case typeAEList:
349 case typeAERecord:
350 case typeAppleEvent:
351 return mac_aelist_to_lisp (desc);
352 #if 0
353 /* The following one is much simpler, but creates and disposes
354 of Apple event descriptors many times. */
356 long count;
357 Lisp_Object elem;
358 AEKeyword keyword;
359 AEDesc desc1;
361 err = AECountItems (desc, &count);
362 if (err != noErr)
363 break;
364 result = Qnil;
365 while (count > 0)
367 err = AEGetNthDesc (desc, count, typeWildCard, &keyword, &desc1);
368 if (err != noErr)
369 break;
370 elem = mac_aedesc_to_lisp (&desc1);
371 AEDisposeDesc (&desc1);
372 if (desc_type != typeAEList)
374 keyword = EndianU32_NtoB (keyword);
375 elem = Fcons (make_unibyte_string ((char *) &keyword, 4), elem);
377 result = Fcons (elem, result);
378 count--;
381 #endif
382 break;
384 default:
385 #if TARGET_API_MAC_CARBON
386 result = make_uninit_string (AEGetDescDataSize (desc));
387 err = AEGetDescData (desc, SDATA (result), SBYTES (result));
388 #else
389 result = make_uninit_string (GetHandleSize (desc->dataHandle));
390 memcpy (SDATA (result), *(desc->dataHandle), SBYTES (result));
391 #endif
392 break;
395 if (err != noErr)
396 return Qnil;
398 desc_type = EndianU32_NtoB (desc_type);
399 return Fcons (make_unibyte_string ((char *) &desc_type, 4), result);
402 static pascal OSErr
403 mac_coerce_file_name_ptr (type_code, data_ptr, data_size,
404 to_type, handler_refcon, result)
405 DescType type_code;
406 const void *data_ptr;
407 Size data_size;
408 DescType to_type;
409 long handler_refcon;
410 AEDesc *result;
412 OSErr err;
414 if (type_code == typeNull)
415 err = errAECoercionFail;
416 else if (type_code == to_type || to_type == typeWildCard)
417 err = AECreateDesc (TYPE_FILE_NAME, data_ptr, data_size, result);
418 else if (type_code == TYPE_FILE_NAME)
419 /* Coercion from undecoded file name. */
421 #ifdef MAC_OSX
422 CFStringRef str;
423 CFURLRef url = NULL;
424 CFDataRef data = NULL;
426 str = CFStringCreateWithBytes (NULL, data_ptr, data_size,
427 kCFStringEncodingUTF8, false);
428 if (str)
430 url = CFURLCreateWithFileSystemPath (NULL, str,
431 kCFURLPOSIXPathStyle, false);
432 CFRelease (str);
434 if (url)
436 data = CFURLCreateData (NULL, url, kCFStringEncodingUTF8, true);
437 CFRelease (url);
439 if (data)
441 err = AECoercePtr (typeFileURL, CFDataGetBytePtr (data),
442 CFDataGetLength (data), to_type, result);
443 CFRelease (data);
445 else
446 err = memFullErr;
447 #else
448 FSSpec fs;
449 char *buf;
451 buf = xmalloc (data_size + 1);
452 if (buf)
454 memcpy (buf, data_ptr, data_size);
455 buf[data_size] = '\0';
456 err = posix_pathname_to_fsspec (buf, &fs);
457 xfree (buf);
459 else
460 err = memFullErr;
461 if (err == noErr)
462 err = AECoercePtr (typeFSS, &fs, sizeof (FSSpec), to_type, result);
463 #endif
465 else if (to_type == TYPE_FILE_NAME)
466 /* Coercion to undecoded file name. */
468 #ifdef MAC_OSX
469 CFURLRef url = NULL;
470 CFStringRef str = NULL;
471 CFDataRef data = NULL;
473 if (type_code == typeFileURL)
474 url = CFURLCreateWithBytes (NULL, data_ptr, data_size,
475 kCFStringEncodingUTF8, NULL);
476 else
478 AEDesc desc;
479 Size size;
480 char *buf;
482 err = AECoercePtr (type_code, data_ptr, data_size,
483 typeFileURL, &desc);
484 if (err == noErr)
486 size = AEGetDescDataSize (&desc);
487 buf = xmalloc (size);
488 if (buf)
490 err = AEGetDescData (&desc, buf, size);
491 if (err == noErr)
492 url = CFURLCreateWithBytes (NULL, buf, size,
493 kCFStringEncodingUTF8, NULL);
494 xfree (buf);
496 AEDisposeDesc (&desc);
499 if (url)
501 str = CFURLCopyFileSystemPath (url, kCFURLPOSIXPathStyle);
502 CFRelease (url);
504 if (str)
506 data = CFStringCreateExternalRepresentation (NULL, str,
507 kCFStringEncodingUTF8,
508 '\0');
509 CFRelease (str);
511 if (data)
513 err = AECreateDesc (TYPE_FILE_NAME, CFDataGetBytePtr (data),
514 CFDataGetLength (data), result);
515 CFRelease (data);
517 #else
518 char file_name[MAXPATHLEN];
520 if (type_code == typeFSS && data_size == sizeof (FSSpec))
521 err = fsspec_to_posix_pathname (data_ptr, file_name,
522 sizeof (file_name) - 1);
523 else
525 AEDesc desc;
526 FSSpec fs;
528 err = AECoercePtr (type_code, data_ptr, data_size, typeFSS, &desc);
529 if (err == noErr)
531 #if TARGET_API_MAC_CARBON
532 err = AEGetDescData (&desc, &fs, sizeof (FSSpec));
533 #else
534 fs = *(FSSpec *)(*(desc.dataHandle));
535 #endif
536 if (err == noErr)
537 err = fsspec_to_posix_pathname (&fs, file_name,
538 sizeof (file_name) - 1);
539 AEDisposeDesc (&desc);
542 if (err == noErr)
543 err = AECreateDesc (TYPE_FILE_NAME, file_name,
544 strlen (file_name), result);
545 #endif
547 else
548 abort ();
550 if (err != noErr)
551 return errAECoercionFail;
552 return noErr;
555 static pascal OSErr
556 mac_coerce_file_name_desc (from_desc, to_type, handler_refcon, result)
557 const AEDesc *from_desc;
558 DescType to_type;
559 long handler_refcon;
560 AEDesc *result;
562 OSErr err = noErr;
563 DescType from_type = from_desc->descriptorType;
565 if (from_type == typeNull)
566 err = errAECoercionFail;
567 else if (from_type == to_type || to_type == typeWildCard)
568 err = AEDuplicateDesc (from_desc, result);
569 else
571 char *data_ptr;
572 Size data_size;
574 #if TARGET_API_MAC_CARBON
575 data_size = AEGetDescDataSize (from_desc);
576 #else
577 data_size = GetHandleSize (from_desc->dataHandle);
578 #endif
579 data_ptr = xmalloc (data_size);
580 if (data_ptr)
582 #if TARGET_API_MAC_CARBON
583 err = AEGetDescData (from_desc, data_ptr, data_size);
584 #else
585 memcpy (data_ptr, *(from_desc->dataHandle), data_size);
586 #endif
587 if (err == noErr)
588 err = mac_coerce_file_name_ptr (from_type, data_ptr,
589 data_size, to_type,
590 handler_refcon, result);
591 xfree (data_ptr);
593 else
594 err = memFullErr;
597 if (err != noErr)
598 return errAECoercionFail;
599 return noErr;
602 OSErr
603 init_coercion_handler ()
605 OSErr err;
607 static AECoercePtrUPP coerce_file_name_ptrUPP = NULL;
608 static AECoerceDescUPP coerce_file_name_descUPP = NULL;
610 if (coerce_file_name_ptrUPP == NULL)
612 coerce_file_name_ptrUPP = NewAECoercePtrUPP (mac_coerce_file_name_ptr);
613 coerce_file_name_descUPP = NewAECoerceDescUPP (mac_coerce_file_name_desc);
616 err = AEInstallCoercionHandler (TYPE_FILE_NAME, typeWildCard,
617 (AECoercionHandlerUPP)
618 coerce_file_name_ptrUPP, 0, false, false);
619 if (err == noErr)
620 err = AEInstallCoercionHandler (typeWildCard, TYPE_FILE_NAME,
621 (AECoercionHandlerUPP)
622 coerce_file_name_ptrUPP, 0, false, false);
623 if (err == noErr)
624 err = AEInstallCoercionHandler (TYPE_FILE_NAME, typeWildCard,
625 coerce_file_name_descUPP, 0, true, false);
626 if (err == noErr)
627 err = AEInstallCoercionHandler (typeWildCard, TYPE_FILE_NAME,
628 coerce_file_name_descUPP, 0, true, false);
629 return err;
632 #if TARGET_API_MAC_CARBON
633 OSErr
634 create_apple_event_from_event_ref (event, num_params, names, types, result)
635 EventRef event;
636 UInt32 num_params;
637 EventParamName *names;
638 EventParamType *types;
639 AppleEvent *result;
641 OSErr err;
642 static const ProcessSerialNumber psn = {0, kCurrentProcess};
643 AEAddressDesc address_desc;
644 UInt32 i, size;
645 CFStringRef string;
646 CFDataRef data;
647 char *buf;
649 err = AECreateDesc (typeProcessSerialNumber, &psn,
650 sizeof (ProcessSerialNumber), &address_desc);
651 if (err == noErr)
653 err = AECreateAppleEvent (0, 0, /* Dummy class and ID. */
654 &address_desc, /* NULL is not allowed
655 on Mac OS Classic. */
656 kAutoGenerateReturnID,
657 kAnyTransactionID, result);
658 AEDisposeDesc (&address_desc);
660 if (err != noErr)
661 return err;
663 for (i = 0; i < num_params; i++)
664 switch (types[i])
666 #ifdef MAC_OSX
667 case typeCFStringRef:
668 err = GetEventParameter (event, names[i], typeCFStringRef, NULL,
669 sizeof (CFStringRef), NULL, &string);
670 if (err != noErr)
671 break;
672 data = CFStringCreateExternalRepresentation (NULL, string,
673 kCFStringEncodingUTF8,
674 '?');
675 if (data == NULL)
676 break;
677 /* typeUTF8Text is not available on Mac OS X 10.1. */
678 AEPutParamPtr (result, names[i], 'utf8',
679 CFDataGetBytePtr (data), CFDataGetLength (data));
680 CFRelease (data);
681 break;
682 #endif
684 default:
685 err = GetEventParameter (event, names[i], types[i], NULL,
686 0, &size, NULL);
687 if (err != noErr)
688 break;
689 buf = xmalloc (size);
690 if (buf == NULL)
691 break;
692 err = GetEventParameter (event, names[i], types[i], NULL,
693 size, NULL, buf);
694 if (err == noErr)
695 AEPutParamPtr (result, names[i], types[i], buf, size);
696 xfree (buf);
697 break;
700 return noErr;
702 #endif
705 /***********************************************************************
706 Conversion between Lisp and Core Foundation objects
707 ***********************************************************************/
709 #if TARGET_API_MAC_CARBON
710 static Lisp_Object Qstring, Qnumber, Qboolean, Qdate, Qdata;
711 static Lisp_Object Qarray, Qdictionary;
713 struct cfdict_context
715 Lisp_Object *result;
716 int with_tag, hash_bound;
719 /* C string to CFString. */
721 CFStringRef
722 cfstring_create_with_utf8_cstring (c_str)
723 const char *c_str;
725 CFStringRef str;
727 str = CFStringCreateWithCString (NULL, c_str, kCFStringEncodingUTF8);
728 if (str == NULL)
729 /* Failed to interpret as UTF 8. Fall back on Mac Roman. */
730 str = CFStringCreateWithCString (NULL, c_str, kCFStringEncodingMacRoman);
732 return str;
736 /* Lisp string to CFString. */
738 CFStringRef
739 cfstring_create_with_string (s)
740 Lisp_Object s;
742 CFStringRef string = NULL;
744 if (STRING_MULTIBYTE (s))
746 char *p, *end = SDATA (s) + SBYTES (s);
748 for (p = SDATA (s); p < end; p++)
749 if (!isascii (*p))
751 s = ENCODE_UTF_8 (s);
752 break;
754 string = CFStringCreateWithBytes (NULL, SDATA (s), SBYTES (s),
755 kCFStringEncodingUTF8, false);
758 if (string == NULL)
759 /* Failed to interpret as UTF 8. Fall back on Mac Roman. */
760 string = CFStringCreateWithBytes (NULL, SDATA (s), SBYTES (s),
761 kCFStringEncodingMacRoman, false);
763 return string;
767 /* From CFData to a lisp string. Always returns a unibyte string. */
769 Lisp_Object
770 cfdata_to_lisp (data)
771 CFDataRef data;
773 CFIndex len = CFDataGetLength (data);
774 Lisp_Object result = make_uninit_string (len);
776 CFDataGetBytes (data, CFRangeMake (0, len), SDATA (result));
778 return result;
782 /* From CFString to a lisp string. Returns a unibyte string
783 containing a UTF-8 byte sequence. */
785 Lisp_Object
786 cfstring_to_lisp_nodecode (string)
787 CFStringRef string;
789 Lisp_Object result = Qnil;
790 const char *s = CFStringGetCStringPtr (string, kCFStringEncodingUTF8);
792 if (s)
793 result = make_unibyte_string (s, strlen (s));
794 else
796 CFDataRef data =
797 CFStringCreateExternalRepresentation (NULL, string,
798 kCFStringEncodingUTF8, '?');
800 if (data)
802 result = cfdata_to_lisp (data);
803 CFRelease (data);
807 return result;
811 /* From CFString to a lisp string. Never returns a unibyte string
812 (even if it only contains ASCII characters).
813 This may cause GC during code conversion. */
815 Lisp_Object
816 cfstring_to_lisp (string)
817 CFStringRef string;
819 Lisp_Object result = cfstring_to_lisp_nodecode (string);
821 if (!NILP (result))
823 result = code_convert_string_norecord (result, Qutf_8, 0);
824 /* This may be superfluous. Just to make sure that the result
825 is a multibyte string. */
826 result = string_to_multibyte (result);
829 return result;
833 /* CFNumber to a lisp integer or a lisp float. */
835 Lisp_Object
836 cfnumber_to_lisp (number)
837 CFNumberRef number;
839 Lisp_Object result = Qnil;
840 #if BITS_PER_EMACS_INT > 32
841 SInt64 int_val;
842 CFNumberType emacs_int_type = kCFNumberSInt64Type;
843 #else
844 SInt32 int_val;
845 CFNumberType emacs_int_type = kCFNumberSInt32Type;
846 #endif
847 double float_val;
849 if (CFNumberGetValue (number, emacs_int_type, &int_val)
850 && !FIXNUM_OVERFLOW_P (int_val))
851 result = make_number (int_val);
852 else
853 if (CFNumberGetValue (number, kCFNumberDoubleType, &float_val))
854 result = make_float (float_val);
855 return result;
859 /* CFDate to a list of three integers as in a return value of
860 `current-time'. */
862 Lisp_Object
863 cfdate_to_lisp (date)
864 CFDateRef date;
866 static const CFGregorianDate epoch_gdate = {1970, 1, 1, 0, 0, 0.0};
867 static CFAbsoluteTime epoch = 0.0, sec;
868 int high, low;
870 if (epoch == 0.0)
871 epoch = CFGregorianDateGetAbsoluteTime (epoch_gdate, NULL);
873 sec = CFDateGetAbsoluteTime (date) - epoch;
874 high = sec / 65536.0;
875 low = sec - high * 65536.0;
877 return list3 (make_number (high), make_number (low), make_number (0));
881 /* CFBoolean to a lisp symbol, `t' or `nil'. */
883 Lisp_Object
884 cfboolean_to_lisp (boolean)
885 CFBooleanRef boolean;
887 return CFBooleanGetValue (boolean) ? Qt : Qnil;
891 /* Any Core Foundation object to a (lengthy) lisp string. */
893 Lisp_Object
894 cfobject_desc_to_lisp (object)
895 CFTypeRef object;
897 Lisp_Object result = Qnil;
898 CFStringRef desc = CFCopyDescription (object);
900 if (desc)
902 result = cfstring_to_lisp (desc);
903 CFRelease (desc);
906 return result;
910 /* Callback functions for cfproperty_list_to_lisp. */
912 static void
913 cfdictionary_add_to_list (key, value, context)
914 const void *key;
915 const void *value;
916 void *context;
918 struct cfdict_context *cxt = (struct cfdict_context *)context;
920 *cxt->result =
921 Fcons (Fcons (cfstring_to_lisp (key),
922 cfproperty_list_to_lisp (value, cxt->with_tag,
923 cxt->hash_bound)),
924 *cxt->result);
927 static void
928 cfdictionary_puthash (key, value, context)
929 const void *key;
930 const void *value;
931 void *context;
933 Lisp_Object lisp_key = cfstring_to_lisp (key);
934 struct cfdict_context *cxt = (struct cfdict_context *)context;
935 struct Lisp_Hash_Table *h = XHASH_TABLE (*(cxt->result));
936 unsigned hash_code;
938 hash_lookup (h, lisp_key, &hash_code);
939 hash_put (h, lisp_key,
940 cfproperty_list_to_lisp (value, cxt->with_tag, cxt->hash_bound),
941 hash_code);
945 /* Convert CFPropertyList PLIST to a lisp object. If WITH_TAG is
946 non-zero, a symbol that represents the type of the original Core
947 Foundation object is prepended. HASH_BOUND specifies which kinds
948 of the lisp objects, alists or hash tables, are used as the targets
949 of the conversion from CFDictionary. If HASH_BOUND is negative,
950 always generate alists. If HASH_BOUND >= 0, generate an alist if
951 the number of keys in the dictionary is smaller than HASH_BOUND,
952 and a hash table otherwise. */
954 Lisp_Object
955 cfproperty_list_to_lisp (plist, with_tag, hash_bound)
956 CFPropertyListRef plist;
957 int with_tag, hash_bound;
959 CFTypeID type_id = CFGetTypeID (plist);
960 Lisp_Object tag = Qnil, result = Qnil;
961 struct gcpro gcpro1, gcpro2;
963 GCPRO2 (tag, result);
965 if (type_id == CFStringGetTypeID ())
967 tag = Qstring;
968 result = cfstring_to_lisp (plist);
970 else if (type_id == CFNumberGetTypeID ())
972 tag = Qnumber;
973 result = cfnumber_to_lisp (plist);
975 else if (type_id == CFBooleanGetTypeID ())
977 tag = Qboolean;
978 result = cfboolean_to_lisp (plist);
980 else if (type_id == CFDateGetTypeID ())
982 tag = Qdate;
983 result = cfdate_to_lisp (plist);
985 else if (type_id == CFDataGetTypeID ())
987 tag = Qdata;
988 result = cfdata_to_lisp (plist);
990 else if (type_id == CFArrayGetTypeID ())
992 CFIndex index, count = CFArrayGetCount (plist);
994 tag = Qarray;
995 result = Fmake_vector (make_number (count), Qnil);
996 for (index = 0; index < count; index++)
997 XVECTOR (result)->contents[index] =
998 cfproperty_list_to_lisp (CFArrayGetValueAtIndex (plist, index),
999 with_tag, hash_bound);
1001 else if (type_id == CFDictionaryGetTypeID ())
1003 struct cfdict_context context;
1004 CFIndex count = CFDictionaryGetCount (plist);
1006 tag = Qdictionary;
1007 context.result = &result;
1008 context.with_tag = with_tag;
1009 context.hash_bound = hash_bound;
1010 if (hash_bound < 0 || count < hash_bound)
1012 result = Qnil;
1013 CFDictionaryApplyFunction (plist, cfdictionary_add_to_list,
1014 &context);
1016 else
1018 result = make_hash_table (Qequal,
1019 make_number (count),
1020 make_float (DEFAULT_REHASH_SIZE),
1021 make_float (DEFAULT_REHASH_THRESHOLD),
1022 Qnil, Qnil, Qnil);
1023 CFDictionaryApplyFunction (plist, cfdictionary_puthash,
1024 &context);
1027 else
1028 abort ();
1030 UNGCPRO;
1032 if (with_tag)
1033 result = Fcons (tag, result);
1035 return result;
1037 #endif
1040 /***********************************************************************
1041 Emulation of the X Resource Manager
1042 ***********************************************************************/
1044 /* Parser functions for resource lines. Each function takes an
1045 address of a variable whose value points to the head of a string.
1046 The value will be advanced so that it points to the next character
1047 of the parsed part when the function returns.
1049 A resource name such as "Emacs*font" is parsed into a non-empty
1050 list called `quarks'. Each element is either a Lisp string that
1051 represents a concrete component, a Lisp symbol LOOSE_BINDING
1052 (actually Qlambda) that represents any number (>=0) of intervening
1053 components, or a Lisp symbol SINGLE_COMPONENT (actually Qquote)
1054 that represents as any single component. */
1056 #define P (*p)
1058 #define LOOSE_BINDING Qlambda /* '*' ("L"oose) */
1059 #define SINGLE_COMPONENT Qquote /* '?' ("Q"uestion) */
1061 static void
1062 skip_white_space (p)
1063 char **p;
1065 /* WhiteSpace = {<space> | <horizontal tab>} */
1066 while (*P == ' ' || *P == '\t')
1067 P++;
1070 static int
1071 parse_comment (p)
1072 char **p;
1074 /* Comment = "!" {<any character except null or newline>} */
1075 if (*P == '!')
1077 P++;
1078 while (*P)
1079 if (*P++ == '\n')
1080 break;
1081 return 1;
1083 else
1084 return 0;
1087 /* Don't interpret filename. Just skip until the newline. */
1088 static int
1089 parse_include_file (p)
1090 char **p;
1092 /* IncludeFile = "#" WhiteSpace "include" WhiteSpace FileName WhiteSpace */
1093 if (*P == '#')
1095 P++;
1096 while (*P)
1097 if (*P++ == '\n')
1098 break;
1099 return 1;
1101 else
1102 return 0;
1105 static char
1106 parse_binding (p)
1107 char **p;
1109 /* Binding = "." | "*" */
1110 if (*P == '.' || *P == '*')
1112 char binding = *P++;
1114 while (*P == '.' || *P == '*')
1115 if (*P++ == '*')
1116 binding = '*';
1117 return binding;
1119 else
1120 return '\0';
1123 static Lisp_Object
1124 parse_component (p)
1125 char **p;
1127 /* Component = "?" | ComponentName
1128 ComponentName = NameChar {NameChar}
1129 NameChar = "a"-"z" | "A"-"Z" | "0"-"9" | "_" | "-" */
1130 if (*P == '?')
1132 P++;
1133 return SINGLE_COMPONENT;
1135 else if (isalnum (*P) || *P == '_' || *P == '-')
1137 char *start = P++;
1139 while (isalnum (*P) || *P == '_' || *P == '-')
1140 P++;
1142 return make_unibyte_string (start, P - start);
1144 else
1145 return Qnil;
1148 static Lisp_Object
1149 parse_resource_name (p)
1150 char **p;
1152 Lisp_Object result = Qnil, component;
1153 char binding;
1155 /* ResourceName = [Binding] {Component Binding} ComponentName */
1156 if (parse_binding (p) == '*')
1157 result = Fcons (LOOSE_BINDING, result);
1159 component = parse_component (p);
1160 if (NILP (component))
1161 return Qnil;
1163 result = Fcons (component, result);
1164 while ((binding = parse_binding (p)) != '\0')
1166 if (binding == '*')
1167 result = Fcons (LOOSE_BINDING, result);
1168 component = parse_component (p);
1169 if (NILP (component))
1170 return Qnil;
1171 else
1172 result = Fcons (component, result);
1175 /* The final component should not be '?'. */
1176 if (EQ (component, SINGLE_COMPONENT))
1177 return Qnil;
1179 return Fnreverse (result);
1182 static Lisp_Object
1183 parse_value (p)
1184 char **p;
1186 char *q, *buf;
1187 Lisp_Object seq = Qnil, result;
1188 int buf_len, total_len = 0, len, continue_p;
1190 q = strchr (P, '\n');
1191 buf_len = q ? q - P : strlen (P);
1192 buf = xmalloc (buf_len);
1194 while (1)
1196 q = buf;
1197 continue_p = 0;
1198 while (*P)
1200 if (*P == '\n')
1202 P++;
1203 break;
1205 else if (*P == '\\')
1207 P++;
1208 if (*P == '\0')
1209 break;
1210 else if (*P == '\n')
1212 P++;
1213 continue_p = 1;
1214 break;
1216 else if (*P == 'n')
1218 *q++ = '\n';
1219 P++;
1221 else if ('0' <= P[0] && P[0] <= '7'
1222 && '0' <= P[1] && P[1] <= '7'
1223 && '0' <= P[2] && P[2] <= '7')
1225 *q++ = (P[0] - '0' << 6) + (P[1] - '0' << 3) + (P[2] - '0');
1226 P += 3;
1228 else
1229 *q++ = *P++;
1231 else
1232 *q++ = *P++;
1234 len = q - buf;
1235 seq = Fcons (make_unibyte_string (buf, len), seq);
1236 total_len += len;
1238 if (continue_p)
1240 q = strchr (P, '\n');
1241 len = q ? q - P : strlen (P);
1242 if (len > buf_len)
1244 xfree (buf);
1245 buf_len = len;
1246 buf = xmalloc (buf_len);
1249 else
1250 break;
1252 xfree (buf);
1254 if (SBYTES (XCAR (seq)) == total_len)
1255 return make_string (SDATA (XCAR (seq)), total_len);
1256 else
1258 buf = xmalloc (total_len);
1259 q = buf + total_len;
1260 for (; CONSP (seq); seq = XCDR (seq))
1262 len = SBYTES (XCAR (seq));
1263 q -= len;
1264 memcpy (q, SDATA (XCAR (seq)), len);
1266 result = make_string (buf, total_len);
1267 xfree (buf);
1268 return result;
1272 static Lisp_Object
1273 parse_resource_line (p)
1274 char **p;
1276 Lisp_Object quarks, value;
1278 /* ResourceLine = Comment | IncludeFile | ResourceSpec | <empty line> */
1279 if (parse_comment (p) || parse_include_file (p))
1280 return Qnil;
1282 /* ResourceSpec = WhiteSpace ResourceName WhiteSpace ":" WhiteSpace Value */
1283 skip_white_space (p);
1284 quarks = parse_resource_name (p);
1285 if (NILP (quarks))
1286 goto cleanup;
1287 skip_white_space (p);
1288 if (*P != ':')
1289 goto cleanup;
1290 P++;
1291 skip_white_space (p);
1292 value = parse_value (p);
1293 return Fcons (quarks, value);
1295 cleanup:
1296 /* Skip the remaining data as a dummy value. */
1297 parse_value (p);
1298 return Qnil;
1301 #undef P
1303 /* Equivalents of X Resource Manager functions.
1305 An X Resource Database acts as a collection of resource names and
1306 associated values. It is implemented as a trie on quarks. Namely,
1307 each edge is labeled by either a string, LOOSE_BINDING, or
1308 SINGLE_COMPONENT. Each node has a node id, which is a unique
1309 nonnegative integer, and the root node id is 0. A database is
1310 implemented as a hash table that maps a pair (SRC-NODE-ID .
1311 EDGE-LABEL) to DEST-NODE-ID. It also holds a maximum node id used
1312 in the table as a value for HASHKEY_MAX_NID. A value associated to
1313 a node is recorded as a value for the node id.
1315 A database also has a cache for past queries as a value for
1316 HASHKEY_QUERY_CACHE. It is another hash table that maps
1317 "NAME-STRING\0CLASS-STRING" to the result of the query. */
1319 #define HASHKEY_MAX_NID (make_number (0))
1320 #define HASHKEY_QUERY_CACHE (make_number (-1))
1322 static XrmDatabase
1323 xrm_create_database ()
1325 XrmDatabase database;
1327 database = make_hash_table (Qequal, make_number (DEFAULT_HASH_SIZE),
1328 make_float (DEFAULT_REHASH_SIZE),
1329 make_float (DEFAULT_REHASH_THRESHOLD),
1330 Qnil, Qnil, Qnil);
1331 Fputhash (HASHKEY_MAX_NID, make_number (0), database);
1332 Fputhash (HASHKEY_QUERY_CACHE, Qnil, database);
1334 return database;
1337 static void
1338 xrm_q_put_resource (database, quarks, value)
1339 XrmDatabase database;
1340 Lisp_Object quarks, value;
1342 struct Lisp_Hash_Table *h = XHASH_TABLE (database);
1343 unsigned hash_code;
1344 int max_nid, i;
1345 Lisp_Object node_id, key;
1347 max_nid = XINT (Fgethash (HASHKEY_MAX_NID, database, Qnil));
1349 XSETINT (node_id, 0);
1350 for (; CONSP (quarks); quarks = XCDR (quarks))
1352 key = Fcons (node_id, XCAR (quarks));
1353 i = hash_lookup (h, key, &hash_code);
1354 if (i < 0)
1356 max_nid++;
1357 XSETINT (node_id, max_nid);
1358 hash_put (h, key, node_id, hash_code);
1360 else
1361 node_id = HASH_VALUE (h, i);
1363 Fputhash (node_id, value, database);
1365 Fputhash (HASHKEY_MAX_NID, make_number (max_nid), database);
1366 Fputhash (HASHKEY_QUERY_CACHE, Qnil, database);
1369 /* Merge multiple resource entries specified by DATA into a resource
1370 database DATABASE. DATA points to the head of a null-terminated
1371 string consisting of multiple resource lines. It's like a
1372 combination of XrmGetStringDatabase and XrmMergeDatabases. */
1374 void
1375 xrm_merge_string_database (database, data)
1376 XrmDatabase database;
1377 char *data;
1379 Lisp_Object quarks_value;
1381 while (*data)
1383 quarks_value = parse_resource_line (&data);
1384 if (!NILP (quarks_value))
1385 xrm_q_put_resource (database,
1386 XCAR (quarks_value), XCDR (quarks_value));
1390 static Lisp_Object
1391 xrm_q_get_resource_1 (database, node_id, quark_name, quark_class)
1392 XrmDatabase database;
1393 Lisp_Object node_id, quark_name, quark_class;
1395 struct Lisp_Hash_Table *h = XHASH_TABLE (database);
1396 Lisp_Object key, labels[3], value;
1397 int i, k;
1399 if (!CONSP (quark_name))
1400 return Fgethash (node_id, database, Qnil);
1402 /* First, try tight bindings */
1403 labels[0] = XCAR (quark_name);
1404 labels[1] = XCAR (quark_class);
1405 labels[2] = SINGLE_COMPONENT;
1407 key = Fcons (node_id, Qnil);
1408 for (k = 0; k < sizeof (labels) / sizeof (*labels); k++)
1410 XSETCDR (key, labels[k]);
1411 i = hash_lookup (h, key, NULL);
1412 if (i >= 0)
1414 value = xrm_q_get_resource_1 (database, HASH_VALUE (h, i),
1415 XCDR (quark_name), XCDR (quark_class));
1416 if (!NILP (value))
1417 return value;
1421 /* Then, try loose bindings */
1422 XSETCDR (key, LOOSE_BINDING);
1423 i = hash_lookup (h, key, NULL);
1424 if (i >= 0)
1426 value = xrm_q_get_resource_1 (database, HASH_VALUE (h, i),
1427 quark_name, quark_class);
1428 if (!NILP (value))
1429 return value;
1430 else
1431 return xrm_q_get_resource_1 (database, node_id,
1432 XCDR (quark_name), XCDR (quark_class));
1434 else
1435 return Qnil;
1438 static Lisp_Object
1439 xrm_q_get_resource (database, quark_name, quark_class)
1440 XrmDatabase database;
1441 Lisp_Object quark_name, quark_class;
1443 return xrm_q_get_resource_1 (database, make_number (0),
1444 quark_name, quark_class);
1447 /* Retrieve a resource value for the specified NAME and CLASS from the
1448 resource database DATABASE. It corresponds to XrmGetResource. */
1450 Lisp_Object
1451 xrm_get_resource (database, name, class)
1452 XrmDatabase database;
1453 char *name, *class;
1455 Lisp_Object key, query_cache, quark_name, quark_class, tmp;
1456 int i, nn, nc;
1457 struct Lisp_Hash_Table *h;
1458 unsigned hash_code;
1460 nn = strlen (name);
1461 nc = strlen (class);
1462 key = make_uninit_string (nn + nc + 1);
1463 strcpy (SDATA (key), name);
1464 strncpy (SDATA (key) + nn + 1, class, nc);
1466 query_cache = Fgethash (HASHKEY_QUERY_CACHE, database, Qnil);
1467 if (NILP (query_cache))
1469 query_cache = make_hash_table (Qequal, make_number (DEFAULT_HASH_SIZE),
1470 make_float (DEFAULT_REHASH_SIZE),
1471 make_float (DEFAULT_REHASH_THRESHOLD),
1472 Qnil, Qnil, Qnil);
1473 Fputhash (HASHKEY_QUERY_CACHE, query_cache, database);
1475 h = XHASH_TABLE (query_cache);
1476 i = hash_lookup (h, key, &hash_code);
1477 if (i >= 0)
1478 return HASH_VALUE (h, i);
1480 quark_name = parse_resource_name (&name);
1481 if (*name != '\0')
1482 return Qnil;
1483 for (tmp = quark_name, nn = 0; CONSP (tmp); tmp = XCDR (tmp), nn++)
1484 if (!STRINGP (XCAR (tmp)))
1485 return Qnil;
1487 quark_class = parse_resource_name (&class);
1488 if (*class != '\0')
1489 return Qnil;
1490 for (tmp = quark_class, nc = 0; CONSP (tmp); tmp = XCDR (tmp), nc++)
1491 if (!STRINGP (XCAR (tmp)))
1492 return Qnil;
1494 if (nn != nc)
1495 return Qnil;
1496 else
1498 tmp = xrm_q_get_resource (database, quark_name, quark_class);
1499 hash_put (h, key, tmp, hash_code);
1500 return tmp;
1504 #if TARGET_API_MAC_CARBON
1505 static Lisp_Object
1506 xrm_cfproperty_list_to_value (plist)
1507 CFPropertyListRef plist;
1509 CFTypeID type_id = CFGetTypeID (plist);
1511 if (type_id == CFStringGetTypeID ())
1512 return cfstring_to_lisp (plist);
1513 else if (type_id == CFNumberGetTypeID ())
1515 CFStringRef string;
1516 Lisp_Object result = Qnil;
1518 string = CFStringCreateWithFormat (NULL, NULL, CFSTR ("%@"), plist);
1519 if (string)
1521 result = cfstring_to_lisp (string);
1522 CFRelease (string);
1524 return result;
1526 else if (type_id == CFBooleanGetTypeID ())
1527 return build_string (CFBooleanGetValue (plist) ? "true" : "false");
1528 else if (type_id == CFDataGetTypeID ())
1529 return cfdata_to_lisp (plist);
1530 else
1531 return Qnil;
1533 #endif
1535 /* Create a new resource database from the preferences for the
1536 application APPLICATION. APPLICATION is either a string that
1537 specifies an application ID, or NULL that represents the current
1538 application. */
1540 XrmDatabase
1541 xrm_get_preference_database (application)
1542 char *application;
1544 #if TARGET_API_MAC_CARBON
1545 CFStringRef app_id, *keys, user_doms[2], host_doms[2];
1546 CFMutableSetRef key_set = NULL;
1547 CFArrayRef key_array;
1548 CFIndex index, count;
1549 char *res_name;
1550 XrmDatabase database;
1551 Lisp_Object quarks = Qnil, value = Qnil;
1552 CFPropertyListRef plist;
1553 int iu, ih;
1554 struct gcpro gcpro1, gcpro2, gcpro3;
1556 user_doms[0] = kCFPreferencesCurrentUser;
1557 user_doms[1] = kCFPreferencesAnyUser;
1558 host_doms[0] = kCFPreferencesCurrentHost;
1559 host_doms[1] = kCFPreferencesAnyHost;
1561 database = xrm_create_database ();
1563 GCPRO3 (database, quarks, value);
1565 BLOCK_INPUT;
1567 app_id = kCFPreferencesCurrentApplication;
1568 if (application)
1570 app_id = cfstring_create_with_utf8_cstring (application);
1571 if (app_id == NULL)
1572 goto out;
1575 key_set = CFSetCreateMutable (NULL, 0, &kCFCopyStringSetCallBacks);
1576 if (key_set == NULL)
1577 goto out;
1578 for (iu = 0; iu < sizeof (user_doms) / sizeof (*user_doms) ; iu++)
1579 for (ih = 0; ih < sizeof (host_doms) / sizeof (*host_doms); ih++)
1581 key_array = CFPreferencesCopyKeyList (app_id, user_doms[iu],
1582 host_doms[ih]);
1583 if (key_array)
1585 count = CFArrayGetCount (key_array);
1586 for (index = 0; index < count; index++)
1587 CFSetAddValue (key_set,
1588 CFArrayGetValueAtIndex (key_array, index));
1589 CFRelease (key_array);
1593 count = CFSetGetCount (key_set);
1594 keys = xmalloc (sizeof (CFStringRef) * count);
1595 if (keys == NULL)
1596 goto out;
1597 CFSetGetValues (key_set, (const void **)keys);
1598 for (index = 0; index < count; index++)
1600 res_name = SDATA (cfstring_to_lisp_nodecode (keys[index]));
1601 quarks = parse_resource_name (&res_name);
1602 if (!(NILP (quarks) || *res_name))
1604 plist = CFPreferencesCopyAppValue (keys[index], app_id);
1605 value = xrm_cfproperty_list_to_value (plist);
1606 CFRelease (plist);
1607 if (!NILP (value))
1608 xrm_q_put_resource (database, quarks, value);
1612 xfree (keys);
1613 out:
1614 if (key_set)
1615 CFRelease (key_set);
1616 CFRelease (app_id);
1618 UNBLOCK_INPUT;
1620 UNGCPRO;
1622 return database;
1623 #else
1624 return xrm_create_database ();
1625 #endif
1629 #ifndef MAC_OSX
1631 /* The following functions with "sys_" prefix are stubs to Unix
1632 functions that have already been implemented by CW or MPW. The
1633 calls to them in Emacs source course are #define'd to call the sys_
1634 versions by the header files s-mac.h. In these stubs pathnames are
1635 converted between their Unix and Mac forms. */
1638 /* Unix epoch is Jan 1, 1970 while Mac epoch is Jan 1, 1904: 66 years
1639 + 17 leap days. These are for adjusting time values returned by
1640 MacOS Toolbox functions. */
1642 #define MAC_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
1644 #ifdef __MWERKS__
1645 #if __MSL__ < 0x6000
1646 /* CW Pro 5 epoch is Jan 1, 1900 (aaarghhhhh!); remember, 1900 is not
1647 a leap year! This is for adjusting time_t values returned by MSL
1648 functions. */
1649 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 70 + 17) * 24 * 60 * 60)
1650 #else /* __MSL__ >= 0x6000 */
1651 /* CW changes Pro 6 to follow Unix! */
1652 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
1653 #endif /* __MSL__ >= 0x6000 */
1654 #elif __MRC__
1655 /* MPW library functions follow Unix (confused?). */
1656 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
1657 #else /* not __MRC__ */
1658 You lose!!!
1659 #endif /* not __MRC__ */
1662 /* Define our own stat function for both MrC and CW. The reason for
1663 doing this: "stat" is both the name of a struct and function name:
1664 can't use the same trick like that for sys_open, sys_close, etc. to
1665 redirect Emacs's calls to our own version that converts Unix style
1666 filenames to Mac style filename because all sorts of compilation
1667 errors will be generated if stat is #define'd to be sys_stat. */
1670 stat_noalias (const char *path, struct stat *buf)
1672 char mac_pathname[MAXPATHLEN+1];
1673 CInfoPBRec cipb;
1675 if (posix_to_mac_pathname (path, mac_pathname, MAXPATHLEN+1) == 0)
1676 return -1;
1678 c2pstr (mac_pathname);
1679 cipb.hFileInfo.ioNamePtr = mac_pathname;
1680 cipb.hFileInfo.ioVRefNum = 0;
1681 cipb.hFileInfo.ioDirID = 0;
1682 cipb.hFileInfo.ioFDirIndex = 0;
1683 /* set to 0 to get information about specific dir or file */
1685 errno = PBGetCatInfo (&cipb, false);
1686 if (errno == -43) /* -43: fnfErr defined in Errors.h */
1687 errno = ENOENT;
1688 if (errno != noErr)
1689 return -1;
1691 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* bit 4 = 1 for directories */
1693 buf->st_mode = S_IFDIR | S_IREAD | S_IEXEC;
1695 if (!(cipb.hFileInfo.ioFlAttrib & 0x1))
1696 buf->st_mode |= S_IWRITE; /* bit 1 = 1 for locked files/directories */
1697 buf->st_ino = cipb.dirInfo.ioDrDirID;
1698 buf->st_dev = cipb.dirInfo.ioVRefNum;
1699 buf->st_size = cipb.dirInfo.ioDrNmFls;
1700 /* size of dir = number of files and dirs */
1701 buf->st_atime
1702 = buf->st_mtime
1703 = cipb.dirInfo.ioDrMdDat - MAC_UNIX_EPOCH_DIFF;
1704 buf->st_ctime = cipb.dirInfo.ioDrCrDat - MAC_UNIX_EPOCH_DIFF;
1706 else
1708 buf->st_mode = S_IFREG | S_IREAD;
1709 if (!(cipb.hFileInfo.ioFlAttrib & 0x1))
1710 buf->st_mode |= S_IWRITE; /* bit 1 = 1 for locked files/directories */
1711 if (cipb.hFileInfo.ioFlFndrInfo.fdType == 'APPL')
1712 buf->st_mode |= S_IEXEC;
1713 buf->st_ino = cipb.hFileInfo.ioDirID;
1714 buf->st_dev = cipb.hFileInfo.ioVRefNum;
1715 buf->st_size = cipb.hFileInfo.ioFlLgLen;
1716 buf->st_atime
1717 = buf->st_mtime
1718 = cipb.hFileInfo.ioFlMdDat - MAC_UNIX_EPOCH_DIFF;
1719 buf->st_ctime = cipb.hFileInfo.ioFlCrDat - MAC_UNIX_EPOCH_DIFF;
1722 if (cipb.hFileInfo.ioFlFndrInfo.fdFlags & 0x8000)
1724 /* identify alias files as symlinks */
1725 buf->st_mode &= ~S_IFREG;
1726 buf->st_mode |= S_IFLNK;
1729 buf->st_nlink = 1;
1730 buf->st_uid = getuid ();
1731 buf->st_gid = getgid ();
1732 buf->st_rdev = 0;
1734 return 0;
1739 lstat (const char *path, struct stat *buf)
1741 int result;
1742 char true_pathname[MAXPATHLEN+1];
1744 /* Try looking for the file without resolving aliases first. */
1745 if ((result = stat_noalias (path, buf)) >= 0)
1746 return result;
1748 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1749 return -1;
1751 return stat_noalias (true_pathname, buf);
1756 stat (const char *path, struct stat *sb)
1758 int result;
1759 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
1760 int len;
1762 if ((result = stat_noalias (path, sb)) >= 0 &&
1763 ! (sb->st_mode & S_IFLNK))
1764 return result;
1766 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1767 return -1;
1769 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
1770 if (len > -1)
1772 fully_resolved_name[len] = '\0';
1773 /* in fact our readlink terminates strings */
1774 return lstat (fully_resolved_name, sb);
1776 else
1777 return lstat (true_pathname, sb);
1781 #if __MRC__
1782 /* CW defines fstat in stat.mac.c while MPW does not provide this
1783 function. Without the information of how to get from a file
1784 descriptor in MPW StdCLib to a Mac OS file spec, it should be hard
1785 to implement this function. Fortunately, there is only one place
1786 where this function is called in our configuration: in fileio.c,
1787 where only the st_dev and st_ino fields are used to determine
1788 whether two fildes point to different i-nodes to prevent copying
1789 a file onto itself equal. What we have here probably needs
1790 improvement. */
1793 fstat (int fildes, struct stat *buf)
1795 buf->st_dev = 0;
1796 buf->st_ino = fildes;
1797 buf->st_mode = S_IFREG; /* added by T.I. for the copy-file */
1798 return 0; /* success */
1800 #endif /* __MRC__ */
1804 mkdir (const char *dirname, int mode)
1806 #pragma unused(mode)
1808 HFileParam hfpb;
1809 char true_pathname[MAXPATHLEN+1], mac_pathname[MAXPATHLEN+1];
1811 if (find_true_pathname (dirname, true_pathname, MAXPATHLEN+1) == -1)
1812 return -1;
1814 if (posix_to_mac_pathname (true_pathname, mac_pathname, MAXPATHLEN+1) == 0)
1815 return -1;
1817 c2pstr (mac_pathname);
1818 hfpb.ioNamePtr = mac_pathname;
1819 hfpb.ioVRefNum = 0; /* ignored unless name is invalid */
1820 hfpb.ioDirID = 0; /* parent is the root */
1822 errno = PBDirCreate ((HParmBlkPtr) &hfpb, false);
1823 /* just return the Mac OSErr code for now */
1824 return errno == noErr ? 0 : -1;
1828 #undef rmdir
1829 sys_rmdir (const char *dirname)
1831 HFileParam hfpb;
1832 char mac_pathname[MAXPATHLEN+1];
1834 if (posix_to_mac_pathname (dirname, mac_pathname, MAXPATHLEN+1) == 0)
1835 return -1;
1837 c2pstr (mac_pathname);
1838 hfpb.ioNamePtr = mac_pathname;
1839 hfpb.ioVRefNum = 0; /* ignored unless name is invalid */
1840 hfpb.ioDirID = 0; /* parent is the root */
1842 errno = PBHDelete ((HParmBlkPtr) &hfpb, false);
1843 return errno == noErr ? 0 : -1;
1847 #ifdef __MRC__
1848 /* No implementation yet. */
1850 execvp (const char *path, ...)
1852 return -1;
1854 #endif /* __MRC__ */
1858 utime (const char *path, const struct utimbuf *times)
1860 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
1861 int len;
1862 char mac_pathname[MAXPATHLEN+1];
1863 CInfoPBRec cipb;
1865 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1866 return -1;
1868 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
1869 if (len > -1)
1870 fully_resolved_name[len] = '\0';
1871 else
1872 strcpy (fully_resolved_name, true_pathname);
1874 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
1875 return -1;
1877 c2pstr (mac_pathname);
1878 cipb.hFileInfo.ioNamePtr = mac_pathname;
1879 cipb.hFileInfo.ioVRefNum = 0;
1880 cipb.hFileInfo.ioDirID = 0;
1881 cipb.hFileInfo.ioFDirIndex = 0;
1882 /* set to 0 to get information about specific dir or file */
1884 errno = PBGetCatInfo (&cipb, false);
1885 if (errno != noErr)
1886 return -1;
1888 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* bit 4 = 1 for directories */
1890 if (times)
1891 cipb.dirInfo.ioDrMdDat = times->modtime + MAC_UNIX_EPOCH_DIFF;
1892 else
1893 GetDateTime (&cipb.dirInfo.ioDrMdDat);
1895 else
1897 if (times)
1898 cipb.hFileInfo.ioFlMdDat = times->modtime + MAC_UNIX_EPOCH_DIFF;
1899 else
1900 GetDateTime (&cipb.hFileInfo.ioFlMdDat);
1903 errno = PBSetCatInfo (&cipb, false);
1904 return errno == noErr ? 0 : -1;
1908 #ifndef F_OK
1909 #define F_OK 0
1910 #endif
1911 #ifndef X_OK
1912 #define X_OK 1
1913 #endif
1914 #ifndef W_OK
1915 #define W_OK 2
1916 #endif
1918 /* Like stat, but test for access mode in hfpb.ioFlAttrib */
1920 access (const char *path, int mode)
1922 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
1923 int len;
1924 char mac_pathname[MAXPATHLEN+1];
1925 CInfoPBRec cipb;
1927 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1928 return -1;
1930 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
1931 if (len > -1)
1932 fully_resolved_name[len] = '\0';
1933 else
1934 strcpy (fully_resolved_name, true_pathname);
1936 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
1937 return -1;
1939 c2pstr (mac_pathname);
1940 cipb.hFileInfo.ioNamePtr = mac_pathname;
1941 cipb.hFileInfo.ioVRefNum = 0;
1942 cipb.hFileInfo.ioDirID = 0;
1943 cipb.hFileInfo.ioFDirIndex = 0;
1944 /* set to 0 to get information about specific dir or file */
1946 errno = PBGetCatInfo (&cipb, false);
1947 if (errno != noErr)
1948 return -1;
1950 if (mode == F_OK) /* got this far, file exists */
1951 return 0;
1953 if (mode & X_OK)
1954 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* path refers to a directory */
1955 return 0;
1956 else
1958 if (cipb.hFileInfo.ioFlFndrInfo.fdType == 'APPL')
1959 return 0;
1960 else
1961 return -1;
1964 if (mode & W_OK)
1965 return (cipb.hFileInfo.ioFlAttrib & 0x1) ? -1 : 0;
1966 /* don't allow if lock bit is on */
1968 return -1;
1972 #define DEV_NULL_FD 0x10000
1974 #undef open
1976 sys_open (const char *path, int oflag)
1978 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
1979 int len;
1980 char mac_pathname[MAXPATHLEN+1];
1982 if (strcmp (path, "/dev/null") == 0)
1983 return DEV_NULL_FD; /* some bogus fd to be ignored in write */
1985 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1986 return -1;
1988 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
1989 if (len > -1)
1990 fully_resolved_name[len] = '\0';
1991 else
1992 strcpy (fully_resolved_name, true_pathname);
1994 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
1995 return -1;
1996 else
1998 #ifdef __MRC__
1999 int res = open (mac_pathname, oflag);
2000 /* if (oflag == O_WRONLY || oflag == O_RDWR) */
2001 if (oflag & O_CREAT)
2002 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
2003 return res;
2004 #else /* not __MRC__ */
2005 return open (mac_pathname, oflag);
2006 #endif /* not __MRC__ */
2011 #undef creat
2013 sys_creat (const char *path, mode_t mode)
2015 char true_pathname[MAXPATHLEN+1];
2016 int len;
2017 char mac_pathname[MAXPATHLEN+1];
2019 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
2020 return -1;
2022 if (!posix_to_mac_pathname (true_pathname, mac_pathname, MAXPATHLEN+1))
2023 return -1;
2024 else
2026 #ifdef __MRC__
2027 int result = creat (mac_pathname);
2028 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
2029 return result;
2030 #else /* not __MRC__ */
2031 return creat (mac_pathname, mode);
2032 #endif /* not __MRC__ */
2037 #undef unlink
2039 sys_unlink (const char *path)
2041 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
2042 int len;
2043 char mac_pathname[MAXPATHLEN+1];
2045 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
2046 return -1;
2048 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
2049 if (len > -1)
2050 fully_resolved_name[len] = '\0';
2051 else
2052 strcpy (fully_resolved_name, true_pathname);
2054 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
2055 return -1;
2056 else
2057 return unlink (mac_pathname);
2061 #undef read
2063 sys_read (int fildes, char *buf, int count)
2065 if (fildes == 0) /* this should not be used for console input */
2066 return -1;
2067 else
2068 #if __MSL__ >= 0x6000
2069 return _read (fildes, buf, count);
2070 #else
2071 return read (fildes, buf, count);
2072 #endif
2076 #undef write
2078 sys_write (int fildes, const char *buf, int count)
2080 if (fildes == DEV_NULL_FD)
2081 return count;
2082 else
2083 #if __MSL__ >= 0x6000
2084 return _write (fildes, buf, count);
2085 #else
2086 return write (fildes, buf, count);
2087 #endif
2091 #undef rename
2093 sys_rename (const char * old_name, const char * new_name)
2095 char true_old_pathname[MAXPATHLEN+1], true_new_pathname[MAXPATHLEN+1];
2096 char fully_resolved_old_name[MAXPATHLEN+1];
2097 int len;
2098 char mac_old_name[MAXPATHLEN+1], mac_new_name[MAXPATHLEN+1];
2100 if (find_true_pathname (old_name, true_old_pathname, MAXPATHLEN+1) == -1)
2101 return -1;
2103 len = readlink (true_old_pathname, fully_resolved_old_name, MAXPATHLEN);
2104 if (len > -1)
2105 fully_resolved_old_name[len] = '\0';
2106 else
2107 strcpy (fully_resolved_old_name, true_old_pathname);
2109 if (find_true_pathname (new_name, true_new_pathname, MAXPATHLEN+1) == -1)
2110 return -1;
2112 if (strcmp (fully_resolved_old_name, true_new_pathname) == 0)
2113 return 0;
2115 if (!posix_to_mac_pathname (fully_resolved_old_name,
2116 mac_old_name,
2117 MAXPATHLEN+1))
2118 return -1;
2120 if (!posix_to_mac_pathname(true_new_pathname, mac_new_name, MAXPATHLEN+1))
2121 return -1;
2123 /* If a file with new_name already exists, rename deletes the old
2124 file in Unix. CW version fails in these situation. So we add a
2125 call to unlink here. */
2126 (void) unlink (mac_new_name);
2128 return rename (mac_old_name, mac_new_name);
2132 #undef fopen
2133 extern FILE *fopen (const char *name, const char *mode);
2134 FILE *
2135 sys_fopen (const char *name, const char *mode)
2137 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
2138 int len;
2139 char mac_pathname[MAXPATHLEN+1];
2141 if (find_true_pathname (name, true_pathname, MAXPATHLEN+1) == -1)
2142 return 0;
2144 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
2145 if (len > -1)
2146 fully_resolved_name[len] = '\0';
2147 else
2148 strcpy (fully_resolved_name, true_pathname);
2150 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
2151 return 0;
2152 else
2154 #ifdef __MRC__
2155 if (mode[0] == 'w' || mode[0] == 'a')
2156 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
2157 #endif /* not __MRC__ */
2158 return fopen (mac_pathname, mode);
2163 #include "keyboard.h"
2164 extern Boolean mac_wait_next_event (EventRecord *, UInt32, Boolean);
2167 select (n, rfds, wfds, efds, timeout)
2168 int n;
2169 SELECT_TYPE *rfds;
2170 SELECT_TYPE *wfds;
2171 SELECT_TYPE *efds;
2172 struct timeval *timeout;
2174 OSErr err;
2175 #if TARGET_API_MAC_CARBON
2176 EventTimeout timeout_sec =
2177 (timeout
2178 ? (EMACS_SECS (*timeout) * kEventDurationSecond
2179 + EMACS_USECS (*timeout) * kEventDurationMicrosecond)
2180 : kEventDurationForever);
2182 BLOCK_INPUT;
2183 err = ReceiveNextEvent (0, NULL, timeout_sec, kEventLeaveInQueue, NULL);
2184 UNBLOCK_INPUT;
2185 #else /* not TARGET_API_MAC_CARBON */
2186 EventRecord e;
2187 UInt32 sleep_time = EMACS_SECS (*timeout) * 60 +
2188 ((EMACS_USECS (*timeout) * 60) / 1000000);
2190 /* Can only handle wait for keyboard input. */
2191 if (n > 1 || wfds || efds)
2192 return -1;
2194 /* Also return true if an event other than a keyDown has occurred.
2195 This causes kbd_buffer_get_event in keyboard.c to call
2196 read_avail_input which in turn calls XTread_socket to poll for
2197 these events. Otherwise these never get processed except but a
2198 very slow poll timer. */
2199 if (mac_wait_next_event (&e, sleep_time, false))
2200 err = noErr;
2201 else
2202 err = -9875; /* eventLoopTimedOutErr */
2203 #endif /* not TARGET_API_MAC_CARBON */
2205 if (FD_ISSET (0, rfds))
2206 if (err == noErr)
2207 return 1;
2208 else
2210 FD_ZERO (rfds);
2211 return 0;
2213 else
2214 if (err == noErr)
2216 if (input_polling_used ())
2218 /* It could be confusing if a real alarm arrives while
2219 processing the fake one. Turn it off and let the
2220 handler reset it. */
2221 extern void poll_for_input_1 P_ ((void));
2222 int old_poll_suppress_count = poll_suppress_count;
2223 poll_suppress_count = 1;
2224 poll_for_input_1 ();
2225 poll_suppress_count = old_poll_suppress_count;
2227 errno = EINTR;
2228 return -1;
2230 else
2231 return 0;
2235 /* Simulation of SIGALRM. The stub for function signal stores the
2236 signal handler function in alarm_signal_func if a SIGALRM is
2237 encountered. */
2239 #include <signal.h>
2240 #include "syssignal.h"
2242 static TMTask mac_atimer_task;
2244 static QElemPtr mac_atimer_qlink = (QElemPtr) &mac_atimer_task;
2246 static int signal_mask = 0;
2248 #ifdef __MRC__
2249 __sigfun alarm_signal_func = (__sigfun) 0;
2250 #elif __MWERKS__
2251 __signal_func_ptr alarm_signal_func = (__signal_func_ptr) 0;
2252 #else /* not __MRC__ and not __MWERKS__ */
2253 You lose!!!
2254 #endif /* not __MRC__ and not __MWERKS__ */
2256 #undef signal
2257 #ifdef __MRC__
2258 extern __sigfun signal (int signal, __sigfun signal_func);
2259 __sigfun
2260 sys_signal (int signal_num, __sigfun signal_func)
2261 #elif __MWERKS__
2262 extern __signal_func_ptr signal (int signal, __signal_func_ptr signal_func);
2263 __signal_func_ptr
2264 sys_signal (int signal_num, __signal_func_ptr signal_func)
2265 #else /* not __MRC__ and not __MWERKS__ */
2266 You lose!!!
2267 #endif /* not __MRC__ and not __MWERKS__ */
2269 if (signal_num != SIGALRM)
2270 return signal (signal_num, signal_func);
2271 else
2273 #ifdef __MRC__
2274 __sigfun old_signal_func;
2275 #elif __MWERKS__
2276 __signal_func_ptr old_signal_func;
2277 #else
2278 You lose!!!
2279 #endif
2280 old_signal_func = alarm_signal_func;
2281 alarm_signal_func = signal_func;
2282 return old_signal_func;
2287 static pascal void
2288 mac_atimer_handler (qlink)
2289 TMTaskPtr qlink;
2291 if (alarm_signal_func)
2292 (alarm_signal_func) (SIGALRM);
2296 static void
2297 set_mac_atimer (count)
2298 long count;
2300 static TimerUPP mac_atimer_handlerUPP = NULL;
2302 if (mac_atimer_handlerUPP == NULL)
2303 mac_atimer_handlerUPP = NewTimerUPP (mac_atimer_handler);
2304 mac_atimer_task.tmCount = 0;
2305 mac_atimer_task.tmAddr = mac_atimer_handlerUPP;
2306 mac_atimer_qlink = (QElemPtr) &mac_atimer_task;
2307 InsTime (mac_atimer_qlink);
2308 if (count)
2309 PrimeTime (mac_atimer_qlink, count);
2314 remove_mac_atimer (remaining_count)
2315 long *remaining_count;
2317 if (mac_atimer_qlink)
2319 RmvTime (mac_atimer_qlink);
2320 if (remaining_count)
2321 *remaining_count = mac_atimer_task.tmCount;
2322 mac_atimer_qlink = NULL;
2324 return 0;
2326 else
2327 return -1;
2332 sigblock (int mask)
2334 int old_mask = signal_mask;
2336 signal_mask |= mask;
2338 if ((old_mask ^ signal_mask) & sigmask (SIGALRM))
2339 remove_mac_atimer (NULL);
2341 return old_mask;
2346 sigsetmask (int mask)
2348 int old_mask = signal_mask;
2350 signal_mask = mask;
2352 if ((old_mask ^ signal_mask) & sigmask (SIGALRM))
2353 if (signal_mask & sigmask (SIGALRM))
2354 remove_mac_atimer (NULL);
2355 else
2356 set_mac_atimer (mac_atimer_task.tmCount);
2358 return old_mask;
2363 alarm (int seconds)
2365 long remaining_count;
2367 if (remove_mac_atimer (&remaining_count) == 0)
2369 set_mac_atimer (seconds * 1000);
2371 return remaining_count / 1000;
2373 else
2375 mac_atimer_task.tmCount = seconds * 1000;
2377 return 0;
2383 setitimer (which, value, ovalue)
2384 int which;
2385 const struct itimerval *value;
2386 struct itimerval *ovalue;
2388 long remaining_count;
2389 long count = (EMACS_SECS (value->it_value) * 1000
2390 + (EMACS_USECS (value->it_value) + 999) / 1000);
2392 if (remove_mac_atimer (&remaining_count) == 0)
2394 if (ovalue)
2396 bzero (ovalue, sizeof (*ovalue));
2397 EMACS_SET_SECS_USECS (ovalue->it_value, remaining_count / 1000,
2398 (remaining_count % 1000) * 1000);
2400 set_mac_atimer (count);
2402 else
2403 mac_atimer_task.tmCount = count;
2405 return 0;
2409 /* gettimeofday should return the amount of time (in a timeval
2410 structure) since midnight today. The toolbox function Microseconds
2411 returns the number of microseconds (in a UnsignedWide value) since
2412 the machine was booted. Also making this complicated is WideAdd,
2413 WideSubtract, etc. take wide values. */
2416 gettimeofday (tp)
2417 struct timeval *tp;
2419 static inited = 0;
2420 static wide wall_clock_at_epoch, clicks_at_epoch;
2421 UnsignedWide uw_microseconds;
2422 wide w_microseconds;
2423 time_t sys_time (time_t *);
2425 /* If this function is called for the first time, record the number
2426 of seconds since midnight and the number of microseconds since
2427 boot at the time of this first call. */
2428 if (!inited)
2430 time_t systime;
2431 inited = 1;
2432 systime = sys_time (NULL);
2433 /* Store microseconds since midnight in wall_clock_at_epoch. */
2434 WideMultiply (systime, 1000000L, &wall_clock_at_epoch);
2435 Microseconds (&uw_microseconds);
2436 /* Store microseconds since boot in clicks_at_epoch. */
2437 clicks_at_epoch.hi = uw_microseconds.hi;
2438 clicks_at_epoch.lo = uw_microseconds.lo;
2441 /* Get time since boot */
2442 Microseconds (&uw_microseconds);
2444 /* Convert to time since midnight*/
2445 w_microseconds.hi = uw_microseconds.hi;
2446 w_microseconds.lo = uw_microseconds.lo;
2447 WideSubtract (&w_microseconds, &clicks_at_epoch);
2448 WideAdd (&w_microseconds, &wall_clock_at_epoch);
2449 tp->tv_sec = WideDivide (&w_microseconds, 1000000L, &tp->tv_usec);
2451 return 0;
2455 #ifdef __MRC__
2456 unsigned int
2457 sleep (unsigned int seconds)
2459 unsigned long time_up;
2460 EventRecord e;
2462 time_up = TickCount () + seconds * 60;
2463 while (TickCount () < time_up)
2465 /* Accept no event; just wait. by T.I. */
2466 WaitNextEvent (0, &e, 30, NULL);
2469 return (0);
2471 #endif /* __MRC__ */
2474 /* The time functions adjust time values according to the difference
2475 between the Unix and CW epoches. */
2477 #undef gmtime
2478 extern struct tm *gmtime (const time_t *);
2479 struct tm *
2480 sys_gmtime (const time_t *timer)
2482 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
2484 return gmtime (&unix_time);
2488 #undef localtime
2489 extern struct tm *localtime (const time_t *);
2490 struct tm *
2491 sys_localtime (const time_t *timer)
2493 #if __MSL__ >= 0x6000
2494 time_t unix_time = *timer;
2495 #else
2496 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
2497 #endif
2499 return localtime (&unix_time);
2503 #undef ctime
2504 extern char *ctime (const time_t *);
2505 char *
2506 sys_ctime (const time_t *timer)
2508 #if __MSL__ >= 0x6000
2509 time_t unix_time = *timer;
2510 #else
2511 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
2512 #endif
2514 return ctime (&unix_time);
2518 #undef time
2519 extern time_t time (time_t *);
2520 time_t
2521 sys_time (time_t *timer)
2523 #if __MSL__ >= 0x6000
2524 time_t mac_time = time (NULL);
2525 #else
2526 time_t mac_time = time (NULL) - CW_OR_MPW_UNIX_EPOCH_DIFF;
2527 #endif
2529 if (timer)
2530 *timer = mac_time;
2532 return mac_time;
2536 /* no subprocesses, empty wait */
2539 wait (int pid)
2541 return 0;
2545 void
2546 croak (char *badfunc)
2548 printf ("%s not yet implemented\r\n", badfunc);
2549 exit (1);
2553 char *
2554 mktemp (char *template)
2556 int len, k;
2557 static seqnum = 0;
2559 len = strlen (template);
2560 k = len - 1;
2561 while (k >= 0 && template[k] == 'X')
2562 k--;
2564 k++; /* make k index of first 'X' */
2566 if (k < len)
2568 /* Zero filled, number of digits equal to the number of X's. */
2569 sprintf (&template[k], "%0*d", len-k, seqnum++);
2571 return template;
2573 else
2574 return 0;
2578 /* Emulate getpwuid, getpwnam and others. */
2580 #define PASSWD_FIELD_SIZE 256
2582 static char my_passwd_name[PASSWD_FIELD_SIZE];
2583 static char my_passwd_dir[MAXPATHLEN+1];
2585 static struct passwd my_passwd =
2587 my_passwd_name,
2588 my_passwd_dir,
2591 static struct group my_group =
2593 /* There are no groups on the mac, so we just return "root" as the
2594 group name. */
2595 "root",
2599 /* Initialized by main () in macterm.c to pathname of emacs directory. */
2601 char emacs_passwd_dir[MAXPATHLEN+1];
2603 char *
2604 getwd (char *);
2606 void
2607 init_emacs_passwd_dir ()
2609 int found = false;
2611 if (getwd (emacs_passwd_dir) && getwd (my_passwd_dir))
2613 /* Need pathname of first ancestor that begins with "emacs"
2614 since Mac emacs application is somewhere in the emacs-*
2615 tree. */
2616 int len = strlen (emacs_passwd_dir);
2617 int j = len - 1;
2618 /* j points to the "/" following the directory name being
2619 compared. */
2620 int i = j - 1;
2621 while (i >= 0 && !found)
2623 while (i >= 0 && emacs_passwd_dir[i] != '/')
2624 i--;
2625 if (emacs_passwd_dir[i] == '/' && i+5 < len)
2626 found = (strncmp (&(emacs_passwd_dir[i+1]), "emacs", 5) == 0);
2627 if (found)
2628 emacs_passwd_dir[j+1] = '\0';
2629 else
2631 j = i;
2632 i = j - 1;
2637 if (!found)
2639 /* Setting to "/" probably won't work but set it to something
2640 anyway. */
2641 strcpy (emacs_passwd_dir, "/");
2642 strcpy (my_passwd_dir, "/");
2647 static struct passwd emacs_passwd =
2649 "emacs",
2650 emacs_passwd_dir,
2653 static int my_passwd_inited = 0;
2656 static void
2657 init_my_passwd ()
2659 char **owner_name;
2661 /* Note: my_passwd_dir initialized in int_emacs_passwd_dir to
2662 directory where Emacs was started. */
2664 owner_name = (char **) GetResource ('STR ',-16096);
2665 if (owner_name)
2667 HLock (owner_name);
2668 BlockMove ((unsigned char *) *owner_name,
2669 (unsigned char *) my_passwd_name,
2670 *owner_name[0]+1);
2671 HUnlock (owner_name);
2672 p2cstr ((unsigned char *) my_passwd_name);
2674 else
2675 my_passwd_name[0] = 0;
2679 struct passwd *
2680 getpwuid (uid_t uid)
2682 if (!my_passwd_inited)
2684 init_my_passwd ();
2685 my_passwd_inited = 1;
2688 return &my_passwd;
2692 struct group *
2693 getgrgid (gid_t gid)
2695 return &my_group;
2699 struct passwd *
2700 getpwnam (const char *name)
2702 if (strcmp (name, "emacs") == 0)
2703 return &emacs_passwd;
2705 if (!my_passwd_inited)
2707 init_my_passwd ();
2708 my_passwd_inited = 1;
2711 return &my_passwd;
2715 /* The functions fork, kill, sigsetmask, sigblock, request_sigio,
2716 setpgrp, setpriority, and unrequest_sigio are defined to be empty
2717 as in msdos.c. */
2721 fork ()
2723 return -1;
2728 kill (int x, int y)
2730 return -1;
2734 void
2735 sys_subshell ()
2737 error ("Can't spawn subshell");
2741 void
2742 request_sigio (void)
2747 void
2748 unrequest_sigio (void)
2754 setpgrp ()
2756 return 0;
2760 /* No pipes yet. */
2763 pipe (int _fildes[2])
2765 errno = EACCES;
2766 return -1;
2770 /* Hard and symbolic links. */
2773 symlink (const char *name1, const char *name2)
2775 errno = ENOENT;
2776 return -1;
2781 link (const char *name1, const char *name2)
2783 errno = ENOENT;
2784 return -1;
2787 #endif /* ! MAC_OSX */
2789 /* Determine the path name of the file specified by VREFNUM, DIRID,
2790 and NAME and place that in the buffer PATH of length
2791 MAXPATHLEN. */
2793 path_from_vol_dir_name (char *path, int man_path_len, short vol_ref_num,
2794 long dir_id, ConstStr255Param name)
2796 Str255 dir_name;
2797 CInfoPBRec cipb;
2798 OSErr err;
2800 if (strlen (name) > man_path_len)
2801 return 0;
2803 memcpy (dir_name, name, name[0]+1);
2804 memcpy (path, name, name[0]+1);
2805 p2cstr (path);
2807 cipb.dirInfo.ioDrParID = dir_id;
2808 cipb.dirInfo.ioNamePtr = dir_name;
2812 cipb.dirInfo.ioVRefNum = vol_ref_num;
2813 cipb.dirInfo.ioFDirIndex = -1;
2814 cipb.dirInfo.ioDrDirID = cipb.dirInfo.ioDrParID;
2815 /* go up to parent each time */
2817 err = PBGetCatInfo (&cipb, false);
2818 if (err != noErr)
2819 return 0;
2821 p2cstr (dir_name);
2822 if (strlen (dir_name) + strlen (path) + 1 >= man_path_len)
2823 return 0;
2825 strcat (dir_name, ":");
2826 strcat (dir_name, path);
2827 /* attach to front since we're going up directory tree */
2828 strcpy (path, dir_name);
2830 while (cipb.dirInfo.ioDrDirID != fsRtDirID);
2831 /* stop when we see the volume's root directory */
2833 return 1; /* success */
2837 static OSErr
2838 posix_pathname_to_fsspec (ufn, fs)
2839 const char *ufn;
2840 FSSpec *fs;
2842 Str255 mac_pathname;
2844 if (posix_to_mac_pathname (ufn, mac_pathname, sizeof (mac_pathname)) == 0)
2845 return fnfErr;
2846 else
2848 c2pstr (mac_pathname);
2849 return FSMakeFSSpec (0, 0, mac_pathname, fs);
2853 static OSErr
2854 fsspec_to_posix_pathname (fs, ufn, ufnbuflen)
2855 const FSSpec *fs;
2856 char *ufn;
2857 int ufnbuflen;
2859 char mac_pathname[MAXPATHLEN];
2861 if (path_from_vol_dir_name (mac_pathname, sizeof (mac_pathname) - 1,
2862 fs->vRefNum, fs->parID, fs->name)
2863 && mac_to_posix_pathname (mac_pathname, ufn, ufnbuflen))
2864 return noErr;
2865 else
2866 return fnfErr;
2869 #ifndef MAC_OSX
2872 readlink (const char *path, char *buf, int bufsiz)
2874 char mac_sym_link_name[MAXPATHLEN+1];
2875 OSErr err;
2876 FSSpec fsspec;
2877 Boolean target_is_folder, was_aliased;
2878 Str255 directory_name, mac_pathname;
2879 CInfoPBRec cipb;
2881 if (posix_to_mac_pathname (path, mac_sym_link_name, MAXPATHLEN+1) == 0)
2882 return -1;
2884 c2pstr (mac_sym_link_name);
2885 err = FSMakeFSSpec (0, 0, mac_sym_link_name, &fsspec);
2886 if (err != noErr)
2888 errno = ENOENT;
2889 return -1;
2892 err = ResolveAliasFile (&fsspec, true, &target_is_folder, &was_aliased);
2893 if (err != noErr || !was_aliased)
2895 errno = ENOENT;
2896 return -1;
2899 if (path_from_vol_dir_name (mac_pathname, 255, fsspec.vRefNum, fsspec.parID,
2900 fsspec.name) == 0)
2902 errno = ENOENT;
2903 return -1;
2906 if (mac_to_posix_pathname (mac_pathname, buf, bufsiz) == 0)
2908 errno = ENOENT;
2909 return -1;
2912 return strlen (buf);
2916 /* Convert a path to one with aliases fully expanded. */
2918 static int
2919 find_true_pathname (const char *path, char *buf, int bufsiz)
2921 char *q, temp[MAXPATHLEN+1];
2922 const char *p;
2923 int len;
2925 if (bufsiz <= 0 || path == 0 || path[0] == '\0')
2926 return -1;
2928 buf[0] = '\0';
2930 p = path;
2931 if (*p == '/')
2932 q = strchr (p + 1, '/');
2933 else
2934 q = strchr (p, '/');
2935 len = 0; /* loop may not be entered, e.g., for "/" */
2937 while (q)
2939 strcpy (temp, buf);
2940 strncat (temp, p, q - p);
2941 len = readlink (temp, buf, bufsiz);
2942 if (len <= -1)
2944 if (strlen (temp) + 1 > bufsiz)
2945 return -1;
2946 strcpy (buf, temp);
2948 strcat (buf, "/");
2949 len++;
2950 p = q + 1;
2951 q = strchr(p, '/');
2954 if (len + strlen (p) + 1 >= bufsiz)
2955 return -1;
2957 strcat (buf, p);
2958 return len + strlen (p);
2962 mode_t
2963 umask (mode_t numask)
2965 static mode_t mask = 022;
2966 mode_t oldmask = mask;
2967 mask = numask;
2968 return oldmask;
2973 chmod (const char *path, mode_t mode)
2975 /* say it always succeed for now */
2976 return 0;
2981 fchmod (int fd, mode_t mode)
2983 /* say it always succeed for now */
2984 return 0;
2989 fchown (int fd, uid_t owner, gid_t group)
2991 /* say it always succeed for now */
2992 return 0;
2997 dup (int oldd)
2999 #ifdef __MRC__
3000 return fcntl (oldd, F_DUPFD, 0);
3001 #elif __MWERKS__
3002 /* current implementation of fcntl in fcntl.mac.c simply returns old
3003 descriptor */
3004 return fcntl (oldd, F_DUPFD);
3005 #else
3006 You lose!!!
3007 #endif
3011 /* This is from the original sysdep.c. Emulate BSD dup2. First close
3012 newd if it already exists. Then, attempt to dup oldd. If not
3013 successful, call dup2 recursively until we are, then close the
3014 unsuccessful ones. */
3017 dup2 (int oldd, int newd)
3019 int fd, ret;
3021 close (newd);
3023 fd = dup (oldd);
3024 if (fd == -1)
3025 return -1;
3026 if (fd == newd)
3027 return newd;
3028 ret = dup2 (oldd, newd);
3029 close (fd);
3030 return ret;
3034 /* let it fail for now */
3036 char *
3037 sbrk (int incr)
3039 return (char *) -1;
3044 fsync (int fd)
3046 return 0;
3051 ioctl (int d, int request, void *argp)
3053 return -1;
3057 #ifdef __MRC__
3059 isatty (int fildes)
3061 if (fildes >=0 && fildes <= 2)
3062 return 1;
3063 else
3064 return 0;
3069 getgid ()
3071 return 100;
3076 getegid ()
3078 return 100;
3083 getuid ()
3085 return 200;
3090 geteuid ()
3092 return 200;
3094 #endif /* __MRC__ */
3097 #ifdef __MWERKS__
3098 #if __MSL__ < 0x6000
3099 #undef getpid
3101 getpid ()
3103 return 9999;
3105 #endif
3106 #endif /* __MWERKS__ */
3108 #endif /* ! MAC_OSX */
3111 /* Return the path to the directory in which Emacs can create
3112 temporary files. The MacOS "temporary items" directory cannot be
3113 used because it removes the file written by a process when it
3114 exits. In that sense it's more like "/dev/null" than "/tmp" (but
3115 again not exactly). And of course Emacs needs to read back the
3116 files written by its subprocesses. So here we write the files to a
3117 directory "Emacs" in the Preferences Folder. This directory is
3118 created if it does not exist. */
3120 char *
3121 get_temp_dir_name ()
3123 static char *temp_dir_name = NULL;
3124 short vol_ref_num;
3125 long dir_id;
3126 OSErr err;
3127 Str255 dir_name, full_path;
3128 CInfoPBRec cpb;
3129 char unix_dir_name[MAXPATHLEN+1];
3130 DIR *dir;
3132 /* Cache directory name with pointer temp_dir_name.
3133 Look for it only the first time. */
3134 if (!temp_dir_name)
3136 err = FindFolder (kOnSystemDisk, kPreferencesFolderType, kCreateFolder,
3137 &vol_ref_num, &dir_id);
3138 if (err != noErr)
3139 return NULL;
3141 if (!path_from_vol_dir_name (full_path, 255, vol_ref_num, dir_id, "\p"))
3142 return NULL;
3144 if (strlen (full_path) + 6 <= MAXPATHLEN)
3145 strcat (full_path, "Emacs:");
3146 else
3147 return NULL;
3149 if (!mac_to_posix_pathname (full_path, unix_dir_name, MAXPATHLEN+1))
3150 return NULL;
3152 dir = opendir (unix_dir_name); /* check whether temp directory exists */
3153 if (dir)
3154 closedir (dir);
3155 else if (mkdir (unix_dir_name, 0700) != 0) /* create it if not */
3156 return NULL;
3158 temp_dir_name = (char *) malloc (strlen (unix_dir_name) + 1);
3159 strcpy (temp_dir_name, unix_dir_name);
3162 return temp_dir_name;
3165 #ifndef MAC_OSX
3167 /* Allocate and construct an array of pointers to strings from a list
3168 of strings stored in a 'STR#' resource. The returned pointer array
3169 is stored in the style of argv and environ: if the 'STR#' resource
3170 contains numString strings, a pointer array with numString+1
3171 elements is returned in which the last entry contains a null
3172 pointer. The pointer to the pointer array is passed by pointer in
3173 parameter t. The resource ID of the 'STR#' resource is passed in
3174 parameter StringListID.
3177 void
3178 get_string_list (char ***t, short string_list_id)
3180 Handle h;
3181 Ptr p;
3182 int i, num_strings;
3184 h = GetResource ('STR#', string_list_id);
3185 if (h)
3187 HLock (h);
3188 p = *h;
3189 num_strings = * (short *) p;
3190 p += sizeof(short);
3191 *t = (char **) malloc (sizeof (char *) * (num_strings + 1));
3192 for (i = 0; i < num_strings; i++)
3194 short length = *p++;
3195 (*t)[i] = (char *) malloc (length + 1);
3196 strncpy ((*t)[i], p, length);
3197 (*t)[i][length] = '\0';
3198 p += length;
3200 (*t)[num_strings] = 0;
3201 HUnlock (h);
3203 else
3205 /* Return no string in case GetResource fails. Bug fixed by
3206 Ikegami Tsutomu. Caused MPW build to crash without sym -on
3207 option (no sym -on implies -opt local). */
3208 *t = (char **) malloc (sizeof (char *));
3209 (*t)[0] = 0;
3214 static char *
3215 get_path_to_system_folder ()
3217 short vol_ref_num;
3218 long dir_id;
3219 OSErr err;
3220 Str255 dir_name, full_path;
3221 CInfoPBRec cpb;
3222 static char system_folder_unix_name[MAXPATHLEN+1];
3223 DIR *dir;
3225 err = FindFolder (kOnSystemDisk, kSystemFolderType, kDontCreateFolder,
3226 &vol_ref_num, &dir_id);
3227 if (err != noErr)
3228 return NULL;
3230 if (!path_from_vol_dir_name (full_path, 255, vol_ref_num, dir_id, "\p"))
3231 return NULL;
3233 if (!mac_to_posix_pathname (full_path, system_folder_unix_name,
3234 MAXPATHLEN+1))
3235 return NULL;
3237 return system_folder_unix_name;
3241 char **environ;
3243 #define ENVIRON_STRING_LIST_ID 128
3245 /* Get environment variable definitions from STR# resource. */
3247 void
3248 init_environ ()
3250 int i;
3252 get_string_list (&environ, ENVIRON_STRING_LIST_ID);
3254 i = 0;
3255 while (environ[i])
3256 i++;
3258 /* Make HOME directory the one Emacs starts up in if not specified
3259 by resource. */
3260 if (getenv ("HOME") == NULL)
3262 environ = (char **) realloc (environ, sizeof (char *) * (i + 2));
3263 if (environ)
3265 environ[i] = (char *) malloc (strlen (my_passwd_dir) + 6);
3266 if (environ[i])
3268 strcpy (environ[i], "HOME=");
3269 strcat (environ[i], my_passwd_dir);
3271 environ[i+1] = 0;
3272 i++;
3276 /* Make HOME directory the one Emacs starts up in if not specified
3277 by resource. */
3278 if (getenv ("MAIL") == NULL)
3280 environ = (char **) realloc (environ, sizeof (char *) * (i + 2));
3281 if (environ)
3283 char * path_to_system_folder = get_path_to_system_folder ();
3284 environ[i] = (char *) malloc (strlen (path_to_system_folder) + 22);
3285 if (environ[i])
3287 strcpy (environ[i], "MAIL=");
3288 strcat (environ[i], path_to_system_folder);
3289 strcat (environ[i], "Eudora Folder/In");
3291 environ[i+1] = 0;
3297 /* Return the value of the environment variable NAME. */
3299 char *
3300 getenv (const char *name)
3302 int length = strlen(name);
3303 char **e;
3305 for (e = environ; *e != 0; e++)
3306 if (strncmp(*e, name, length) == 0 && (*e)[length] == '=')
3307 return &(*e)[length + 1];
3309 if (strcmp (name, "TMPDIR") == 0)
3310 return get_temp_dir_name ();
3312 return 0;
3316 #ifdef __MRC__
3317 /* see Interfaces&Libraries:Interfaces:CIncludes:signal.h */
3318 char *sys_siglist[] =
3320 "Zero is not a signal!!!",
3321 "Abort", /* 1 */
3322 "Interactive user interrupt", /* 2 */ "?",
3323 "Floating point exception", /* 4 */ "?", "?", "?",
3324 "Illegal instruction", /* 8 */ "?", "?", "?", "?", "?", "?", "?",
3325 "Segment violation", /* 16 */ "?", "?", "?", "?", "?", "?", "?",
3326 "?", "?", "?", "?", "?", "?", "?", "?",
3327 "Terminal" /* 32 */
3329 #elif __MWERKS__
3330 char *sys_siglist[] =
3332 "Zero is not a signal!!!",
3333 "Abort",
3334 "Floating point exception",
3335 "Illegal instruction",
3336 "Interactive user interrupt",
3337 "Segment violation",
3338 "Terminal"
3340 #else /* not __MRC__ and not __MWERKS__ */
3341 You lose!!!
3342 #endif /* not __MRC__ and not __MWERKS__ */
3345 #include <utsname.h>
3348 uname (struct utsname *name)
3350 char **system_name;
3351 system_name = GetString (-16413); /* IM - Resource Manager Reference */
3352 if (system_name)
3354 BlockMove (*system_name, name->nodename, (*system_name)[0]+1);
3355 p2cstr (name->nodename);
3356 return 0;
3358 else
3359 return -1;
3363 /* Event class of HLE sent to subprocess. */
3364 const OSType kEmacsSubprocessSend = 'ESND';
3366 /* Event class of HLE sent back from subprocess. */
3367 const OSType kEmacsSubprocessReply = 'ERPY';
3370 char *
3371 mystrchr (char *s, char c)
3373 while (*s && *s != c)
3375 if (*s == '\\')
3376 s++;
3377 s++;
3380 if (*s)
3382 *s = '\0';
3383 return s;
3385 else
3386 return NULL;
3390 char *
3391 mystrtok (char *s)
3393 while (*s)
3394 s++;
3396 return s + 1;
3400 void
3401 mystrcpy (char *to, char *from)
3403 while (*from)
3405 if (*from == '\\')
3406 from++;
3407 *to++ = *from++;
3409 *to = '\0';
3413 /* Start a Mac subprocess. Arguments for it is passed in argv (null
3414 terminated). The process should run with the default directory
3415 "workdir", read input from "infn", and write output and error to
3416 "outfn" and "errfn", resp. The Process Manager call
3417 LaunchApplication is used to start the subprocess. We use high
3418 level events as the mechanism to pass arguments to the subprocess
3419 and to make Emacs wait for the subprocess to terminate and pass
3420 back a result code. The bulk of the code here packs the arguments
3421 into one message to be passed together with the high level event.
3422 Emacs also sometimes starts a subprocess using a shell to perform
3423 wildcard filename expansion. Since we don't really have a shell on
3424 the Mac, this case is detected and the starting of the shell is
3425 by-passed. We really need to add code here to do filename
3426 expansion to support such functionality.
3428 We can't use this strategy in Carbon because the High Level Event
3429 APIs are not available. */
3432 run_mac_command (argv, workdir, infn, outfn, errfn)
3433 unsigned char **argv;
3434 const char *workdir;
3435 const char *infn, *outfn, *errfn;
3437 #if TARGET_API_MAC_CARBON
3438 return -1;
3439 #else /* not TARGET_API_MAC_CARBON */
3440 char macappname[MAXPATHLEN+1], macworkdir[MAXPATHLEN+1];
3441 char macinfn[MAXPATHLEN+1], macoutfn[MAXPATHLEN+1], macerrfn[MAXPATHLEN+1];
3442 int paramlen, argc, newargc, j, retries;
3443 char **newargv, *param, *p;
3444 OSErr iErr;
3445 FSSpec spec;
3446 LaunchParamBlockRec lpbr;
3447 EventRecord send_event, reply_event;
3448 RgnHandle cursor_region_handle;
3449 TargetID targ;
3450 unsigned long ref_con, len;
3452 if (posix_to_mac_pathname (workdir, macworkdir, MAXPATHLEN+1) == 0)
3453 return -1;
3454 if (posix_to_mac_pathname (infn, macinfn, MAXPATHLEN+1) == 0)
3455 return -1;
3456 if (posix_to_mac_pathname (outfn, macoutfn, MAXPATHLEN+1) == 0)
3457 return -1;
3458 if (posix_to_mac_pathname (errfn, macerrfn, MAXPATHLEN+1) == 0)
3459 return -1;
3461 paramlen = strlen (macworkdir) + strlen (macinfn) + strlen (macoutfn)
3462 + strlen (macerrfn) + 4; /* count nulls at end of strings */
3464 argc = 0;
3465 while (argv[argc])
3466 argc++;
3468 if (argc == 0)
3469 return -1;
3471 /* If a subprocess is invoked with a shell, we receive 3 arguments
3472 of the form: "<path to emacs bins>/sh" "-c" "<path to emacs
3473 bins>/<command> <command args>" */
3474 j = strlen (argv[0]);
3475 if (j >= 3 && strcmp (argv[0]+j-3, "/sh") == 0
3476 && argc == 3 && strcmp (argv[1], "-c") == 0)
3478 char *command, *t, tempmacpathname[MAXPATHLEN+1];
3480 /* The arguments for the command in argv[2] are separated by
3481 spaces. Count them and put the count in newargc. */
3482 command = (char *) alloca (strlen (argv[2])+2);
3483 strcpy (command, argv[2]);
3484 if (command[strlen (command) - 1] != ' ')
3485 strcat (command, " ");
3487 t = command;
3488 newargc = 0;
3489 t = mystrchr (t, ' ');
3490 while (t)
3492 newargc++;
3493 t = mystrchr (t+1, ' ');
3496 newargv = (char **) alloca (sizeof (char *) * newargc);
3498 t = command;
3499 for (j = 0; j < newargc; j++)
3501 newargv[j] = (char *) alloca (strlen (t) + 1);
3502 mystrcpy (newargv[j], t);
3504 t = mystrtok (t);
3505 paramlen += strlen (newargv[j]) + 1;
3508 if (strncmp (newargv[0], "~emacs/", 7) == 0)
3510 if (posix_to_mac_pathname (newargv[0], tempmacpathname, MAXPATHLEN+1)
3511 == 0)
3512 return -1;
3514 else
3515 { /* sometimes Emacs call "sh" without a path for the command */
3516 #if 0
3517 char *t = (char *) alloca (strlen (newargv[0]) + 7 + 1);
3518 strcpy (t, "~emacs/");
3519 strcat (t, newargv[0]);
3520 #endif /* 0 */
3521 Lisp_Object path;
3522 openp (Vexec_path, build_string (newargv[0]), Vexec_suffixes, &path,
3523 make_number (X_OK));
3525 if (NILP (path))
3526 return -1;
3527 if (posix_to_mac_pathname (SDATA (path), tempmacpathname,
3528 MAXPATHLEN+1) == 0)
3529 return -1;
3531 strcpy (macappname, tempmacpathname);
3533 else
3535 if (posix_to_mac_pathname (argv[0], macappname, MAXPATHLEN+1) == 0)
3536 return -1;
3538 newargv = (char **) alloca (sizeof (char *) * argc);
3539 newargc = argc;
3540 for (j = 1; j < argc; j++)
3542 if (strncmp (argv[j], "~emacs/", 7) == 0)
3544 char *t = strchr (argv[j], ' ');
3545 if (t)
3547 char tempcmdname[MAXPATHLEN+1], tempmaccmdname[MAXPATHLEN+1];
3548 strncpy (tempcmdname, argv[j], t-argv[j]);
3549 tempcmdname[t-argv[j]] = '\0';
3550 if (posix_to_mac_pathname (tempcmdname, tempmaccmdname,
3551 MAXPATHLEN+1) == 0)
3552 return -1;
3553 newargv[j] = (char *) alloca (strlen (tempmaccmdname)
3554 + strlen (t) + 1);
3555 strcpy (newargv[j], tempmaccmdname);
3556 strcat (newargv[j], t);
3558 else
3560 char tempmaccmdname[MAXPATHLEN+1];
3561 if (posix_to_mac_pathname (argv[j], tempmaccmdname,
3562 MAXPATHLEN+1) == 0)
3563 return -1;
3564 newargv[j] = (char *) alloca (strlen (tempmaccmdname)+1);
3565 strcpy (newargv[j], tempmaccmdname);
3568 else
3569 newargv[j] = argv[j];
3570 paramlen += strlen (newargv[j]) + 1;
3574 /* After expanding all the arguments, we now know the length of the
3575 parameter block to be sent to the subprocess as a message
3576 attached to the HLE. */
3577 param = (char *) malloc (paramlen + 1);
3578 if (!param)
3579 return -1;
3581 p = param;
3582 *p++ = newargc;
3583 /* first byte of message contains number of arguments for command */
3584 strcpy (p, macworkdir);
3585 p += strlen (macworkdir);
3586 *p++ = '\0';
3587 /* null terminate strings sent so it's possible to use strcpy over there */
3588 strcpy (p, macinfn);
3589 p += strlen (macinfn);
3590 *p++ = '\0';
3591 strcpy (p, macoutfn);
3592 p += strlen (macoutfn);
3593 *p++ = '\0';
3594 strcpy (p, macerrfn);
3595 p += strlen (macerrfn);
3596 *p++ = '\0';
3597 for (j = 1; j < newargc; j++)
3599 strcpy (p, newargv[j]);
3600 p += strlen (newargv[j]);
3601 *p++ = '\0';
3604 c2pstr (macappname);
3606 iErr = FSMakeFSSpec (0, 0, macappname, &spec);
3608 if (iErr != noErr)
3610 free (param);
3611 return -1;
3614 lpbr.launchBlockID = extendedBlock;
3615 lpbr.launchEPBLength = extendedBlockLen;
3616 lpbr.launchControlFlags = launchContinue + launchNoFileFlags;
3617 lpbr.launchAppSpec = &spec;
3618 lpbr.launchAppParameters = NULL;
3620 iErr = LaunchApplication (&lpbr); /* call the subprocess */
3621 if (iErr != noErr)
3623 free (param);
3624 return -1;
3627 send_event.what = kHighLevelEvent;
3628 send_event.message = kEmacsSubprocessSend;
3629 /* Event ID stored in "where" unused */
3631 retries = 3;
3632 /* OS may think current subprocess has terminated if previous one
3633 terminated recently. */
3636 iErr = PostHighLevelEvent (&send_event, &lpbr.launchProcessSN, 0, param,
3637 paramlen + 1, receiverIDisPSN);
3639 while (iErr == sessClosedErr && retries-- > 0);
3641 if (iErr != noErr)
3643 free (param);
3644 return -1;
3647 cursor_region_handle = NewRgn ();
3649 /* Wait for the subprocess to finish, when it will send us a ERPY
3650 high level event. */
3651 while (1)
3652 if (WaitNextEvent (highLevelEventMask, &reply_event, 180,
3653 cursor_region_handle)
3654 && reply_event.message == kEmacsSubprocessReply)
3655 break;
3657 /* The return code is sent through the refCon */
3658 iErr = AcceptHighLevelEvent (&targ, &ref_con, NULL, &len);
3659 if (iErr != noErr)
3661 DisposeHandle ((Handle) cursor_region_handle);
3662 free (param);
3663 return -1;
3666 DisposeHandle ((Handle) cursor_region_handle);
3667 free (param);
3669 return ref_con;
3670 #endif /* not TARGET_API_MAC_CARBON */
3674 DIR *
3675 opendir (const char *dirname)
3677 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
3678 char mac_pathname[MAXPATHLEN+1], vol_name[MAXPATHLEN+1];
3679 DIR *dirp;
3680 CInfoPBRec cipb;
3681 HVolumeParam vpb;
3682 int len, vol_name_len;
3684 if (find_true_pathname (dirname, true_pathname, MAXPATHLEN+1) == -1)
3685 return 0;
3687 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
3688 if (len > -1)
3689 fully_resolved_name[len] = '\0';
3690 else
3691 strcpy (fully_resolved_name, true_pathname);
3693 dirp = (DIR *) malloc (sizeof(DIR));
3694 if (!dirp)
3695 return 0;
3697 /* Handle special case when dirname is "/": sets up for readir to
3698 get all mount volumes. */
3699 if (strcmp (fully_resolved_name, "/") == 0)
3701 dirp->getting_volumes = 1; /* special all mounted volumes DIR struct */
3702 dirp->current_index = 1; /* index for first volume */
3703 return dirp;
3706 /* Handle typical cases: not accessing all mounted volumes. */
3707 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
3708 return 0;
3710 /* Emacs calls opendir without the trailing '/', Mac needs trailing ':' */
3711 len = strlen (mac_pathname);
3712 if (mac_pathname[len - 1] != ':' && len < MAXPATHLEN)
3713 strcat (mac_pathname, ":");
3715 /* Extract volume name */
3716 vol_name_len = strchr (mac_pathname, ':') - mac_pathname;
3717 strncpy (vol_name, mac_pathname, vol_name_len);
3718 vol_name[vol_name_len] = '\0';
3719 strcat (vol_name, ":");
3721 c2pstr (mac_pathname);
3722 cipb.hFileInfo.ioNamePtr = mac_pathname;
3723 /* using full pathname so vRefNum and DirID ignored */
3724 cipb.hFileInfo.ioVRefNum = 0;
3725 cipb.hFileInfo.ioDirID = 0;
3726 cipb.hFileInfo.ioFDirIndex = 0;
3727 /* set to 0 to get information about specific dir or file */
3729 errno = PBGetCatInfo (&cipb, false);
3730 if (errno != noErr)
3732 errno = ENOENT;
3733 return 0;
3736 if (!(cipb.hFileInfo.ioFlAttrib & 0x10)) /* bit 4 = 1 for directories */
3737 return 0; /* not a directory */
3739 dirp->dir_id = cipb.dirInfo.ioDrDirID; /* used later in readdir */
3740 dirp->getting_volumes = 0;
3741 dirp->current_index = 1; /* index for first file/directory */
3743 c2pstr (vol_name);
3744 vpb.ioNamePtr = vol_name;
3745 /* using full pathname so vRefNum and DirID ignored */
3746 vpb.ioVRefNum = 0;
3747 vpb.ioVolIndex = -1;
3748 errno = PBHGetVInfo ((union HParamBlockRec *) &vpb, false);
3749 if (errno != noErr)
3751 errno = ENOENT;
3752 return 0;
3755 dirp->vol_ref_num = vpb.ioVRefNum;
3757 return dirp;
3761 closedir (DIR *dp)
3763 free (dp);
3765 return 0;
3769 struct dirent *
3770 readdir (DIR *dp)
3772 HParamBlockRec hpblock;
3773 CInfoPBRec cipb;
3774 static struct dirent s_dirent;
3775 static Str255 s_name;
3776 int done;
3777 char *p;
3779 /* Handle the root directory containing the mounted volumes. Call
3780 PBHGetVInfo specifying an index to obtain the info for a volume.
3781 PBHGetVInfo returns an error when it receives an index beyond the
3782 last volume, at which time we should return a nil dirent struct
3783 pointer. */
3784 if (dp->getting_volumes)
3786 hpblock.volumeParam.ioNamePtr = s_name;
3787 hpblock.volumeParam.ioVRefNum = 0;
3788 hpblock.volumeParam.ioVolIndex = dp->current_index;
3790 errno = PBHGetVInfo (&hpblock, false);
3791 if (errno != noErr)
3793 errno = ENOENT;
3794 return 0;
3797 p2cstr (s_name);
3798 strcat (s_name, "/"); /* need "/" for stat to work correctly */
3800 dp->current_index++;
3802 s_dirent.d_ino = hpblock.volumeParam.ioVRefNum;
3803 s_dirent.d_name = s_name;
3805 return &s_dirent;
3807 else
3809 cipb.hFileInfo.ioVRefNum = dp->vol_ref_num;
3810 cipb.hFileInfo.ioNamePtr = s_name;
3811 /* location to receive filename returned */
3813 /* return only visible files */
3814 done = false;
3815 while (!done)
3817 cipb.hFileInfo.ioDirID = dp->dir_id;
3818 /* directory ID found by opendir */
3819 cipb.hFileInfo.ioFDirIndex = dp->current_index;
3821 errno = PBGetCatInfo (&cipb, false);
3822 if (errno != noErr)
3824 errno = ENOENT;
3825 return 0;
3828 /* insist on a visible entry */
3829 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* directory? */
3830 done = !(cipb.dirInfo.ioDrUsrWds.frFlags & fInvisible);
3831 else
3832 done = !(cipb.hFileInfo.ioFlFndrInfo.fdFlags & fInvisible);
3834 dp->current_index++;
3837 p2cstr (s_name);
3839 p = s_name;
3840 while (*p)
3842 if (*p == '/')
3843 *p = ':';
3844 p++;
3847 s_dirent.d_ino = cipb.dirInfo.ioDrDirID;
3848 /* value unimportant: non-zero for valid file */
3849 s_dirent.d_name = s_name;
3851 return &s_dirent;
3856 char *
3857 getwd (char *path)
3859 char mac_pathname[MAXPATHLEN+1];
3860 Str255 directory_name;
3861 OSErr errno;
3862 CInfoPBRec cipb;
3864 if (path_from_vol_dir_name (mac_pathname, 255, 0, 0, "\p") == 0)
3865 return NULL;
3867 if (mac_to_posix_pathname (mac_pathname, path, MAXPATHLEN+1) == 0)
3868 return 0;
3869 else
3870 return path;
3873 #endif /* ! MAC_OSX */
3876 void
3877 initialize_applescript ()
3879 AEDesc null_desc;
3880 OSAError osaerror;
3882 /* if open fails, as_scripting_component is set to NULL. Its
3883 subsequent use in OSA calls will fail with badComponentInstance
3884 error. */
3885 as_scripting_component = OpenDefaultComponent (kOSAComponentType,
3886 kAppleScriptSubtype);
3888 null_desc.descriptorType = typeNull;
3889 null_desc.dataHandle = 0;
3890 osaerror = OSAMakeContext (as_scripting_component, &null_desc,
3891 kOSANullScript, &as_script_context);
3892 if (osaerror)
3893 as_script_context = kOSANullScript;
3894 /* use default context if create fails */
3898 void
3899 terminate_applescript()
3901 OSADispose (as_scripting_component, as_script_context);
3902 CloseComponent (as_scripting_component);
3905 /* Convert a lisp string to the 4 byte character code. */
3907 OSType
3908 mac_get_code_from_arg(Lisp_Object arg, OSType defCode)
3910 OSType result;
3911 if (NILP(arg))
3913 result = defCode;
3915 else
3917 /* check type string */
3918 CHECK_STRING(arg);
3919 if (SBYTES (arg) != 4)
3921 error ("Wrong argument: need string of length 4 for code");
3923 result = EndianU32_BtoN (*((UInt32 *) SDATA (arg)));
3925 return result;
3928 /* Convert the 4 byte character code into a 4 byte string. */
3930 Lisp_Object
3931 mac_get_object_from_code(OSType defCode)
3933 UInt32 code = EndianU32_NtoB (defCode);
3935 return make_unibyte_string ((char *)&code, 4);
3939 DEFUN ("mac-get-file-creator", Fmac_get_file_creator, Smac_get_file_creator, 1, 1, 0,
3940 doc: /* Get the creator code of FILENAME as a four character string. */)
3941 (filename)
3942 Lisp_Object filename;
3944 OSErr status;
3945 #ifdef MAC_OSX
3946 FSRef fref;
3947 #else
3948 FSSpec fss;
3949 #endif
3950 OSType cCode;
3951 Lisp_Object result = Qnil;
3952 CHECK_STRING (filename);
3954 if (NILP(Ffile_exists_p(filename)) || !NILP(Ffile_directory_p(filename))) {
3955 return Qnil;
3957 filename = Fexpand_file_name (filename, Qnil);
3959 BLOCK_INPUT;
3960 #ifdef MAC_OSX
3961 status = FSPathMakeRef(SDATA(ENCODE_FILE(filename)), &fref, NULL);
3962 #else
3963 status = posix_pathname_to_fsspec (SDATA (ENCODE_FILE (filename)), &fss);
3964 #endif
3966 if (status == noErr)
3968 #ifdef MAC_OSX
3969 FSCatalogInfo catalogInfo;
3971 status = FSGetCatalogInfo(&fref, kFSCatInfoFinderInfo,
3972 &catalogInfo, NULL, NULL, NULL);
3973 #else
3974 FInfo finder_info;
3976 status = FSpGetFInfo (&fss, &finder_info);
3977 #endif
3978 if (status == noErr)
3980 #ifdef MAC_OSX
3981 result = mac_get_object_from_code(((FileInfo*)&catalogInfo.finderInfo)->fileCreator);
3982 #else
3983 result = mac_get_object_from_code (finder_info.fdCreator);
3984 #endif
3987 UNBLOCK_INPUT;
3988 if (status != noErr) {
3989 error ("Error while getting file information.");
3991 return result;
3994 DEFUN ("mac-get-file-type", Fmac_get_file_type, Smac_get_file_type, 1, 1, 0,
3995 doc: /* Get the type code of FILENAME as a four character string. */)
3996 (filename)
3997 Lisp_Object filename;
3999 OSErr status;
4000 #ifdef MAC_OSX
4001 FSRef fref;
4002 #else
4003 FSSpec fss;
4004 #endif
4005 OSType cCode;
4006 Lisp_Object result = Qnil;
4007 CHECK_STRING (filename);
4009 if (NILP(Ffile_exists_p(filename)) || !NILP(Ffile_directory_p(filename))) {
4010 return Qnil;
4012 filename = Fexpand_file_name (filename, Qnil);
4014 BLOCK_INPUT;
4015 #ifdef MAC_OSX
4016 status = FSPathMakeRef(SDATA(ENCODE_FILE(filename)), &fref, NULL);
4017 #else
4018 status = posix_pathname_to_fsspec (SDATA (ENCODE_FILE (filename)), &fss);
4019 #endif
4021 if (status == noErr)
4023 #ifdef MAC_OSX
4024 FSCatalogInfo catalogInfo;
4026 status = FSGetCatalogInfo(&fref, kFSCatInfoFinderInfo,
4027 &catalogInfo, NULL, NULL, NULL);
4028 #else
4029 FInfo finder_info;
4031 status = FSpGetFInfo (&fss, &finder_info);
4032 #endif
4033 if (status == noErr)
4035 #ifdef MAC_OSX
4036 result = mac_get_object_from_code(((FileInfo*)&catalogInfo.finderInfo)->fileType);
4037 #else
4038 result = mac_get_object_from_code (finder_info.fdType);
4039 #endif
4042 UNBLOCK_INPUT;
4043 if (status != noErr) {
4044 error ("Error while getting file information.");
4046 return result;
4049 DEFUN ("mac-set-file-creator", Fmac_set_file_creator, Smac_set_file_creator, 1, 2, 0,
4050 doc: /* Set creator code of file FILENAME to CODE.
4051 If non-nil, CODE must be a 4-character string. Otherwise, 'EMAx' is
4052 assumed. Return non-nil if successful. */)
4053 (filename, code)
4054 Lisp_Object filename, code;
4056 OSErr status;
4057 #ifdef MAC_OSX
4058 FSRef fref;
4059 #else
4060 FSSpec fss;
4061 #endif
4062 OSType cCode;
4063 CHECK_STRING (filename);
4065 cCode = mac_get_code_from_arg(code, 'EMAx');
4067 if (NILP(Ffile_exists_p(filename)) || !NILP(Ffile_directory_p(filename))) {
4068 return Qnil;
4070 filename = Fexpand_file_name (filename, Qnil);
4072 BLOCK_INPUT;
4073 #ifdef MAC_OSX
4074 status = FSPathMakeRef(SDATA(ENCODE_FILE(filename)), &fref, NULL);
4075 #else
4076 status = posix_pathname_to_fsspec (SDATA (ENCODE_FILE (filename)), &fss);
4077 #endif
4079 if (status == noErr)
4081 #ifdef MAC_OSX
4082 FSCatalogInfo catalogInfo;
4083 FSRef parentDir;
4084 status = FSGetCatalogInfo(&fref, kFSCatInfoFinderInfo,
4085 &catalogInfo, NULL, NULL, &parentDir);
4086 #else
4087 FInfo finder_info;
4089 status = FSpGetFInfo (&fss, &finder_info);
4090 #endif
4091 if (status == noErr)
4093 #ifdef MAC_OSX
4094 ((FileInfo*)&catalogInfo.finderInfo)->fileCreator = cCode;
4095 status = FSSetCatalogInfo(&fref, kFSCatInfoFinderInfo, &catalogInfo);
4096 /* TODO: on Mac OS 10.2, we need to touch the parent dir, FNNotify? */
4097 #else
4098 finder_info.fdCreator = cCode;
4099 status = FSpSetFInfo (&fss, &finder_info);
4100 #endif
4103 UNBLOCK_INPUT;
4104 if (status != noErr) {
4105 error ("Error while setting creator information.");
4107 return Qt;
4110 DEFUN ("mac-set-file-type", Fmac_set_file_type, Smac_set_file_type, 2, 2, 0,
4111 doc: /* Set file code of file FILENAME to CODE.
4112 CODE must be a 4-character string. Return non-nil if successful. */)
4113 (filename, code)
4114 Lisp_Object filename, code;
4116 OSErr status;
4117 #ifdef MAC_OSX
4118 FSRef fref;
4119 #else
4120 FSSpec fss;
4121 #endif
4122 OSType cCode;
4123 CHECK_STRING (filename);
4125 cCode = mac_get_code_from_arg(code, 0); /* Default to empty code*/
4127 if (NILP(Ffile_exists_p(filename)) || !NILP(Ffile_directory_p(filename))) {
4128 return Qnil;
4130 filename = Fexpand_file_name (filename, Qnil);
4132 BLOCK_INPUT;
4133 #ifdef MAC_OSX
4134 status = FSPathMakeRef(SDATA(ENCODE_FILE(filename)), &fref, NULL);
4135 #else
4136 status = posix_pathname_to_fsspec (SDATA (ENCODE_FILE (filename)), &fss);
4137 #endif
4139 if (status == noErr)
4141 #ifdef MAC_OSX
4142 FSCatalogInfo catalogInfo;
4143 FSRef parentDir;
4144 status = FSGetCatalogInfo(&fref, kFSCatInfoFinderInfo,
4145 &catalogInfo, NULL, NULL, &parentDir);
4146 #else
4147 FInfo finder_info;
4149 status = FSpGetFInfo (&fss, &finder_info);
4150 #endif
4151 if (status == noErr)
4153 #ifdef MAC_OSX
4154 ((FileInfo*)&catalogInfo.finderInfo)->fileType = cCode;
4155 status = FSSetCatalogInfo(&fref, kFSCatInfoFinderInfo, &catalogInfo);
4156 /* TODO: on Mac OS 10.2, we need to touch the parent dir, FNNotify? */
4157 #else
4158 finder_info.fdType = cCode;
4159 status = FSpSetFInfo (&fss, &finder_info);
4160 #endif
4163 UNBLOCK_INPUT;
4164 if (status != noErr) {
4165 error ("Error while setting creator information.");
4167 return Qt;
4171 /* Compile and execute the AppleScript SCRIPT and return the error
4172 status as function value. A zero is returned if compilation and
4173 execution is successful, in which case *RESULT is set to a Lisp
4174 string containing the resulting script value. Otherwise, the Mac
4175 error code is returned and *RESULT is set to an error Lisp string.
4176 For documentation on the MacOS scripting architecture, see Inside
4177 Macintosh - Interapplication Communications: Scripting
4178 Components. */
4180 static long
4181 do_applescript (script, result)
4182 Lisp_Object script, *result;
4184 AEDesc script_desc, result_desc, error_desc, *desc = NULL;
4185 OSErr error;
4186 OSAError osaerror;
4188 *result = Qnil;
4190 if (!as_scripting_component)
4191 initialize_applescript();
4193 error = AECreateDesc (typeChar, SDATA (script), SBYTES (script),
4194 &script_desc);
4195 if (error)
4196 return error;
4198 osaerror = OSADoScript (as_scripting_component, &script_desc, kOSANullScript,
4199 typeChar, kOSAModeNull, &result_desc);
4201 if (osaerror == noErr)
4202 /* success: retrieve resulting script value */
4203 desc = &result_desc;
4204 else if (osaerror == errOSAScriptError)
4205 /* error executing AppleScript: retrieve error message */
4206 if (!OSAScriptError (as_scripting_component, kOSAErrorMessage, typeChar,
4207 &error_desc))
4208 desc = &error_desc;
4210 if (desc)
4212 #if TARGET_API_MAC_CARBON
4213 *result = make_uninit_string (AEGetDescDataSize (desc));
4214 AEGetDescData (desc, SDATA (*result), SBYTES (*result));
4215 #else /* not TARGET_API_MAC_CARBON */
4216 *result = make_uninit_string (GetHandleSize (desc->dataHandle));
4217 memcpy (SDATA (*result), *(desc->dataHandle), SBYTES (*result));
4218 #endif /* not TARGET_API_MAC_CARBON */
4219 AEDisposeDesc (desc);
4222 AEDisposeDesc (&script_desc);
4224 return osaerror;
4228 DEFUN ("do-applescript", Fdo_applescript, Sdo_applescript, 1, 1, 0,
4229 doc: /* Compile and execute AppleScript SCRIPT and return the result.
4230 If compilation and execution are successful, the resulting script
4231 value is returned as a string. Otherwise the function aborts and
4232 displays the error message returned by the AppleScript scripting
4233 component. */)
4234 (script)
4235 Lisp_Object script;
4237 Lisp_Object result;
4238 long status;
4240 CHECK_STRING (script);
4242 BLOCK_INPUT;
4243 status = do_applescript (script, &result);
4244 UNBLOCK_INPUT;
4245 if (status == 0)
4246 return result;
4247 else if (!STRINGP (result))
4248 error ("AppleScript error %d", status);
4249 else
4250 error ("%s", SDATA (result));
4254 DEFUN ("mac-file-name-to-posix", Fmac_file_name_to_posix,
4255 Smac_file_name_to_posix, 1, 1, 0,
4256 doc: /* Convert Macintosh FILENAME to Posix form. */)
4257 (filename)
4258 Lisp_Object filename;
4260 char posix_filename[MAXPATHLEN+1];
4262 CHECK_STRING (filename);
4264 if (mac_to_posix_pathname (SDATA (filename), posix_filename, MAXPATHLEN))
4265 return build_string (posix_filename);
4266 else
4267 return Qnil;
4271 DEFUN ("posix-file-name-to-mac", Fposix_file_name_to_mac,
4272 Sposix_file_name_to_mac, 1, 1, 0,
4273 doc: /* Convert Posix FILENAME to Mac form. */)
4274 (filename)
4275 Lisp_Object filename;
4277 char mac_filename[MAXPATHLEN+1];
4279 CHECK_STRING (filename);
4281 if (posix_to_mac_pathname (SDATA (filename), mac_filename, MAXPATHLEN))
4282 return build_string (mac_filename);
4283 else
4284 return Qnil;
4288 DEFUN ("mac-coerce-ae-data", Fmac_coerce_ae_data, Smac_coerce_ae_data, 3, 3, 0,
4289 doc: /* Coerce Apple event data SRC-DATA of type SRC-TYPE to DST-TYPE.
4290 Each type should be a string of length 4 or the symbol
4291 `undecoded-file-name'. */)
4292 (src_type, src_data, dst_type)
4293 Lisp_Object src_type, src_data, dst_type;
4295 OSErr err;
4296 Lisp_Object result = Qnil;
4297 DescType src_desc_type, dst_desc_type;
4298 AEDesc dst_desc;
4299 #ifdef MAC_OSX
4300 FSRef fref;
4301 #else
4302 FSSpec fs;
4303 #endif
4305 CHECK_STRING (src_data);
4306 if (EQ (src_type, Qundecoded_file_name))
4307 src_desc_type = TYPE_FILE_NAME;
4308 else
4309 src_desc_type = mac_get_code_from_arg (src_type, 0);
4311 if (EQ (dst_type, Qundecoded_file_name))
4312 dst_desc_type = TYPE_FILE_NAME;
4313 else
4314 dst_desc_type = mac_get_code_from_arg (dst_type, 0);
4316 BLOCK_INPUT;
4317 err = AECoercePtr (src_desc_type, SDATA (src_data), SBYTES (src_data),
4318 dst_desc_type, &dst_desc);
4319 if (err == noErr)
4321 result = Fcdr (mac_aedesc_to_lisp (&dst_desc));
4322 AEDisposeDesc (&dst_desc);
4324 UNBLOCK_INPUT;
4326 return result;
4330 #if TARGET_API_MAC_CARBON
4331 static Lisp_Object Qxml, Qmime_charset;
4332 static Lisp_Object QNFD, QNFKD, QNFC, QNFKC, QHFS_plus_D, QHFS_plus_C;
4334 DEFUN ("mac-get-preference", Fmac_get_preference, Smac_get_preference, 1, 4, 0,
4335 doc: /* Return the application preference value for KEY.
4336 KEY is either a string specifying a preference key, or a list of key
4337 strings. If it is a list, the (i+1)-th element is used as a key for
4338 the CFDictionary value obtained by the i-th element. Return nil if
4339 lookup is failed at some stage.
4341 Optional arg APPLICATION is an application ID string. If omitted or
4342 nil, that stands for the current application.
4344 Optional arg FORMAT specifies the data format of the return value. If
4345 omitted or nil, each Core Foundation object is converted into a
4346 corresponding Lisp object as follows:
4348 Core Foundation Lisp Tag
4349 ------------------------------------------------------------
4350 CFString Multibyte string string
4351 CFNumber Integer or float number
4352 CFBoolean Symbol (t or nil) boolean
4353 CFDate List of three integers date
4354 (cf. `current-time')
4355 CFData Unibyte string data
4356 CFArray Vector array
4357 CFDictionary Alist or hash table dictionary
4358 (depending on HASH-BOUND)
4360 If it is t, a symbol that represents the type of the original Core
4361 Foundation object is prepended. If it is `xml', the value is returned
4362 as an XML representation.
4364 Optional arg HASH-BOUND specifies which kinds of the list objects,
4365 alists or hash tables, are used as the targets of the conversion from
4366 CFDictionary. If HASH-BOUND is a negative integer or nil, always
4367 generate alists. If HASH-BOUND >= 0, generate an alist if the number
4368 of keys in the dictionary is smaller than HASH-BOUND, and a hash table
4369 otherwise. */)
4370 (key, application, format, hash_bound)
4371 Lisp_Object key, application, format, hash_bound;
4373 CFStringRef app_id, key_str;
4374 CFPropertyListRef app_plist = NULL, plist;
4375 Lisp_Object result = Qnil, tmp;
4377 if (STRINGP (key))
4378 key = Fcons (key, Qnil);
4379 else
4381 CHECK_CONS (key);
4382 for (tmp = key; CONSP (tmp); tmp = XCDR (tmp))
4383 CHECK_STRING_CAR (tmp);
4384 if (!NILP (tmp))
4385 wrong_type_argument (Qlistp, key);
4387 if (!NILP (application))
4388 CHECK_STRING (application);
4389 CHECK_SYMBOL (format);
4390 if (!NILP (hash_bound))
4391 CHECK_NUMBER (hash_bound);
4393 BLOCK_INPUT;
4395 app_id = kCFPreferencesCurrentApplication;
4396 if (!NILP (application))
4398 app_id = cfstring_create_with_string (application);
4399 if (app_id == NULL)
4400 goto out;
4402 key_str = cfstring_create_with_string (XCAR (key));
4403 if (key_str == NULL)
4404 goto out;
4405 app_plist = CFPreferencesCopyAppValue (key_str, app_id);
4406 CFRelease (key_str);
4407 if (app_plist == NULL)
4408 goto out;
4410 plist = app_plist;
4411 for (key = XCDR (key); CONSP (key); key = XCDR (key))
4413 if (CFGetTypeID (plist) != CFDictionaryGetTypeID ())
4414 break;
4415 key_str = cfstring_create_with_string (XCAR (key));
4416 if (key_str == NULL)
4417 goto out;
4418 plist = CFDictionaryGetValue (plist, key_str);
4419 CFRelease (key_str);
4420 if (plist == NULL)
4421 goto out;
4424 if (NILP (key))
4425 if (EQ (format, Qxml))
4427 CFDataRef data = CFPropertyListCreateXMLData (NULL, plist);
4428 if (data == NULL)
4429 goto out;
4430 result = cfdata_to_lisp (data);
4431 CFRelease (data);
4433 else
4434 result =
4435 cfproperty_list_to_lisp (plist, EQ (format, Qt),
4436 NILP (hash_bound) ? -1 : XINT (hash_bound));
4438 out:
4439 if (app_plist)
4440 CFRelease (app_plist);
4441 CFRelease (app_id);
4443 UNBLOCK_INPUT;
4445 return result;
4449 static CFStringEncoding
4450 get_cfstring_encoding_from_lisp (obj)
4451 Lisp_Object obj;
4453 CFStringRef iana_name;
4454 CFStringEncoding encoding = kCFStringEncodingInvalidId;
4456 if (NILP (obj))
4457 return kCFStringEncodingUnicode;
4459 if (INTEGERP (obj))
4460 return XINT (obj);
4462 if (SYMBOLP (obj) && !NILP (Fcoding_system_p (obj)))
4464 Lisp_Object coding_spec, plist;
4466 coding_spec = Fget (obj, Qcoding_system);
4467 plist = XVECTOR (coding_spec)->contents[3];
4468 obj = Fplist_get (XVECTOR (coding_spec)->contents[3], Qmime_charset);
4471 if (SYMBOLP (obj))
4472 obj = SYMBOL_NAME (obj);
4474 if (STRINGP (obj))
4476 iana_name = cfstring_create_with_string (obj);
4477 if (iana_name)
4479 encoding = CFStringConvertIANACharSetNameToEncoding (iana_name);
4480 CFRelease (iana_name);
4484 return encoding;
4487 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
4488 static CFStringRef
4489 cfstring_create_normalized (str, symbol)
4490 CFStringRef str;
4491 Lisp_Object symbol;
4493 int form = -1;
4494 TextEncodingVariant variant;
4495 float initial_mag = 0.0;
4496 CFStringRef result = NULL;
4498 if (EQ (symbol, QNFD))
4499 form = kCFStringNormalizationFormD;
4500 else if (EQ (symbol, QNFKD))
4501 form = kCFStringNormalizationFormKD;
4502 else if (EQ (symbol, QNFC))
4503 form = kCFStringNormalizationFormC;
4504 else if (EQ (symbol, QNFKC))
4505 form = kCFStringNormalizationFormKC;
4506 else if (EQ (symbol, QHFS_plus_D))
4508 variant = kUnicodeHFSPlusDecompVariant;
4509 initial_mag = 1.5;
4511 else if (EQ (symbol, QHFS_plus_C))
4513 variant = kUnicodeHFSPlusCompVariant;
4514 initial_mag = 1.0;
4517 if (form >= 0)
4519 CFMutableStringRef mut_str = CFStringCreateMutableCopy (NULL, 0, str);
4521 if (mut_str)
4523 CFStringNormalize (mut_str, form);
4524 result = mut_str;
4527 else if (initial_mag > 0.0)
4529 UnicodeToTextInfo uni = NULL;
4530 UnicodeMapping map;
4531 CFIndex length;
4532 UniChar *in_text, *buffer = NULL, *out_buf = NULL;
4533 OSErr err = noErr;
4534 ByteCount out_read, out_size, out_len;
4536 map.unicodeEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault,
4537 kUnicodeNoSubset,
4538 kTextEncodingDefaultFormat);
4539 map.otherEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault,
4540 variant,
4541 kTextEncodingDefaultFormat);
4542 map.mappingVersion = kUnicodeUseLatestMapping;
4544 length = CFStringGetLength (str);
4545 out_size = (int)((float)length * initial_mag) * sizeof (UniChar);
4546 if (out_size < 32)
4547 out_size = 32;
4549 in_text = (UniChar *)CFStringGetCharactersPtr (str);
4550 if (in_text == NULL)
4552 buffer = xmalloc (sizeof (UniChar) * length);
4553 if (buffer)
4555 CFStringGetCharacters (str, CFRangeMake (0, length), buffer);
4556 in_text = buffer;
4560 if (in_text)
4561 err = CreateUnicodeToTextInfo(&map, &uni);
4562 while (err == noErr)
4564 out_buf = xmalloc (out_size);
4565 if (out_buf == NULL)
4566 err = mFulErr;
4567 else
4568 err = ConvertFromUnicodeToText (uni, length * sizeof (UniChar),
4569 in_text,
4570 kUnicodeDefaultDirectionMask,
4571 0, NULL, NULL, NULL,
4572 out_size, &out_read, &out_len,
4573 out_buf);
4574 if (err == noErr && out_read < length * sizeof (UniChar))
4576 xfree (out_buf);
4577 out_size += length;
4579 else
4580 break;
4582 if (err == noErr)
4583 result = CFStringCreateWithCharacters (NULL, out_buf,
4584 out_len / sizeof (UniChar));
4585 if (uni)
4586 DisposeUnicodeToTextInfo (&uni);
4587 if (out_buf)
4588 xfree (out_buf);
4589 if (buffer)
4590 xfree (buffer);
4592 else
4594 result = str;
4595 CFRetain (result);
4598 return result;
4600 #endif
4602 DEFUN ("mac-code-convert-string", Fmac_code_convert_string, Smac_code_convert_string, 3, 4, 0,
4603 doc: /* Convert STRING from SOURCE encoding to TARGET encoding.
4604 The conversion is performed using the converter provided by the system.
4605 Each encoding is specified by either a coding system symbol, a mime
4606 charset string, or an integer as a CFStringEncoding value. Nil for
4607 encoding means UTF-16 in native byte order, no byte order mark.
4608 On Mac OS X 10.2 and later, you can do Unicode Normalization by
4609 specifying the optional argument NORMALIZATION-FORM with a symbol NFD,
4610 NFKD, NFC, NFKC, HFS+D, or HFS+C.
4611 On successful conversion, return the result string, else return nil. */)
4612 (string, source, target, normalization_form)
4613 Lisp_Object string, source, target, normalization_form;
4615 Lisp_Object result = Qnil;
4616 CFStringEncoding src_encoding, tgt_encoding;
4617 CFStringRef str = NULL;
4619 CHECK_STRING (string);
4620 if (!INTEGERP (source) && !STRINGP (source))
4621 CHECK_SYMBOL (source);
4622 if (!INTEGERP (target) && !STRINGP (target))
4623 CHECK_SYMBOL (target);
4624 CHECK_SYMBOL (normalization_form);
4626 BLOCK_INPUT;
4628 src_encoding = get_cfstring_encoding_from_lisp (source);
4629 tgt_encoding = get_cfstring_encoding_from_lisp (target);
4631 /* We really want string_to_unibyte, but since it doesn't exist yet, we
4632 use string_as_unibyte which works as well, except for the fact that
4633 it's too permissive (it doesn't check that the multibyte string only
4634 contain single-byte chars). */
4635 string = Fstring_as_unibyte (string);
4636 if (src_encoding != kCFStringEncodingInvalidId
4637 && tgt_encoding != kCFStringEncodingInvalidId)
4638 str = CFStringCreateWithBytes (NULL, SDATA (string), SBYTES (string),
4639 src_encoding, !NILP (source));
4640 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
4641 if (str)
4643 CFStringRef saved_str = str;
4645 str = cfstring_create_normalized (saved_str, normalization_form);
4646 CFRelease (saved_str);
4648 #endif
4649 if (str)
4651 CFIndex str_len, buf_len;
4653 str_len = CFStringGetLength (str);
4654 if (CFStringGetBytes (str, CFRangeMake (0, str_len), tgt_encoding, 0,
4655 !NILP (target), NULL, 0, &buf_len) == str_len)
4657 result = make_uninit_string (buf_len);
4658 CFStringGetBytes (str, CFRangeMake (0, str_len), tgt_encoding, 0,
4659 !NILP (target), SDATA (result), buf_len, NULL);
4661 CFRelease (str);
4664 UNBLOCK_INPUT;
4666 return result;
4668 #endif /* TARGET_API_MAC_CARBON */
4671 DEFUN ("mac-clear-font-name-table", Fmac_clear_font_name_table, Smac_clear_font_name_table, 0, 0, 0,
4672 doc: /* Clear the font name table. */)
4675 check_mac ();
4676 mac_clear_font_name_table ();
4677 return Qnil;
4681 static Lisp_Object
4682 mac_get_system_locale ()
4684 OSErr err;
4685 LangCode lang;
4686 RegionCode region;
4687 LocaleRef locale;
4688 Str255 str;
4690 lang = GetScriptVariable (smSystemScript, smScriptLang);
4691 region = GetScriptManagerVariable (smRegionCode);
4692 err = LocaleRefFromLangOrRegionCode (lang, region, &locale);
4693 if (err == noErr)
4694 err = LocaleRefGetPartString (locale, kLocaleAllPartsMask,
4695 sizeof (str), str);
4696 if (err == noErr)
4697 return build_string (str);
4698 else
4699 return Qnil;
4703 #ifdef MAC_OSX
4704 #undef select
4706 extern int inhibit_window_system;
4707 extern int noninteractive;
4709 /* Unlike in X11, window events in Carbon do not come from sockets.
4710 So we cannot simply use `select' to monitor two kinds of inputs:
4711 window events and process outputs. We emulate such functionality
4712 by regarding fd 0 as the window event channel and simultaneously
4713 monitoring both kinds of input channels. It is implemented by
4714 dividing into some cases:
4715 1. The window event channel is not involved.
4716 -> Use `select'.
4717 2. Sockets are not involved.
4718 -> Use ReceiveNextEvent.
4719 3. [If SELECT_USE_CFSOCKET is defined]
4720 Only the window event channel and socket read channels are
4721 involved, and timeout is not too short (greater than
4722 SELECT_TIMEOUT_THRESHHOLD_RUNLOOP seconds).
4723 -> Create CFSocket for each socket and add it into the current
4724 event RunLoop so that a `ready-to-read' event can be posted
4725 to the event queue that is also used for window events. Then
4726 ReceiveNextEvent can wait for both kinds of inputs.
4727 4. Otherwise.
4728 -> Periodically poll the window input channel while repeatedly
4729 executing `select' with a short timeout
4730 (SELECT_POLLING_PERIOD_USEC microseconds). */
4732 #define SELECT_POLLING_PERIOD_USEC 20000
4733 #ifdef SELECT_USE_CFSOCKET
4734 #define SELECT_TIMEOUT_THRESHOLD_RUNLOOP 0.2
4735 #define EVENT_CLASS_SOCK 'Sock'
4737 static void
4738 socket_callback (s, type, address, data, info)
4739 CFSocketRef s;
4740 CFSocketCallBackType type;
4741 CFDataRef address;
4742 const void *data;
4743 void *info;
4745 EventRef event;
4747 CreateEvent (NULL, EVENT_CLASS_SOCK, 0, 0, kEventAttributeNone, &event);
4748 PostEventToQueue (GetCurrentEventQueue (), event, kEventPriorityStandard);
4749 ReleaseEvent (event);
4751 #endif /* SELECT_USE_CFSOCKET */
4753 static int
4754 select_and_poll_event (n, rfds, wfds, efds, timeout)
4755 int n;
4756 SELECT_TYPE *rfds;
4757 SELECT_TYPE *wfds;
4758 SELECT_TYPE *efds;
4759 struct timeval *timeout;
4761 int r;
4762 OSErr err;
4764 r = select (n, rfds, wfds, efds, timeout);
4765 if (r != -1)
4767 BLOCK_INPUT;
4768 err = ReceiveNextEvent (0, NULL, kEventDurationNoWait,
4769 kEventLeaveInQueue, NULL);
4770 UNBLOCK_INPUT;
4771 if (err == noErr)
4773 FD_SET (0, rfds);
4774 r++;
4777 return r;
4780 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1020
4781 #undef SELECT_INVALIDATE_CFSOCKET
4782 #endif
4785 sys_select (n, rfds, wfds, efds, timeout)
4786 int n;
4787 SELECT_TYPE *rfds;
4788 SELECT_TYPE *wfds;
4789 SELECT_TYPE *efds;
4790 struct timeval *timeout;
4792 OSErr err;
4793 int i, r;
4794 EMACS_TIME select_timeout;
4796 if (inhibit_window_system || noninteractive
4797 || rfds == NULL || !FD_ISSET (0, rfds))
4798 return select (n, rfds, wfds, efds, timeout);
4800 FD_CLR (0, rfds);
4802 if (wfds == NULL && efds == NULL)
4804 int nsocks = 0;
4805 SELECT_TYPE orfds = *rfds;
4807 EventTimeout timeout_sec =
4808 (timeout
4809 ? (EMACS_SECS (*timeout) * kEventDurationSecond
4810 + EMACS_USECS (*timeout) * kEventDurationMicrosecond)
4811 : kEventDurationForever);
4813 for (i = 1; i < n; i++)
4814 if (FD_ISSET (i, rfds))
4815 nsocks++;
4817 if (nsocks == 0)
4819 BLOCK_INPUT;
4820 err = ReceiveNextEvent (0, NULL, timeout_sec,
4821 kEventLeaveInQueue, NULL);
4822 UNBLOCK_INPUT;
4823 if (err == noErr)
4825 FD_SET (0, rfds);
4826 return 1;
4828 else
4829 return 0;
4832 /* Avoid initial overhead of RunLoop setup for the case that
4833 some input is already available. */
4834 EMACS_SET_SECS_USECS (select_timeout, 0, 0);
4835 r = select_and_poll_event (n, rfds, wfds, efds, &select_timeout);
4836 if (r != 0 || timeout_sec == 0.0)
4837 return r;
4839 *rfds = orfds;
4841 #ifdef SELECT_USE_CFSOCKET
4842 if (timeout_sec > 0 && timeout_sec <= SELECT_TIMEOUT_THRESHOLD_RUNLOOP)
4843 goto poll_periodically;
4846 CFRunLoopRef runloop =
4847 (CFRunLoopRef) GetCFRunLoopFromEventLoop (GetCurrentEventLoop ());
4848 EventTypeSpec specs[] = {{EVENT_CLASS_SOCK, 0}};
4849 #ifdef SELECT_INVALIDATE_CFSOCKET
4850 CFSocketRef *shead, *s;
4851 #else
4852 CFRunLoopSourceRef *shead, *s;
4853 #endif
4855 BLOCK_INPUT;
4857 #ifdef SELECT_INVALIDATE_CFSOCKET
4858 shead = xmalloc (sizeof (CFSocketRef) * nsocks);
4859 #else
4860 shead = xmalloc (sizeof (CFRunLoopSourceRef) * nsocks);
4861 #endif
4862 s = shead;
4863 for (i = 1; i < n; i++)
4864 if (FD_ISSET (i, rfds))
4866 CFSocketRef socket =
4867 CFSocketCreateWithNative (NULL, i, kCFSocketReadCallBack,
4868 socket_callback, NULL);
4869 CFRunLoopSourceRef source =
4870 CFSocketCreateRunLoopSource (NULL, socket, 0);
4872 #ifdef SELECT_INVALIDATE_CFSOCKET
4873 CFSocketSetSocketFlags (socket, 0);
4874 #endif
4875 CFRunLoopAddSource (runloop, source, kCFRunLoopDefaultMode);
4876 #ifdef SELECT_INVALIDATE_CFSOCKET
4877 CFRelease (source);
4878 *s = socket;
4879 #else
4880 CFRelease (socket);
4881 *s = source;
4882 #endif
4883 s++;
4886 err = ReceiveNextEvent (0, NULL, timeout_sec, kEventLeaveInQueue, NULL);
4890 --s;
4891 #ifdef SELECT_INVALIDATE_CFSOCKET
4892 CFSocketInvalidate (*s);
4893 #else
4894 CFRunLoopRemoveSource (runloop, *s, kCFRunLoopDefaultMode);
4895 #endif
4896 CFRelease (*s);
4898 while (s != shead);
4900 xfree (shead);
4902 if (err)
4904 FD_ZERO (rfds);
4905 r = 0;
4907 else
4909 FlushEventsMatchingListFromQueue (GetCurrentEventQueue (),
4910 GetEventTypeCount (specs),
4911 specs);
4912 EMACS_SET_SECS_USECS (select_timeout, 0, 0);
4913 r = select_and_poll_event (n, rfds, wfds, efds, &select_timeout);
4916 UNBLOCK_INPUT;
4918 return r;
4920 #endif /* SELECT_USE_CFSOCKET */
4923 poll_periodically:
4925 EMACS_TIME end_time, now, remaining_time;
4926 SELECT_TYPE orfds = *rfds, owfds, oefds;
4928 if (wfds)
4929 owfds = *wfds;
4930 if (efds)
4931 oefds = *efds;
4932 if (timeout)
4934 remaining_time = *timeout;
4935 EMACS_GET_TIME (now);
4936 EMACS_ADD_TIME (end_time, now, remaining_time);
4941 EMACS_SET_SECS_USECS (select_timeout, 0, SELECT_POLLING_PERIOD_USEC);
4942 if (timeout && EMACS_TIME_LT (remaining_time, select_timeout))
4943 select_timeout = remaining_time;
4944 r = select_and_poll_event (n, rfds, wfds, efds, &select_timeout);
4945 if (r != 0)
4946 return r;
4948 *rfds = orfds;
4949 if (wfds)
4950 *wfds = owfds;
4951 if (efds)
4952 *efds = oefds;
4954 if (timeout)
4956 EMACS_GET_TIME (now);
4957 EMACS_SUB_TIME (remaining_time, end_time, now);
4960 while (!timeout || EMACS_TIME_LT (now, end_time));
4962 FD_ZERO (rfds);
4963 if (wfds)
4964 FD_ZERO (wfds);
4965 if (efds)
4966 FD_ZERO (efds);
4967 return 0;
4971 /* Set up environment variables so that Emacs can correctly find its
4972 support files when packaged as an application bundle. Directories
4973 placed in /usr/local/share/emacs/<emacs-version>/, /usr/local/bin,
4974 and /usr/local/libexec/emacs/<emacs-version>/<system-configuration>
4975 by `make install' by default can instead be placed in
4976 .../Emacs.app/Contents/Resources/ and
4977 .../Emacs.app/Contents/MacOS/. Each of these environment variables
4978 is changed only if it is not already set. Presumably if the user
4979 sets an environment variable, he will want to use files in his path
4980 instead of ones in the application bundle. */
4981 void
4982 init_mac_osx_environment ()
4984 CFBundleRef bundle;
4985 CFURLRef bundleURL;
4986 CFStringRef cf_app_bundle_pathname;
4987 int app_bundle_pathname_len;
4988 char *app_bundle_pathname;
4989 char *p, *q;
4990 struct stat st;
4992 /* Initialize locale related variables. */
4993 mac_system_script_code =
4994 (ScriptCode) GetScriptManagerVariable (smSysScript);
4995 Vmac_system_locale = mac_get_system_locale ();
4997 /* Fetch the pathname of the application bundle as a C string into
4998 app_bundle_pathname. */
5000 bundle = CFBundleGetMainBundle ();
5001 if (!bundle || CFBundleGetIdentifier (bundle) == NULL)
5003 /* We could not find the bundle identifier. For now, prevent
5004 the fatal error by bringing it up in the terminal. */
5005 inhibit_window_system = 1;
5006 return;
5009 bundleURL = CFBundleCopyBundleURL (bundle);
5010 if (!bundleURL)
5011 return;
5013 cf_app_bundle_pathname = CFURLCopyFileSystemPath (bundleURL,
5014 kCFURLPOSIXPathStyle);
5015 app_bundle_pathname_len = CFStringGetLength (cf_app_bundle_pathname);
5016 app_bundle_pathname = (char *) alloca (app_bundle_pathname_len + 1);
5018 if (!CFStringGetCString (cf_app_bundle_pathname,
5019 app_bundle_pathname,
5020 app_bundle_pathname_len + 1,
5021 kCFStringEncodingISOLatin1))
5023 CFRelease (cf_app_bundle_pathname);
5024 return;
5027 CFRelease (cf_app_bundle_pathname);
5029 /* P should have sufficient room for the pathname of the bundle plus
5030 the subpath in it leading to the respective directories. Q
5031 should have three times that much room because EMACSLOADPATH can
5032 have the value "<path to lisp dir>:<path to leim dir>:<path to
5033 site-lisp dir>". */
5034 p = (char *) alloca (app_bundle_pathname_len + 50);
5035 q = (char *) alloca (3 * app_bundle_pathname_len + 150);
5036 if (!getenv ("EMACSLOADPATH"))
5038 q[0] = '\0';
5040 strcpy (p, app_bundle_pathname);
5041 strcat (p, "/Contents/Resources/lisp");
5042 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5043 strcat (q, p);
5045 strcpy (p, app_bundle_pathname);
5046 strcat (p, "/Contents/Resources/leim");
5047 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5049 if (q[0] != '\0')
5050 strcat (q, ":");
5051 strcat (q, p);
5054 strcpy (p, app_bundle_pathname);
5055 strcat (p, "/Contents/Resources/site-lisp");
5056 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5058 if (q[0] != '\0')
5059 strcat (q, ":");
5060 strcat (q, p);
5063 if (q[0] != '\0')
5064 setenv ("EMACSLOADPATH", q, 1);
5067 if (!getenv ("EMACSPATH"))
5069 q[0] = '\0';
5071 strcpy (p, app_bundle_pathname);
5072 strcat (p, "/Contents/MacOS/libexec");
5073 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5074 strcat (q, p);
5076 strcpy (p, app_bundle_pathname);
5077 strcat (p, "/Contents/MacOS/bin");
5078 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5080 if (q[0] != '\0')
5081 strcat (q, ":");
5082 strcat (q, p);
5085 if (q[0] != '\0')
5086 setenv ("EMACSPATH", q, 1);
5089 if (!getenv ("EMACSDATA"))
5091 strcpy (p, app_bundle_pathname);
5092 strcat (p, "/Contents/Resources/etc");
5093 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5094 setenv ("EMACSDATA", p, 1);
5097 if (!getenv ("EMACSDOC"))
5099 strcpy (p, app_bundle_pathname);
5100 strcat (p, "/Contents/Resources/etc");
5101 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5102 setenv ("EMACSDOC", p, 1);
5105 if (!getenv ("INFOPATH"))
5107 strcpy (p, app_bundle_pathname);
5108 strcat (p, "/Contents/Resources/info");
5109 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5110 setenv ("INFOPATH", p, 1);
5113 #endif /* MAC_OSX */
5116 void
5117 syms_of_mac ()
5119 Qundecoded_file_name = intern ("undecoded-file-name");
5120 staticpro (&Qundecoded_file_name);
5122 #if TARGET_API_MAC_CARBON
5123 Qstring = intern ("string"); staticpro (&Qstring);
5124 Qnumber = intern ("number"); staticpro (&Qnumber);
5125 Qboolean = intern ("boolean"); staticpro (&Qboolean);
5126 Qdate = intern ("date"); staticpro (&Qdate);
5127 Qdata = intern ("data"); staticpro (&Qdata);
5128 Qarray = intern ("array"); staticpro (&Qarray);
5129 Qdictionary = intern ("dictionary"); staticpro (&Qdictionary);
5131 Qxml = intern ("xml");
5132 staticpro (&Qxml);
5134 Qmime_charset = intern ("mime-charset");
5135 staticpro (&Qmime_charset);
5137 QNFD = intern ("NFD"); staticpro (&QNFD);
5138 QNFKD = intern ("NFKD"); staticpro (&QNFKD);
5139 QNFC = intern ("NFC"); staticpro (&QNFC);
5140 QNFKC = intern ("NFKC"); staticpro (&QNFKC);
5141 QHFS_plus_D = intern ("HFS+D"); staticpro (&QHFS_plus_D);
5142 QHFS_plus_C = intern ("HFS+C"); staticpro (&QHFS_plus_C);
5143 #endif
5145 defsubr (&Smac_coerce_ae_data);
5146 #if TARGET_API_MAC_CARBON
5147 defsubr (&Smac_get_preference);
5148 defsubr (&Smac_code_convert_string);
5149 #endif
5150 defsubr (&Smac_clear_font_name_table);
5152 defsubr (&Smac_set_file_creator);
5153 defsubr (&Smac_set_file_type);
5154 defsubr (&Smac_get_file_creator);
5155 defsubr (&Smac_get_file_type);
5156 defsubr (&Sdo_applescript);
5157 defsubr (&Smac_file_name_to_posix);
5158 defsubr (&Sposix_file_name_to_mac);
5160 DEFVAR_INT ("mac-system-script-code", &mac_system_script_code,
5161 doc: /* The system script code. */);
5162 mac_system_script_code = (ScriptCode) GetScriptManagerVariable (smSysScript);
5164 DEFVAR_LISP ("mac-system-locale", &Vmac_system_locale,
5165 doc: /* The system locale identifier string.
5166 This is not a POSIX locale ID, but an ICU locale ID. So encoding
5167 information is not included. */);
5168 Vmac_system_locale = mac_get_system_locale ();
5171 /* arch-tag: 29d30c1f-0c6b-4f88-8a6d-0558d7f9dbff
5172 (do not change this comment) */