Update Changelog.
[emacs.git] / src / mac.c
blobd783243399d1d089461e5fe82d44c3105c2afdf2
1 /* Unix emulation routines for GNU Emacs on the Mac OS.
2 Copyright (C) 2000, 2001, 2002, 2003, 2004,
3 2005, 2006, 2007 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 #ifdef MAC_OSX
32 #undef select
33 #endif
34 #include "systime.h"
35 #include "sysselect.h"
36 #include "blockinput.h"
38 #include "macterm.h"
40 #include "charset.h"
41 #include "coding.h"
42 #if !TARGET_API_MAC_CARBON
43 #include <Files.h>
44 #include <MacTypes.h>
45 #include <TextUtils.h>
46 #include <Folders.h>
47 #include <Resources.h>
48 #include <Aliases.h>
49 #include <Timer.h>
50 #include <OSA.h>
51 #include <AppleScript.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 #if TARGET_API_MAC_CARBON
83 static int wakeup_from_rne_enabled_p = 0;
84 #define ENABLE_WAKEUP_FROM_RNE (wakeup_from_rne_enabled_p = 1)
85 #define DISABLE_WAKEUP_FROM_RNE (wakeup_from_rne_enabled_p = 0)
86 #else
87 #define ENABLE_WAKEUP_FROM_RNE 0
88 #define DISABLE_WAKEUP_FROM_RNE 0
89 #endif
91 #ifndef MAC_OSX
92 static OSErr posix_pathname_to_fsspec P_ ((const char *, FSSpec *));
93 static OSErr fsspec_to_posix_pathname P_ ((const FSSpec *, char *, int));
94 #endif
96 /* When converting from Mac to Unix pathnames, /'s in folder names are
97 converted to :'s. This function, used in copying folder names,
98 performs a strncat and converts all character a to b in the copy of
99 the string s2 appended to the end of s1. */
101 void
102 string_cat_and_replace (char *s1, const char *s2, int n, char a, char b)
104 int l1 = strlen (s1);
105 int l2 = strlen (s2);
106 char *p = s1 + l1;
107 int i;
109 strncat (s1, s2, n);
110 for (i = 0; i < l2; i++)
112 if (*p == a)
113 *p = b;
114 p++;
119 /* Convert a Mac pathname to Posix form. A Mac full pathname is one
120 that does not begin with a ':' and contains at least one ':'. A Mac
121 full pathname causes a '/' to be prepended to the Posix pathname.
122 The algorithm for the rest of the pathname is as follows:
123 For each segment between two ':',
124 if it is non-null, copy as is and then add a '/' at the end,
125 otherwise, insert a "../" into the Posix pathname.
126 Returns 1 if successful; 0 if fails. */
129 mac_to_posix_pathname (const char *mfn, char *ufn, int ufnbuflen)
131 const char *p, *q, *pe;
133 strcpy (ufn, "");
135 if (*mfn == '\0')
136 return 1;
138 p = strchr (mfn, ':');
139 if (p != 0 && p != mfn) /* full pathname */
140 strcat (ufn, "/");
142 p = mfn;
143 if (*p == ':')
144 p++;
146 pe = mfn + strlen (mfn);
147 while (p < pe)
149 q = strchr (p, ':');
150 if (q)
152 if (q == p)
153 { /* two consecutive ':' */
154 if (strlen (ufn) + 3 >= ufnbuflen)
155 return 0;
156 strcat (ufn, "../");
158 else
160 if (strlen (ufn) + (q - p) + 1 >= ufnbuflen)
161 return 0;
162 string_cat_and_replace (ufn, p, q - p, '/', ':');
163 strcat (ufn, "/");
165 p = q + 1;
167 else
169 if (strlen (ufn) + (pe - p) >= ufnbuflen)
170 return 0;
171 string_cat_and_replace (ufn, p, pe - p, '/', ':');
172 /* no separator for last one */
173 p = pe;
177 return 1;
181 extern char *get_temp_dir_name ();
184 /* Convert a Posix pathname to Mac form. Approximately reverse of the
185 above in algorithm. */
188 posix_to_mac_pathname (const char *ufn, char *mfn, int mfnbuflen)
190 const char *p, *q, *pe;
191 char expanded_pathname[MAXPATHLEN+1];
193 strcpy (mfn, "");
195 if (*ufn == '\0')
196 return 1;
198 p = ufn;
200 /* Check for and handle volume names. Last comparison: strangely
201 somewhere "/.emacs" is passed. A temporary fix for now. */
202 if (*p == '/' && strchr (p+1, '/') == NULL && strcmp (p, "/.emacs") != 0)
204 if (strlen (p) + 1 > mfnbuflen)
205 return 0;
206 strcpy (mfn, p+1);
207 strcat (mfn, ":");
208 return 1;
211 /* expand to emacs dir found by init_emacs_passwd_dir */
212 if (strncmp (p, "~emacs/", 7) == 0)
214 struct passwd *pw = getpwnam ("emacs");
215 p += 7;
216 if (strlen (pw->pw_dir) + strlen (p) > MAXPATHLEN)
217 return 0;
218 strcpy (expanded_pathname, pw->pw_dir);
219 strcat (expanded_pathname, p);
220 p = expanded_pathname;
221 /* now p points to the pathname with emacs dir prefix */
223 else if (strncmp (p, "/tmp/", 5) == 0)
225 char *t = get_temp_dir_name ();
226 p += 5;
227 if (strlen (t) + strlen (p) > MAXPATHLEN)
228 return 0;
229 strcpy (expanded_pathname, t);
230 strcat (expanded_pathname, p);
231 p = expanded_pathname;
232 /* now p points to the pathname with emacs dir prefix */
234 else if (*p != '/') /* relative pathname */
235 strcat (mfn, ":");
237 if (*p == '/')
238 p++;
240 pe = p + strlen (p);
241 while (p < pe)
243 q = strchr (p, '/');
244 if (q)
246 if (q - p == 2 && *p == '.' && *(p+1) == '.')
248 if (strlen (mfn) + 1 >= mfnbuflen)
249 return 0;
250 strcat (mfn, ":");
252 else
254 if (strlen (mfn) + (q - p) + 1 >= mfnbuflen)
255 return 0;
256 string_cat_and_replace (mfn, p, q - p, ':', '/');
257 strcat (mfn, ":");
259 p = q + 1;
261 else
263 if (strlen (mfn) + (pe - p) >= mfnbuflen)
264 return 0;
265 string_cat_and_replace (mfn, p, pe - p, ':', '/');
266 p = pe;
270 return 1;
274 /***********************************************************************
275 Conversions on Apple event objects
276 ***********************************************************************/
278 static Lisp_Object Qundecoded_file_name;
280 static struct {
281 AEKeyword keyword;
282 char *name;
283 Lisp_Object symbol;
284 } ae_attr_table [] =
285 {{keyTransactionIDAttr, "transaction-id"},
286 {keyReturnIDAttr, "return-id"},
287 {keyEventClassAttr, "event-class"},
288 {keyEventIDAttr, "event-id"},
289 {keyAddressAttr, "address"},
290 {keyOptionalKeywordAttr, "optional-keyword"},
291 {keyTimeoutAttr, "timeout"},
292 {keyInteractLevelAttr, "interact-level"},
293 {keyEventSourceAttr, "event-source"},
294 /* {keyMissedKeywordAttr, "missed-keyword"}, */
295 {keyOriginalAddressAttr, "original-address"},
296 {keyReplyRequestedAttr, "reply-requested"},
297 {KEY_EMACS_SUSPENSION_ID_ATTR, "emacs-suspension-id"}
300 static Lisp_Object
301 mac_aelist_to_lisp (desc_list)
302 const AEDescList *desc_list;
304 OSErr err;
305 long count;
306 Lisp_Object result, elem;
307 DescType desc_type;
308 Size size;
309 AEKeyword keyword;
310 AEDesc desc;
311 int attribute_p = 0;
313 err = AECountItems (desc_list, &count);
314 if (err != noErr)
315 return Qnil;
316 result = Qnil;
318 again:
319 while (count > 0)
321 if (attribute_p)
323 keyword = ae_attr_table[count - 1].keyword;
324 err = AESizeOfAttribute (desc_list, keyword, &desc_type, &size);
326 else
327 err = AESizeOfNthItem (desc_list, count, &desc_type, &size);
329 if (err == noErr)
330 switch (desc_type)
332 case typeAEList:
333 case typeAERecord:
334 case typeAppleEvent:
335 if (attribute_p)
336 err = AEGetAttributeDesc (desc_list, keyword, typeWildCard,
337 &desc);
338 else
339 err = AEGetNthDesc (desc_list, count, typeWildCard,
340 &keyword, &desc);
341 if (err != noErr)
342 break;
343 elem = mac_aelist_to_lisp (&desc);
344 AEDisposeDesc (&desc);
345 break;
347 default:
348 if (desc_type == typeNull)
349 elem = Qnil;
350 else
352 elem = make_uninit_string (size);
353 if (attribute_p)
354 err = AEGetAttributePtr (desc_list, keyword, typeWildCard,
355 &desc_type, SDATA (elem),
356 size, &size);
357 else
358 err = AEGetNthPtr (desc_list, count, typeWildCard, &keyword,
359 &desc_type, SDATA (elem), size, &size);
361 if (err != noErr)
362 break;
363 desc_type = EndianU32_NtoB (desc_type);
364 elem = Fcons (make_unibyte_string ((char *) &desc_type, 4), elem);
365 break;
368 if (err == noErr || desc_list->descriptorType == typeAEList)
370 if (err != noErr)
371 elem = Qnil; /* Don't skip elements in AEList. */
372 else if (desc_list->descriptorType != typeAEList)
374 if (attribute_p)
375 elem = Fcons (ae_attr_table[count-1].symbol, elem);
376 else
378 keyword = EndianU32_NtoB (keyword);
379 elem = Fcons (make_unibyte_string ((char *) &keyword, 4),
380 elem);
384 result = Fcons (elem, result);
387 count--;
390 if (desc_list->descriptorType == typeAppleEvent && !attribute_p)
392 attribute_p = 1;
393 count = sizeof (ae_attr_table) / sizeof (ae_attr_table[0]);
394 goto again;
397 desc_type = EndianU32_NtoB (desc_list->descriptorType);
398 return Fcons (make_unibyte_string ((char *) &desc_type, 4), result);
401 Lisp_Object
402 mac_aedesc_to_lisp (desc)
403 const AEDesc *desc;
405 OSErr err = noErr;
406 DescType desc_type = desc->descriptorType;
407 Lisp_Object result;
409 switch (desc_type)
411 case typeNull:
412 result = Qnil;
413 break;
415 case typeAEList:
416 case typeAERecord:
417 case typeAppleEvent:
418 return mac_aelist_to_lisp (desc);
419 #if 0
420 /* The following one is much simpler, but creates and disposes
421 of Apple event descriptors many times. */
423 long count;
424 Lisp_Object elem;
425 AEKeyword keyword;
426 AEDesc desc1;
428 err = AECountItems (desc, &count);
429 if (err != noErr)
430 break;
431 result = Qnil;
432 while (count > 0)
434 err = AEGetNthDesc (desc, count, typeWildCard, &keyword, &desc1);
435 if (err != noErr)
436 break;
437 elem = mac_aedesc_to_lisp (&desc1);
438 AEDisposeDesc (&desc1);
439 if (desc_type != typeAEList)
441 keyword = EndianU32_NtoB (keyword);
442 elem = Fcons (make_unibyte_string ((char *) &keyword, 4), elem);
444 result = Fcons (elem, result);
445 count--;
448 #endif
449 break;
451 default:
452 #if TARGET_API_MAC_CARBON
453 result = make_uninit_string (AEGetDescDataSize (desc));
454 err = AEGetDescData (desc, SDATA (result), SBYTES (result));
455 #else
456 result = make_uninit_string (GetHandleSize (desc->dataHandle));
457 memcpy (SDATA (result), *(desc->dataHandle), SBYTES (result));
458 #endif
459 break;
462 if (err != noErr)
463 return Qnil;
465 desc_type = EndianU32_NtoB (desc_type);
466 return Fcons (make_unibyte_string ((char *) &desc_type, 4), result);
469 OSErr
470 mac_ae_put_lisp (desc, keyword_or_index, obj)
471 AEDescList *desc;
472 UInt32 keyword_or_index;
473 Lisp_Object obj;
475 OSErr err;
477 if (!(desc->descriptorType == typeAppleEvent
478 || desc->descriptorType == typeAERecord
479 || desc->descriptorType == typeAEList))
480 return errAEWrongDataType;
482 if (CONSP (obj) && STRINGP (XCAR (obj)) && SBYTES (XCAR (obj)) == 4)
484 DescType desc_type1 = EndianU32_BtoN (*((UInt32 *) SDATA (XCAR (obj))));
485 Lisp_Object data = XCDR (obj), rest;
486 AEDesc desc1;
488 switch (desc_type1)
490 case typeNull:
491 case typeAppleEvent:
492 break;
494 case typeAEList:
495 case typeAERecord:
496 err = AECreateList (NULL, 0, desc_type1 == typeAERecord, &desc1);
497 if (err == noErr)
499 for (rest = data; CONSP (rest); rest = XCDR (rest))
501 UInt32 keyword_or_index1 = 0;
502 Lisp_Object elem = XCAR (rest);
504 if (desc_type1 == typeAERecord)
506 if (CONSP (elem) && STRINGP (XCAR (elem))
507 && SBYTES (XCAR (elem)) == 4)
509 keyword_or_index1 =
510 EndianU32_BtoN (*((UInt32 *)
511 SDATA (XCAR (elem))));
512 elem = XCDR (elem);
514 else
515 continue;
518 err = mac_ae_put_lisp (&desc1, keyword_or_index1, elem);
519 if (err != noErr)
520 break;
523 if (err == noErr)
525 if (desc->descriptorType == typeAEList)
526 err = AEPutDesc (desc, keyword_or_index, &desc1);
527 else
528 err = AEPutParamDesc (desc, keyword_or_index, &desc1);
531 AEDisposeDesc (&desc1);
533 return err;
535 default:
536 if (!STRINGP (data))
537 break;
538 if (desc->descriptorType == typeAEList)
539 err = AEPutPtr (desc, keyword_or_index, desc_type1,
540 SDATA (data), SBYTES (data));
541 else
542 err = AEPutParamPtr (desc, keyword_or_index, desc_type1,
543 SDATA (data), SBYTES (data));
544 return err;
548 if (desc->descriptorType == typeAEList)
549 err = AEPutPtr (desc, keyword_or_index, typeNull, NULL, 0);
550 else
551 err = AEPutParamPtr (desc, keyword_or_index, typeNull, NULL, 0);
553 return err;
556 static pascal OSErr
557 mac_coerce_file_name_ptr (type_code, data_ptr, data_size,
558 to_type, handler_refcon, result)
559 DescType type_code;
560 const void *data_ptr;
561 Size data_size;
562 DescType to_type;
563 long handler_refcon;
564 AEDesc *result;
566 OSErr err;
568 if (type_code == typeNull)
569 err = errAECoercionFail;
570 else if (type_code == to_type || to_type == typeWildCard)
571 err = AECreateDesc (TYPE_FILE_NAME, data_ptr, data_size, result);
572 else if (type_code == TYPE_FILE_NAME)
573 /* Coercion from undecoded file name. */
575 #ifdef MAC_OSX
576 CFStringRef str;
577 CFURLRef url = NULL;
578 CFDataRef data = NULL;
580 str = CFStringCreateWithBytes (NULL, data_ptr, data_size,
581 kCFStringEncodingUTF8, false);
582 if (str)
584 url = CFURLCreateWithFileSystemPath (NULL, str,
585 kCFURLPOSIXPathStyle, false);
586 CFRelease (str);
588 if (url)
590 data = CFURLCreateData (NULL, url, kCFStringEncodingUTF8, true);
591 CFRelease (url);
593 if (data)
595 err = AECoercePtr (typeFileURL, CFDataGetBytePtr (data),
596 CFDataGetLength (data), to_type, result);
597 CFRelease (data);
599 else
600 err = memFullErr;
602 if (err != noErr)
604 /* Just to be paranoid ... */
605 FSRef fref;
606 char *buf;
608 buf = xmalloc (data_size + 1);
609 memcpy (buf, data_ptr, data_size);
610 buf[data_size] = '\0';
611 err = FSPathMakeRef (buf, &fref, NULL);
612 xfree (buf);
613 if (err == noErr)
614 err = AECoercePtr (typeFSRef, &fref, sizeof (FSRef),
615 to_type, result);
617 #else
618 FSSpec fs;
619 char *buf;
621 buf = xmalloc (data_size + 1);
622 memcpy (buf, data_ptr, data_size);
623 buf[data_size] = '\0';
624 err = posix_pathname_to_fsspec (buf, &fs);
625 xfree (buf);
626 if (err == noErr)
627 err = AECoercePtr (typeFSS, &fs, sizeof (FSSpec), to_type, result);
628 #endif
630 else if (to_type == TYPE_FILE_NAME)
631 /* Coercion to undecoded file name. */
633 #ifdef MAC_OSX
634 CFURLRef url = NULL;
635 CFStringRef str = NULL;
636 CFDataRef data = NULL;
638 if (type_code == typeFileURL)
639 url = CFURLCreateWithBytes (NULL, data_ptr, data_size,
640 kCFStringEncodingUTF8, NULL);
641 else
643 AEDesc desc;
644 Size size;
645 char *buf;
647 err = AECoercePtr (type_code, data_ptr, data_size,
648 typeFileURL, &desc);
649 if (err == noErr)
651 size = AEGetDescDataSize (&desc);
652 buf = xmalloc (size);
653 err = AEGetDescData (&desc, buf, size);
654 if (err == noErr)
655 url = CFURLCreateWithBytes (NULL, buf, size,
656 kCFStringEncodingUTF8, NULL);
657 xfree (buf);
658 AEDisposeDesc (&desc);
661 if (url)
663 str = CFURLCopyFileSystemPath (url, kCFURLPOSIXPathStyle);
664 CFRelease (url);
666 if (str)
668 data = CFStringCreateExternalRepresentation (NULL, str,
669 kCFStringEncodingUTF8,
670 '\0');
671 CFRelease (str);
673 if (data)
675 err = AECreateDesc (TYPE_FILE_NAME, CFDataGetBytePtr (data),
676 CFDataGetLength (data), result);
677 CFRelease (data);
680 if (err != noErr)
682 /* Coercion from typeAlias to typeFileURL fails on Mac OS X
683 10.2. In such cases, try typeFSRef as a target type. */
684 char file_name[MAXPATHLEN];
686 if (type_code == typeFSRef && data_size == sizeof (FSRef))
687 err = FSRefMakePath (data_ptr, file_name, sizeof (file_name));
688 else
690 AEDesc desc;
691 FSRef fref;
693 err = AECoercePtr (type_code, data_ptr, data_size,
694 typeFSRef, &desc);
695 if (err == noErr)
697 err = AEGetDescData (&desc, &fref, sizeof (FSRef));
698 AEDisposeDesc (&desc);
700 if (err == noErr)
701 err = FSRefMakePath (&fref, file_name, sizeof (file_name));
703 if (err == noErr)
704 err = AECreateDesc (TYPE_FILE_NAME, file_name,
705 strlen (file_name), result);
707 #else
708 char file_name[MAXPATHLEN];
710 if (type_code == typeFSS && data_size == sizeof (FSSpec))
711 err = fsspec_to_posix_pathname (data_ptr, file_name,
712 sizeof (file_name) - 1);
713 else
715 AEDesc desc;
716 FSSpec fs;
718 err = AECoercePtr (type_code, data_ptr, data_size, typeFSS, &desc);
719 if (err == noErr)
721 #if TARGET_API_MAC_CARBON
722 err = AEGetDescData (&desc, &fs, sizeof (FSSpec));
723 #else
724 fs = *(FSSpec *)(*(desc.dataHandle));
725 #endif
726 AEDisposeDesc (&desc);
728 if (err == noErr)
729 err = fsspec_to_posix_pathname (&fs, file_name,
730 sizeof (file_name) - 1);
732 if (err == noErr)
733 err = AECreateDesc (TYPE_FILE_NAME, file_name,
734 strlen (file_name), result);
735 #endif
737 else
738 abort ();
740 if (err != noErr)
741 return errAECoercionFail;
742 return noErr;
745 static pascal OSErr
746 mac_coerce_file_name_desc (from_desc, to_type, handler_refcon, result)
747 const AEDesc *from_desc;
748 DescType to_type;
749 long handler_refcon;
750 AEDesc *result;
752 OSErr err = noErr;
753 DescType from_type = from_desc->descriptorType;
755 if (from_type == typeNull)
756 err = errAECoercionFail;
757 else if (from_type == to_type || to_type == typeWildCard)
758 err = AEDuplicateDesc (from_desc, result);
759 else
761 char *data_ptr;
762 Size data_size;
764 #if TARGET_API_MAC_CARBON
765 data_size = AEGetDescDataSize (from_desc);
766 #else
767 data_size = GetHandleSize (from_desc->dataHandle);
768 #endif
769 data_ptr = xmalloc (data_size);
770 #if TARGET_API_MAC_CARBON
771 err = AEGetDescData (from_desc, data_ptr, data_size);
772 #else
773 memcpy (data_ptr, *(from_desc->dataHandle), data_size);
774 #endif
775 if (err == noErr)
776 err = mac_coerce_file_name_ptr (from_type, data_ptr,
777 data_size, to_type,
778 handler_refcon, result);
779 xfree (data_ptr);
782 if (err != noErr)
783 return errAECoercionFail;
784 return noErr;
787 OSErr
788 init_coercion_handler ()
790 OSErr err;
792 static AECoercePtrUPP coerce_file_name_ptrUPP = NULL;
793 static AECoerceDescUPP coerce_file_name_descUPP = NULL;
795 if (coerce_file_name_ptrUPP == NULL)
797 coerce_file_name_ptrUPP = NewAECoercePtrUPP (mac_coerce_file_name_ptr);
798 coerce_file_name_descUPP = NewAECoerceDescUPP (mac_coerce_file_name_desc);
801 err = AEInstallCoercionHandler (TYPE_FILE_NAME, typeWildCard,
802 (AECoercionHandlerUPP)
803 coerce_file_name_ptrUPP, 0, false, false);
804 if (err == noErr)
805 err = AEInstallCoercionHandler (typeWildCard, TYPE_FILE_NAME,
806 (AECoercionHandlerUPP)
807 coerce_file_name_ptrUPP, 0, false, false);
808 if (err == noErr)
809 err = AEInstallCoercionHandler (TYPE_FILE_NAME, typeWildCard,
810 coerce_file_name_descUPP, 0, true, false);
811 if (err == noErr)
812 err = AEInstallCoercionHandler (typeWildCard, TYPE_FILE_NAME,
813 coerce_file_name_descUPP, 0, true, false);
814 return err;
817 #if TARGET_API_MAC_CARBON
818 static OSErr
819 create_apple_event (class, id, result)
820 AEEventClass class;
821 AEEventID id;
822 AppleEvent *result;
824 OSErr err;
825 static const ProcessSerialNumber psn = {0, kCurrentProcess};
826 AEAddressDesc address_desc;
828 err = AECreateDesc (typeProcessSerialNumber, &psn,
829 sizeof (ProcessSerialNumber), &address_desc);
830 if (err == noErr)
832 err = AECreateAppleEvent (class, id,
833 &address_desc, /* NULL is not allowed
834 on Mac OS Classic. */
835 kAutoGenerateReturnID,
836 kAnyTransactionID, result);
837 AEDisposeDesc (&address_desc);
840 return err;
843 OSStatus
844 create_apple_event_from_event_ref (event, num_params, names, types, result)
845 EventRef event;
846 UInt32 num_params;
847 const EventParamName *names;
848 const EventParamType *types;
849 AppleEvent *result;
851 OSStatus err;
852 UInt32 i, size;
853 CFStringRef string;
854 CFDataRef data;
855 char *buf = NULL;
857 err = create_apple_event (0, 0, result); /* Dummy class and ID. */
858 if (err != noErr)
859 return err;
861 for (i = 0; i < num_params; i++)
862 switch (types[i])
864 #ifdef MAC_OSX
865 case typeCFStringRef:
866 err = GetEventParameter (event, names[i], typeCFStringRef, NULL,
867 sizeof (CFStringRef), NULL, &string);
868 if (err != noErr)
869 break;
870 data = CFStringCreateExternalRepresentation (NULL, string,
871 kCFStringEncodingUTF8,
872 '?');
873 if (data == NULL)
874 break;
875 AEPutParamPtr (result, names[i], typeUTF8Text,
876 CFDataGetBytePtr (data), CFDataGetLength (data));
877 CFRelease (data);
878 break;
879 #endif
881 default:
882 err = GetEventParameter (event, names[i], types[i], NULL,
883 0, &size, NULL);
884 if (err != noErr)
885 break;
886 buf = xrealloc (buf, size);
887 err = GetEventParameter (event, names[i], types[i], NULL,
888 size, NULL, buf);
889 if (err == noErr)
890 AEPutParamPtr (result, names[i], types[i], buf, size);
891 break;
893 if (buf)
894 xfree (buf);
896 return noErr;
899 OSErr
900 create_apple_event_from_drag_ref (drag, num_types, types, result)
901 DragRef drag;
902 UInt32 num_types;
903 const FlavorType *types;
904 AppleEvent *result;
906 OSErr err;
907 UInt16 num_items;
908 AppleEvent items;
909 long index;
910 char *buf = NULL;
912 err = CountDragItems (drag, &num_items);
913 if (err != noErr)
914 return err;
915 err = AECreateList (NULL, 0, false, &items);
916 if (err != noErr)
917 return err;
919 for (index = 1; index <= num_items; index++)
921 ItemReference item;
922 DescType desc_type = typeNull;
923 Size size;
925 err = GetDragItemReferenceNumber (drag, index, &item);
926 if (err == noErr)
928 int i;
930 for (i = 0; i < num_types; i++)
932 err = GetFlavorDataSize (drag, item, types[i], &size);
933 if (err == noErr)
935 buf = xrealloc (buf, size);
936 err = GetFlavorData (drag, item, types[i], buf, &size, 0);
938 if (err == noErr)
940 desc_type = types[i];
941 break;
945 err = AEPutPtr (&items, index, desc_type,
946 desc_type != typeNull ? buf : NULL,
947 desc_type != typeNull ? size : 0);
948 if (err != noErr)
949 break;
951 if (buf)
952 xfree (buf);
954 if (err == noErr)
956 err = create_apple_event (0, 0, result); /* Dummy class and ID. */
957 if (err == noErr)
958 err = AEPutParamDesc (result, keyDirectObject, &items);
959 if (err != noErr)
960 AEDisposeDesc (result);
963 AEDisposeDesc (&items);
965 return err;
967 #endif /* TARGET_API_MAC_CARBON */
969 /***********************************************************************
970 Conversion between Lisp and Core Foundation objects
971 ***********************************************************************/
973 #if TARGET_API_MAC_CARBON
974 static Lisp_Object Qstring, Qnumber, Qboolean, Qdate, Qdata;
975 static Lisp_Object Qarray, Qdictionary;
977 struct cfdict_context
979 Lisp_Object *result;
980 int with_tag, hash_bound;
983 /* C string to CFString. */
985 CFStringRef
986 cfstring_create_with_utf8_cstring (c_str)
987 const char *c_str;
989 CFStringRef str;
991 str = CFStringCreateWithCString (NULL, c_str, kCFStringEncodingUTF8);
992 if (str == NULL)
993 /* Failed to interpret as UTF 8. Fall back on Mac Roman. */
994 str = CFStringCreateWithCString (NULL, c_str, kCFStringEncodingMacRoman);
996 return str;
1000 /* Lisp string to CFString. */
1002 CFStringRef
1003 cfstring_create_with_string (s)
1004 Lisp_Object s;
1006 CFStringRef string = NULL;
1008 if (STRING_MULTIBYTE (s))
1010 char *p, *end = SDATA (s) + SBYTES (s);
1012 for (p = SDATA (s); p < end; p++)
1013 if (!isascii (*p))
1015 s = ENCODE_UTF_8 (s);
1016 break;
1018 string = CFStringCreateWithBytes (NULL, SDATA (s), SBYTES (s),
1019 kCFStringEncodingUTF8, false);
1022 if (string == NULL)
1023 /* Failed to interpret as UTF 8. Fall back on Mac Roman. */
1024 string = CFStringCreateWithBytes (NULL, SDATA (s), SBYTES (s),
1025 kCFStringEncodingMacRoman, false);
1027 return string;
1031 /* From CFData to a lisp string. Always returns a unibyte string. */
1033 Lisp_Object
1034 cfdata_to_lisp (data)
1035 CFDataRef data;
1037 CFIndex len = CFDataGetLength (data);
1038 Lisp_Object result = make_uninit_string (len);
1040 CFDataGetBytes (data, CFRangeMake (0, len), SDATA (result));
1042 return result;
1046 /* From CFString to a lisp string. Returns a unibyte string
1047 containing a UTF-8 byte sequence. */
1049 Lisp_Object
1050 cfstring_to_lisp_nodecode (string)
1051 CFStringRef string;
1053 Lisp_Object result = Qnil;
1054 const char *s = CFStringGetCStringPtr (string, kCFStringEncodingUTF8);
1056 if (s)
1057 result = make_unibyte_string (s, strlen (s));
1058 else
1060 CFDataRef data =
1061 CFStringCreateExternalRepresentation (NULL, string,
1062 kCFStringEncodingUTF8, '?');
1064 if (data)
1066 result = cfdata_to_lisp (data);
1067 CFRelease (data);
1071 return result;
1075 /* From CFString to a lisp string. Never returns a unibyte string
1076 (even if it only contains ASCII characters).
1077 This may cause GC during code conversion. */
1079 Lisp_Object
1080 cfstring_to_lisp (string)
1081 CFStringRef string;
1083 Lisp_Object result = cfstring_to_lisp_nodecode (string);
1085 if (!NILP (result))
1087 result = code_convert_string_norecord (result, Qutf_8, 0);
1088 /* This may be superfluous. Just to make sure that the result
1089 is a multibyte string. */
1090 result = string_to_multibyte (result);
1093 return result;
1097 /* CFNumber to a lisp integer or a lisp float. */
1099 Lisp_Object
1100 cfnumber_to_lisp (number)
1101 CFNumberRef number;
1103 Lisp_Object result = Qnil;
1104 #if BITS_PER_EMACS_INT > 32
1105 SInt64 int_val;
1106 CFNumberType emacs_int_type = kCFNumberSInt64Type;
1107 #else
1108 SInt32 int_val;
1109 CFNumberType emacs_int_type = kCFNumberSInt32Type;
1110 #endif
1111 double float_val;
1113 if (CFNumberGetValue (number, emacs_int_type, &int_val)
1114 && !FIXNUM_OVERFLOW_P (int_val))
1115 result = make_number (int_val);
1116 else
1117 if (CFNumberGetValue (number, kCFNumberDoubleType, &float_val))
1118 result = make_float (float_val);
1119 return result;
1123 /* CFDate to a list of three integers as in a return value of
1124 `current-time'. */
1126 Lisp_Object
1127 cfdate_to_lisp (date)
1128 CFDateRef date;
1130 static const CFGregorianDate epoch_gdate = {1970, 1, 1, 0, 0, 0.0};
1131 static CFAbsoluteTime epoch = 0.0, sec;
1132 int high, low;
1134 if (epoch == 0.0)
1135 epoch = CFGregorianDateGetAbsoluteTime (epoch_gdate, NULL);
1137 sec = CFDateGetAbsoluteTime (date) - epoch;
1138 high = sec / 65536.0;
1139 low = sec - high * 65536.0;
1141 return list3 (make_number (high), make_number (low), make_number (0));
1145 /* CFBoolean to a lisp symbol, `t' or `nil'. */
1147 Lisp_Object
1148 cfboolean_to_lisp (boolean)
1149 CFBooleanRef boolean;
1151 return CFBooleanGetValue (boolean) ? Qt : Qnil;
1155 /* Any Core Foundation object to a (lengthy) lisp string. */
1157 Lisp_Object
1158 cfobject_desc_to_lisp (object)
1159 CFTypeRef object;
1161 Lisp_Object result = Qnil;
1162 CFStringRef desc = CFCopyDescription (object);
1164 if (desc)
1166 result = cfstring_to_lisp (desc);
1167 CFRelease (desc);
1170 return result;
1174 /* Callback functions for cfproperty_list_to_lisp. */
1176 static void
1177 cfdictionary_add_to_list (key, value, context)
1178 const void *key;
1179 const void *value;
1180 void *context;
1182 struct cfdict_context *cxt = (struct cfdict_context *)context;
1184 *cxt->result =
1185 Fcons (Fcons (cfstring_to_lisp (key),
1186 cfproperty_list_to_lisp (value, cxt->with_tag,
1187 cxt->hash_bound)),
1188 *cxt->result);
1191 static void
1192 cfdictionary_puthash (key, value, context)
1193 const void *key;
1194 const void *value;
1195 void *context;
1197 Lisp_Object lisp_key = cfstring_to_lisp (key);
1198 struct cfdict_context *cxt = (struct cfdict_context *)context;
1199 struct Lisp_Hash_Table *h = XHASH_TABLE (*(cxt->result));
1200 unsigned hash_code;
1202 hash_lookup (h, lisp_key, &hash_code);
1203 hash_put (h, lisp_key,
1204 cfproperty_list_to_lisp (value, cxt->with_tag, cxt->hash_bound),
1205 hash_code);
1209 /* Convert CFPropertyList PLIST to a lisp object. If WITH_TAG is
1210 non-zero, a symbol that represents the type of the original Core
1211 Foundation object is prepended. HASH_BOUND specifies which kinds
1212 of the lisp objects, alists or hash tables, are used as the targets
1213 of the conversion from CFDictionary. If HASH_BOUND is negative,
1214 always generate alists. If HASH_BOUND >= 0, generate an alist if
1215 the number of keys in the dictionary is smaller than HASH_BOUND,
1216 and a hash table otherwise. */
1218 Lisp_Object
1219 cfproperty_list_to_lisp (plist, with_tag, hash_bound)
1220 CFPropertyListRef plist;
1221 int with_tag, hash_bound;
1223 CFTypeID type_id = CFGetTypeID (plist);
1224 Lisp_Object tag = Qnil, result = Qnil;
1225 struct gcpro gcpro1, gcpro2;
1227 GCPRO2 (tag, result);
1229 if (type_id == CFStringGetTypeID ())
1231 tag = Qstring;
1232 result = cfstring_to_lisp (plist);
1234 else if (type_id == CFNumberGetTypeID ())
1236 tag = Qnumber;
1237 result = cfnumber_to_lisp (plist);
1239 else if (type_id == CFBooleanGetTypeID ())
1241 tag = Qboolean;
1242 result = cfboolean_to_lisp (plist);
1244 else if (type_id == CFDateGetTypeID ())
1246 tag = Qdate;
1247 result = cfdate_to_lisp (plist);
1249 else if (type_id == CFDataGetTypeID ())
1251 tag = Qdata;
1252 result = cfdata_to_lisp (plist);
1254 else if (type_id == CFArrayGetTypeID ())
1256 CFIndex index, count = CFArrayGetCount (plist);
1258 tag = Qarray;
1259 result = Fmake_vector (make_number (count), Qnil);
1260 for (index = 0; index < count; index++)
1261 XVECTOR (result)->contents[index] =
1262 cfproperty_list_to_lisp (CFArrayGetValueAtIndex (plist, index),
1263 with_tag, hash_bound);
1265 else if (type_id == CFDictionaryGetTypeID ())
1267 struct cfdict_context context;
1268 CFIndex count = CFDictionaryGetCount (plist);
1270 tag = Qdictionary;
1271 context.result = &result;
1272 context.with_tag = with_tag;
1273 context.hash_bound = hash_bound;
1274 if (hash_bound < 0 || count < hash_bound)
1276 result = Qnil;
1277 CFDictionaryApplyFunction (plist, cfdictionary_add_to_list,
1278 &context);
1280 else
1282 result = make_hash_table (Qequal,
1283 make_number (count),
1284 make_float (DEFAULT_REHASH_SIZE),
1285 make_float (DEFAULT_REHASH_THRESHOLD),
1286 Qnil, Qnil, Qnil);
1287 CFDictionaryApplyFunction (plist, cfdictionary_puthash,
1288 &context);
1291 else
1292 abort ();
1294 UNGCPRO;
1296 if (with_tag)
1297 result = Fcons (tag, result);
1299 return result;
1301 #endif
1304 /***********************************************************************
1305 Emulation of the X Resource Manager
1306 ***********************************************************************/
1308 /* Parser functions for resource lines. Each function takes an
1309 address of a variable whose value points to the head of a string.
1310 The value will be advanced so that it points to the next character
1311 of the parsed part when the function returns.
1313 A resource name such as "Emacs*font" is parsed into a non-empty
1314 list called `quarks'. Each element is either a Lisp string that
1315 represents a concrete component, a Lisp symbol LOOSE_BINDING
1316 (actually Qlambda) that represents any number (>=0) of intervening
1317 components, or a Lisp symbol SINGLE_COMPONENT (actually Qquote)
1318 that represents as any single component. */
1320 #define P (*p)
1322 #define LOOSE_BINDING Qlambda /* '*' ("L"oose) */
1323 #define SINGLE_COMPONENT Qquote /* '?' ("Q"uestion) */
1325 static void
1326 skip_white_space (p)
1327 const char **p;
1329 /* WhiteSpace = {<space> | <horizontal tab>} */
1330 while (*P == ' ' || *P == '\t')
1331 P++;
1334 static int
1335 parse_comment (p)
1336 const char **p;
1338 /* Comment = "!" {<any character except null or newline>} */
1339 if (*P == '!')
1341 P++;
1342 while (*P)
1343 if (*P++ == '\n')
1344 break;
1345 return 1;
1347 else
1348 return 0;
1351 /* Don't interpret filename. Just skip until the newline. */
1352 static int
1353 parse_include_file (p)
1354 const char **p;
1356 /* IncludeFile = "#" WhiteSpace "include" WhiteSpace FileName WhiteSpace */
1357 if (*P == '#')
1359 P++;
1360 while (*P)
1361 if (*P++ == '\n')
1362 break;
1363 return 1;
1365 else
1366 return 0;
1369 static char
1370 parse_binding (p)
1371 const char **p;
1373 /* Binding = "." | "*" */
1374 if (*P == '.' || *P == '*')
1376 char binding = *P++;
1378 while (*P == '.' || *P == '*')
1379 if (*P++ == '*')
1380 binding = '*';
1381 return binding;
1383 else
1384 return '\0';
1387 static Lisp_Object
1388 parse_component (p)
1389 const char **p;
1391 /* Component = "?" | ComponentName
1392 ComponentName = NameChar {NameChar}
1393 NameChar = "a"-"z" | "A"-"Z" | "0"-"9" | "_" | "-" */
1394 if (*P == '?')
1396 P++;
1397 return SINGLE_COMPONENT;
1399 else if (isalnum (*P) || *P == '_' || *P == '-')
1401 const char *start = P++;
1403 while (isalnum (*P) || *P == '_' || *P == '-')
1404 P++;
1406 return make_unibyte_string (start, P - start);
1408 else
1409 return Qnil;
1412 static Lisp_Object
1413 parse_resource_name (p)
1414 const char **p;
1416 Lisp_Object result = Qnil, component;
1417 char binding;
1419 /* ResourceName = [Binding] {Component Binding} ComponentName */
1420 if (parse_binding (p) == '*')
1421 result = Fcons (LOOSE_BINDING, result);
1423 component = parse_component (p);
1424 if (NILP (component))
1425 return Qnil;
1427 result = Fcons (component, result);
1428 while ((binding = parse_binding (p)) != '\0')
1430 if (binding == '*')
1431 result = Fcons (LOOSE_BINDING, result);
1432 component = parse_component (p);
1433 if (NILP (component))
1434 return Qnil;
1435 else
1436 result = Fcons (component, result);
1439 /* The final component should not be '?'. */
1440 if (EQ (component, SINGLE_COMPONENT))
1441 return Qnil;
1443 return Fnreverse (result);
1446 static Lisp_Object
1447 parse_value (p)
1448 const char **p;
1450 char *q, *buf;
1451 Lisp_Object seq = Qnil, result;
1452 int buf_len, total_len = 0, len, continue_p;
1454 q = strchr (P, '\n');
1455 buf_len = q ? q - P : strlen (P);
1456 buf = xmalloc (buf_len);
1458 while (1)
1460 q = buf;
1461 continue_p = 0;
1462 while (*P)
1464 if (*P == '\n')
1466 P++;
1467 break;
1469 else if (*P == '\\')
1471 P++;
1472 if (*P == '\0')
1473 break;
1474 else if (*P == '\n')
1476 P++;
1477 continue_p = 1;
1478 break;
1480 else if (*P == 'n')
1482 *q++ = '\n';
1483 P++;
1485 else if ('0' <= P[0] && P[0] <= '7'
1486 && '0' <= P[1] && P[1] <= '7'
1487 && '0' <= P[2] && P[2] <= '7')
1489 *q++ = ((P[0] - '0') << 6) + ((P[1] - '0') << 3) + (P[2] - '0');
1490 P += 3;
1492 else
1493 *q++ = *P++;
1495 else
1496 *q++ = *P++;
1498 len = q - buf;
1499 seq = Fcons (make_unibyte_string (buf, len), seq);
1500 total_len += len;
1502 if (continue_p)
1504 q = strchr (P, '\n');
1505 len = q ? q - P : strlen (P);
1506 if (len > buf_len)
1508 xfree (buf);
1509 buf_len = len;
1510 buf = xmalloc (buf_len);
1513 else
1514 break;
1516 xfree (buf);
1518 if (SBYTES (XCAR (seq)) == total_len)
1519 return make_string (SDATA (XCAR (seq)), total_len);
1520 else
1522 buf = xmalloc (total_len);
1523 q = buf + total_len;
1524 for (; CONSP (seq); seq = XCDR (seq))
1526 len = SBYTES (XCAR (seq));
1527 q -= len;
1528 memcpy (q, SDATA (XCAR (seq)), len);
1530 result = make_string (buf, total_len);
1531 xfree (buf);
1532 return result;
1536 static Lisp_Object
1537 parse_resource_line (p)
1538 const char **p;
1540 Lisp_Object quarks, value;
1542 /* ResourceLine = Comment | IncludeFile | ResourceSpec | <empty line> */
1543 if (parse_comment (p) || parse_include_file (p))
1544 return Qnil;
1546 /* ResourceSpec = WhiteSpace ResourceName WhiteSpace ":" WhiteSpace Value */
1547 skip_white_space (p);
1548 quarks = parse_resource_name (p);
1549 if (NILP (quarks))
1550 goto cleanup;
1551 skip_white_space (p);
1552 if (*P != ':')
1553 goto cleanup;
1554 P++;
1555 skip_white_space (p);
1556 value = parse_value (p);
1557 return Fcons (quarks, value);
1559 cleanup:
1560 /* Skip the remaining data as a dummy value. */
1561 parse_value (p);
1562 return Qnil;
1565 #undef P
1567 /* Equivalents of X Resource Manager functions.
1569 An X Resource Database acts as a collection of resource names and
1570 associated values. It is implemented as a trie on quarks. Namely,
1571 each edge is labeled by either a string, LOOSE_BINDING, or
1572 SINGLE_COMPONENT. Each node has a node id, which is a unique
1573 nonnegative integer, and the root node id is 0. A database is
1574 implemented as a hash table that maps a pair (SRC-NODE-ID .
1575 EDGE-LABEL) to DEST-NODE-ID. It also holds a maximum node id used
1576 in the table as a value for HASHKEY_MAX_NID. A value associated to
1577 a node is recorded as a value for the node id.
1579 A database also has a cache for past queries as a value for
1580 HASHKEY_QUERY_CACHE. It is another hash table that maps
1581 "NAME-STRING\0CLASS-STRING" to the result of the query. */
1583 #define HASHKEY_MAX_NID (make_number (0))
1584 #define HASHKEY_QUERY_CACHE (make_number (-1))
1586 static XrmDatabase
1587 xrm_create_database ()
1589 XrmDatabase database;
1591 database = make_hash_table (Qequal, make_number (DEFAULT_HASH_SIZE),
1592 make_float (DEFAULT_REHASH_SIZE),
1593 make_float (DEFAULT_REHASH_THRESHOLD),
1594 Qnil, Qnil, Qnil);
1595 Fputhash (HASHKEY_MAX_NID, make_number (0), database);
1596 Fputhash (HASHKEY_QUERY_CACHE, Qnil, database);
1598 return database;
1601 static void
1602 xrm_q_put_resource (database, quarks, value)
1603 XrmDatabase database;
1604 Lisp_Object quarks, value;
1606 struct Lisp_Hash_Table *h = XHASH_TABLE (database);
1607 unsigned hash_code;
1608 int max_nid, i;
1609 Lisp_Object node_id, key;
1611 max_nid = XINT (Fgethash (HASHKEY_MAX_NID, database, Qnil));
1613 XSETINT (node_id, 0);
1614 for (; CONSP (quarks); quarks = XCDR (quarks))
1616 key = Fcons (node_id, XCAR (quarks));
1617 i = hash_lookup (h, key, &hash_code);
1618 if (i < 0)
1620 max_nid++;
1621 XSETINT (node_id, max_nid);
1622 hash_put (h, key, node_id, hash_code);
1624 else
1625 node_id = HASH_VALUE (h, i);
1627 Fputhash (node_id, value, database);
1629 Fputhash (HASHKEY_MAX_NID, make_number (max_nid), database);
1630 Fputhash (HASHKEY_QUERY_CACHE, Qnil, database);
1633 /* Merge multiple resource entries specified by DATA into a resource
1634 database DATABASE. DATA points to the head of a null-terminated
1635 string consisting of multiple resource lines. It's like a
1636 combination of XrmGetStringDatabase and XrmMergeDatabases. */
1638 void
1639 xrm_merge_string_database (database, data)
1640 XrmDatabase database;
1641 const char *data;
1643 Lisp_Object quarks_value;
1645 while (*data)
1647 quarks_value = parse_resource_line (&data);
1648 if (!NILP (quarks_value))
1649 xrm_q_put_resource (database,
1650 XCAR (quarks_value), XCDR (quarks_value));
1654 static Lisp_Object
1655 xrm_q_get_resource_1 (database, node_id, quark_name, quark_class)
1656 XrmDatabase database;
1657 Lisp_Object node_id, quark_name, quark_class;
1659 struct Lisp_Hash_Table *h = XHASH_TABLE (database);
1660 Lisp_Object key, labels[3], value;
1661 int i, k;
1663 if (!CONSP (quark_name))
1664 return Fgethash (node_id, database, Qnil);
1666 /* First, try tight bindings */
1667 labels[0] = XCAR (quark_name);
1668 labels[1] = XCAR (quark_class);
1669 labels[2] = SINGLE_COMPONENT;
1671 key = Fcons (node_id, Qnil);
1672 for (k = 0; k < sizeof (labels) / sizeof (*labels); k++)
1674 XSETCDR (key, labels[k]);
1675 i = hash_lookup (h, key, NULL);
1676 if (i >= 0)
1678 value = xrm_q_get_resource_1 (database, HASH_VALUE (h, i),
1679 XCDR (quark_name), XCDR (quark_class));
1680 if (!NILP (value))
1681 return value;
1685 /* Then, try loose bindings */
1686 XSETCDR (key, LOOSE_BINDING);
1687 i = hash_lookup (h, key, NULL);
1688 if (i >= 0)
1690 value = xrm_q_get_resource_1 (database, HASH_VALUE (h, i),
1691 quark_name, quark_class);
1692 if (!NILP (value))
1693 return value;
1694 else
1695 return xrm_q_get_resource_1 (database, node_id,
1696 XCDR (quark_name), XCDR (quark_class));
1698 else
1699 return Qnil;
1702 static Lisp_Object
1703 xrm_q_get_resource (database, quark_name, quark_class)
1704 XrmDatabase database;
1705 Lisp_Object quark_name, quark_class;
1707 return xrm_q_get_resource_1 (database, make_number (0),
1708 quark_name, quark_class);
1711 /* Retrieve a resource value for the specified NAME and CLASS from the
1712 resource database DATABASE. It corresponds to XrmGetResource. */
1714 Lisp_Object
1715 xrm_get_resource (database, name, class)
1716 XrmDatabase database;
1717 const char *name, *class;
1719 Lisp_Object key, query_cache, quark_name, quark_class, tmp;
1720 int i, nn, nc;
1721 struct Lisp_Hash_Table *h;
1722 unsigned hash_code;
1724 nn = strlen (name);
1725 nc = strlen (class);
1726 key = make_uninit_string (nn + nc + 1);
1727 strcpy (SDATA (key), name);
1728 strncpy (SDATA (key) + nn + 1, class, nc);
1730 query_cache = Fgethash (HASHKEY_QUERY_CACHE, database, Qnil);
1731 if (NILP (query_cache))
1733 query_cache = make_hash_table (Qequal, make_number (DEFAULT_HASH_SIZE),
1734 make_float (DEFAULT_REHASH_SIZE),
1735 make_float (DEFAULT_REHASH_THRESHOLD),
1736 Qnil, Qnil, Qnil);
1737 Fputhash (HASHKEY_QUERY_CACHE, query_cache, database);
1739 h = XHASH_TABLE (query_cache);
1740 i = hash_lookup (h, key, &hash_code);
1741 if (i >= 0)
1742 return HASH_VALUE (h, i);
1744 quark_name = parse_resource_name (&name);
1745 if (*name != '\0')
1746 return Qnil;
1747 for (tmp = quark_name, nn = 0; CONSP (tmp); tmp = XCDR (tmp), nn++)
1748 if (!STRINGP (XCAR (tmp)))
1749 return Qnil;
1751 quark_class = parse_resource_name (&class);
1752 if (*class != '\0')
1753 return Qnil;
1754 for (tmp = quark_class, nc = 0; CONSP (tmp); tmp = XCDR (tmp), nc++)
1755 if (!STRINGP (XCAR (tmp)))
1756 return Qnil;
1758 if (nn != nc)
1759 return Qnil;
1760 else
1762 tmp = xrm_q_get_resource (database, quark_name, quark_class);
1763 hash_put (h, key, tmp, hash_code);
1764 return tmp;
1768 #if TARGET_API_MAC_CARBON
1769 static Lisp_Object
1770 xrm_cfproperty_list_to_value (plist)
1771 CFPropertyListRef plist;
1773 CFTypeID type_id = CFGetTypeID (plist);
1775 if (type_id == CFStringGetTypeID ())
1776 return cfstring_to_lisp (plist);
1777 else if (type_id == CFNumberGetTypeID ())
1779 CFStringRef string;
1780 Lisp_Object result = Qnil;
1782 string = CFStringCreateWithFormat (NULL, NULL, CFSTR ("%@"), plist);
1783 if (string)
1785 result = cfstring_to_lisp (string);
1786 CFRelease (string);
1788 return result;
1790 else if (type_id == CFBooleanGetTypeID ())
1791 return build_string (CFBooleanGetValue (plist) ? "true" : "false");
1792 else if (type_id == CFDataGetTypeID ())
1793 return cfdata_to_lisp (plist);
1794 else
1795 return Qnil;
1797 #endif
1799 /* Create a new resource database from the preferences for the
1800 application APPLICATION. APPLICATION is either a string that
1801 specifies an application ID, or NULL that represents the current
1802 application. */
1804 XrmDatabase
1805 xrm_get_preference_database (application)
1806 const char *application;
1808 #if TARGET_API_MAC_CARBON
1809 CFStringRef app_id, *keys, user_doms[2], host_doms[2];
1810 CFMutableSetRef key_set = NULL;
1811 CFArrayRef key_array;
1812 CFIndex index, count;
1813 char *res_name;
1814 XrmDatabase database;
1815 Lisp_Object quarks = Qnil, value = Qnil;
1816 CFPropertyListRef plist;
1817 int iu, ih;
1818 struct gcpro gcpro1, gcpro2, gcpro3;
1820 user_doms[0] = kCFPreferencesCurrentUser;
1821 user_doms[1] = kCFPreferencesAnyUser;
1822 host_doms[0] = kCFPreferencesCurrentHost;
1823 host_doms[1] = kCFPreferencesAnyHost;
1825 database = xrm_create_database ();
1827 GCPRO3 (database, quarks, value);
1829 app_id = kCFPreferencesCurrentApplication;
1830 if (application)
1832 app_id = cfstring_create_with_utf8_cstring (application);
1833 if (app_id == NULL)
1834 goto out;
1836 if (!CFPreferencesAppSynchronize (app_id))
1837 goto out;
1839 key_set = CFSetCreateMutable (NULL, 0, &kCFCopyStringSetCallBacks);
1840 if (key_set == NULL)
1841 goto out;
1842 for (iu = 0; iu < sizeof (user_doms) / sizeof (*user_doms) ; iu++)
1843 for (ih = 0; ih < sizeof (host_doms) / sizeof (*host_doms); ih++)
1845 key_array = CFPreferencesCopyKeyList (app_id, user_doms[iu],
1846 host_doms[ih]);
1847 if (key_array)
1849 count = CFArrayGetCount (key_array);
1850 for (index = 0; index < count; index++)
1851 CFSetAddValue (key_set,
1852 CFArrayGetValueAtIndex (key_array, index));
1853 CFRelease (key_array);
1857 count = CFSetGetCount (key_set);
1858 keys = xmalloc (sizeof (CFStringRef) * count);
1859 CFSetGetValues (key_set, (const void **)keys);
1860 for (index = 0; index < count; index++)
1862 res_name = SDATA (cfstring_to_lisp_nodecode (keys[index]));
1863 quarks = parse_resource_name (&res_name);
1864 if (!(NILP (quarks) || *res_name))
1866 plist = CFPreferencesCopyAppValue (keys[index], app_id);
1867 value = xrm_cfproperty_list_to_value (plist);
1868 CFRelease (plist);
1869 if (!NILP (value))
1870 xrm_q_put_resource (database, quarks, value);
1874 xfree (keys);
1875 out:
1876 if (key_set)
1877 CFRelease (key_set);
1878 CFRelease (app_id);
1880 UNGCPRO;
1882 return database;
1883 #else
1884 return xrm_create_database ();
1885 #endif
1889 #ifndef MAC_OSX
1891 /* The following functions with "sys_" prefix are stubs to Unix
1892 functions that have already been implemented by CW or MPW. The
1893 calls to them in Emacs source course are #define'd to call the sys_
1894 versions by the header files s-mac.h. In these stubs pathnames are
1895 converted between their Unix and Mac forms. */
1898 /* Unix epoch is Jan 1, 1970 while Mac epoch is Jan 1, 1904: 66 years
1899 + 17 leap days. These are for adjusting time values returned by
1900 MacOS Toolbox functions. */
1902 #define MAC_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
1904 #ifdef __MWERKS__
1905 #if __MSL__ < 0x6000
1906 /* CW Pro 5 epoch is Jan 1, 1900 (aaarghhhhh!); remember, 1900 is not
1907 a leap year! This is for adjusting time_t values returned by MSL
1908 functions. */
1909 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 70 + 17) * 24 * 60 * 60)
1910 #else /* __MSL__ >= 0x6000 */
1911 /* CW changes Pro 6 to follow Unix! */
1912 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
1913 #endif /* __MSL__ >= 0x6000 */
1914 #elif __MRC__
1915 /* MPW library functions follow Unix (confused?). */
1916 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
1917 #else /* not __MRC__ */
1918 You lose!!!
1919 #endif /* not __MRC__ */
1922 /* Define our own stat function for both MrC and CW. The reason for
1923 doing this: "stat" is both the name of a struct and function name:
1924 can't use the same trick like that for sys_open, sys_close, etc. to
1925 redirect Emacs's calls to our own version that converts Unix style
1926 filenames to Mac style filename because all sorts of compilation
1927 errors will be generated if stat is #define'd to be sys_stat. */
1930 stat_noalias (const char *path, struct stat *buf)
1932 char mac_pathname[MAXPATHLEN+1];
1933 CInfoPBRec cipb;
1935 if (posix_to_mac_pathname (path, mac_pathname, MAXPATHLEN+1) == 0)
1936 return -1;
1938 c2pstr (mac_pathname);
1939 cipb.hFileInfo.ioNamePtr = mac_pathname;
1940 cipb.hFileInfo.ioVRefNum = 0;
1941 cipb.hFileInfo.ioDirID = 0;
1942 cipb.hFileInfo.ioFDirIndex = 0;
1943 /* set to 0 to get information about specific dir or file */
1945 errno = PBGetCatInfo (&cipb, false);
1946 if (errno == -43) /* -43: fnfErr defined in Errors.h */
1947 errno = ENOENT;
1948 if (errno != noErr)
1949 return -1;
1951 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* bit 4 = 1 for directories */
1953 buf->st_mode = S_IFDIR | S_IREAD | S_IEXEC;
1955 if (!(cipb.hFileInfo.ioFlAttrib & 0x1))
1956 buf->st_mode |= S_IWRITE; /* bit 1 = 1 for locked files/directories */
1957 buf->st_ino = cipb.dirInfo.ioDrDirID;
1958 buf->st_dev = cipb.dirInfo.ioVRefNum;
1959 buf->st_size = cipb.dirInfo.ioDrNmFls;
1960 /* size of dir = number of files and dirs */
1961 buf->st_atime
1962 = buf->st_mtime
1963 = cipb.dirInfo.ioDrMdDat - MAC_UNIX_EPOCH_DIFF;
1964 buf->st_ctime = cipb.dirInfo.ioDrCrDat - MAC_UNIX_EPOCH_DIFF;
1966 else
1968 buf->st_mode = S_IFREG | S_IREAD;
1969 if (!(cipb.hFileInfo.ioFlAttrib & 0x1))
1970 buf->st_mode |= S_IWRITE; /* bit 1 = 1 for locked files/directories */
1971 if (cipb.hFileInfo.ioFlFndrInfo.fdType == 'APPL')
1972 buf->st_mode |= S_IEXEC;
1973 buf->st_ino = cipb.hFileInfo.ioDirID;
1974 buf->st_dev = cipb.hFileInfo.ioVRefNum;
1975 buf->st_size = cipb.hFileInfo.ioFlLgLen;
1976 buf->st_atime
1977 = buf->st_mtime
1978 = cipb.hFileInfo.ioFlMdDat - MAC_UNIX_EPOCH_DIFF;
1979 buf->st_ctime = cipb.hFileInfo.ioFlCrDat - MAC_UNIX_EPOCH_DIFF;
1982 if (cipb.hFileInfo.ioFlFndrInfo.fdFlags & 0x8000)
1984 /* identify alias files as symlinks */
1985 buf->st_mode &= ~S_IFREG;
1986 buf->st_mode |= S_IFLNK;
1989 buf->st_nlink = 1;
1990 buf->st_uid = getuid ();
1991 buf->st_gid = getgid ();
1992 buf->st_rdev = 0;
1994 return 0;
1999 lstat (const char *path, struct stat *buf)
2001 int result;
2002 char true_pathname[MAXPATHLEN+1];
2004 /* Try looking for the file without resolving aliases first. */
2005 if ((result = stat_noalias (path, buf)) >= 0)
2006 return result;
2008 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
2009 return -1;
2011 return stat_noalias (true_pathname, buf);
2016 stat (const char *path, struct stat *sb)
2018 int result;
2019 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
2020 int len;
2022 if ((result = stat_noalias (path, sb)) >= 0 &&
2023 ! (sb->st_mode & S_IFLNK))
2024 return result;
2026 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
2027 return -1;
2029 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
2030 if (len > -1)
2032 fully_resolved_name[len] = '\0';
2033 /* in fact our readlink terminates strings */
2034 return lstat (fully_resolved_name, sb);
2036 else
2037 return lstat (true_pathname, sb);
2041 #if __MRC__
2042 /* CW defines fstat in stat.mac.c while MPW does not provide this
2043 function. Without the information of how to get from a file
2044 descriptor in MPW StdCLib to a Mac OS file spec, it should be hard
2045 to implement this function. Fortunately, there is only one place
2046 where this function is called in our configuration: in fileio.c,
2047 where only the st_dev and st_ino fields are used to determine
2048 whether two fildes point to different i-nodes to prevent copying
2049 a file onto itself equal. What we have here probably needs
2050 improvement. */
2053 fstat (int fildes, struct stat *buf)
2055 buf->st_dev = 0;
2056 buf->st_ino = fildes;
2057 buf->st_mode = S_IFREG; /* added by T.I. for the copy-file */
2058 return 0; /* success */
2060 #endif /* __MRC__ */
2064 mkdir (const char *dirname, int mode)
2066 #pragma unused(mode)
2068 HFileParam hfpb;
2069 char true_pathname[MAXPATHLEN+1], mac_pathname[MAXPATHLEN+1];
2071 if (find_true_pathname (dirname, true_pathname, MAXPATHLEN+1) == -1)
2072 return -1;
2074 if (posix_to_mac_pathname (true_pathname, mac_pathname, MAXPATHLEN+1) == 0)
2075 return -1;
2077 c2pstr (mac_pathname);
2078 hfpb.ioNamePtr = mac_pathname;
2079 hfpb.ioVRefNum = 0; /* ignored unless name is invalid */
2080 hfpb.ioDirID = 0; /* parent is the root */
2082 errno = PBDirCreate ((HParmBlkPtr) &hfpb, false);
2083 /* just return the Mac OSErr code for now */
2084 return errno == noErr ? 0 : -1;
2088 #undef rmdir
2089 sys_rmdir (const char *dirname)
2091 HFileParam hfpb;
2092 char mac_pathname[MAXPATHLEN+1];
2094 if (posix_to_mac_pathname (dirname, mac_pathname, MAXPATHLEN+1) == 0)
2095 return -1;
2097 c2pstr (mac_pathname);
2098 hfpb.ioNamePtr = mac_pathname;
2099 hfpb.ioVRefNum = 0; /* ignored unless name is invalid */
2100 hfpb.ioDirID = 0; /* parent is the root */
2102 errno = PBHDelete ((HParmBlkPtr) &hfpb, false);
2103 return errno == noErr ? 0 : -1;
2107 #ifdef __MRC__
2108 /* No implementation yet. */
2110 execvp (const char *path, ...)
2112 return -1;
2114 #endif /* __MRC__ */
2118 utime (const char *path, const struct utimbuf *times)
2120 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
2121 int len;
2122 char mac_pathname[MAXPATHLEN+1];
2123 CInfoPBRec cipb;
2125 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
2126 return -1;
2128 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
2129 if (len > -1)
2130 fully_resolved_name[len] = '\0';
2131 else
2132 strcpy (fully_resolved_name, true_pathname);
2134 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
2135 return -1;
2137 c2pstr (mac_pathname);
2138 cipb.hFileInfo.ioNamePtr = mac_pathname;
2139 cipb.hFileInfo.ioVRefNum = 0;
2140 cipb.hFileInfo.ioDirID = 0;
2141 cipb.hFileInfo.ioFDirIndex = 0;
2142 /* set to 0 to get information about specific dir or file */
2144 errno = PBGetCatInfo (&cipb, false);
2145 if (errno != noErr)
2146 return -1;
2148 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* bit 4 = 1 for directories */
2150 if (times)
2151 cipb.dirInfo.ioDrMdDat = times->modtime + MAC_UNIX_EPOCH_DIFF;
2152 else
2153 GetDateTime (&cipb.dirInfo.ioDrMdDat);
2155 else
2157 if (times)
2158 cipb.hFileInfo.ioFlMdDat = times->modtime + MAC_UNIX_EPOCH_DIFF;
2159 else
2160 GetDateTime (&cipb.hFileInfo.ioFlMdDat);
2163 errno = PBSetCatInfo (&cipb, false);
2164 return errno == noErr ? 0 : -1;
2168 #ifndef F_OK
2169 #define F_OK 0
2170 #endif
2171 #ifndef X_OK
2172 #define X_OK 1
2173 #endif
2174 #ifndef W_OK
2175 #define W_OK 2
2176 #endif
2178 /* Like stat, but test for access mode in hfpb.ioFlAttrib */
2180 access (const char *path, int mode)
2182 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
2183 int len;
2184 char mac_pathname[MAXPATHLEN+1];
2185 CInfoPBRec cipb;
2187 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
2188 return -1;
2190 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
2191 if (len > -1)
2192 fully_resolved_name[len] = '\0';
2193 else
2194 strcpy (fully_resolved_name, true_pathname);
2196 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
2197 return -1;
2199 c2pstr (mac_pathname);
2200 cipb.hFileInfo.ioNamePtr = mac_pathname;
2201 cipb.hFileInfo.ioVRefNum = 0;
2202 cipb.hFileInfo.ioDirID = 0;
2203 cipb.hFileInfo.ioFDirIndex = 0;
2204 /* set to 0 to get information about specific dir or file */
2206 errno = PBGetCatInfo (&cipb, false);
2207 if (errno != noErr)
2208 return -1;
2210 if (mode == F_OK) /* got this far, file exists */
2211 return 0;
2213 if (mode & X_OK)
2214 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* path refers to a directory */
2215 return 0;
2216 else
2218 if (cipb.hFileInfo.ioFlFndrInfo.fdType == 'APPL')
2219 return 0;
2220 else
2221 return -1;
2224 if (mode & W_OK)
2225 return (cipb.hFileInfo.ioFlAttrib & 0x1) ? -1 : 0;
2226 /* don't allow if lock bit is on */
2228 return -1;
2232 #define DEV_NULL_FD 0x10000
2234 #undef open
2236 sys_open (const char *path, int oflag)
2238 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
2239 int len;
2240 char mac_pathname[MAXPATHLEN+1];
2242 if (strcmp (path, "/dev/null") == 0)
2243 return DEV_NULL_FD; /* some bogus fd to be ignored in write */
2245 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
2246 return -1;
2248 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
2249 if (len > -1)
2250 fully_resolved_name[len] = '\0';
2251 else
2252 strcpy (fully_resolved_name, true_pathname);
2254 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
2255 return -1;
2256 else
2258 #ifdef __MRC__
2259 int res = open (mac_pathname, oflag);
2260 /* if (oflag == O_WRONLY || oflag == O_RDWR) */
2261 if (oflag & O_CREAT)
2262 fsetfileinfo (mac_pathname, MAC_EMACS_CREATOR_CODE, 'TEXT');
2263 return res;
2264 #else /* not __MRC__ */
2265 return open (mac_pathname, oflag);
2266 #endif /* not __MRC__ */
2271 #undef creat
2273 sys_creat (const char *path, mode_t mode)
2275 char true_pathname[MAXPATHLEN+1];
2276 int len;
2277 char mac_pathname[MAXPATHLEN+1];
2279 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
2280 return -1;
2282 if (!posix_to_mac_pathname (true_pathname, mac_pathname, MAXPATHLEN+1))
2283 return -1;
2284 else
2286 #ifdef __MRC__
2287 int result = creat (mac_pathname);
2288 fsetfileinfo (mac_pathname, MAC_EMACS_CREATOR_CODE, 'TEXT');
2289 return result;
2290 #else /* not __MRC__ */
2291 return creat (mac_pathname, mode);
2292 #endif /* not __MRC__ */
2297 #undef unlink
2299 sys_unlink (const char *path)
2301 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
2302 int len;
2303 char mac_pathname[MAXPATHLEN+1];
2305 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
2306 return -1;
2308 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
2309 if (len > -1)
2310 fully_resolved_name[len] = '\0';
2311 else
2312 strcpy (fully_resolved_name, true_pathname);
2314 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
2315 return -1;
2316 else
2317 return unlink (mac_pathname);
2321 #undef read
2323 sys_read (int fildes, char *buf, int count)
2325 if (fildes == 0) /* this should not be used for console input */
2326 return -1;
2327 else
2328 #if __MSL__ >= 0x6000
2329 return _read (fildes, buf, count);
2330 #else
2331 return read (fildes, buf, count);
2332 #endif
2336 #undef write
2338 sys_write (int fildes, const char *buf, int count)
2340 if (fildes == DEV_NULL_FD)
2341 return count;
2342 else
2343 #if __MSL__ >= 0x6000
2344 return _write (fildes, buf, count);
2345 #else
2346 return write (fildes, buf, count);
2347 #endif
2351 #undef rename
2353 sys_rename (const char * old_name, const char * new_name)
2355 char true_old_pathname[MAXPATHLEN+1], true_new_pathname[MAXPATHLEN+1];
2356 char fully_resolved_old_name[MAXPATHLEN+1];
2357 int len;
2358 char mac_old_name[MAXPATHLEN+1], mac_new_name[MAXPATHLEN+1];
2360 if (find_true_pathname (old_name, true_old_pathname, MAXPATHLEN+1) == -1)
2361 return -1;
2363 len = readlink (true_old_pathname, fully_resolved_old_name, MAXPATHLEN);
2364 if (len > -1)
2365 fully_resolved_old_name[len] = '\0';
2366 else
2367 strcpy (fully_resolved_old_name, true_old_pathname);
2369 if (find_true_pathname (new_name, true_new_pathname, MAXPATHLEN+1) == -1)
2370 return -1;
2372 if (strcmp (fully_resolved_old_name, true_new_pathname) == 0)
2373 return 0;
2375 if (!posix_to_mac_pathname (fully_resolved_old_name,
2376 mac_old_name,
2377 MAXPATHLEN+1))
2378 return -1;
2380 if (!posix_to_mac_pathname(true_new_pathname, mac_new_name, MAXPATHLEN+1))
2381 return -1;
2383 /* If a file with new_name already exists, rename deletes the old
2384 file in Unix. CW version fails in these situation. So we add a
2385 call to unlink here. */
2386 (void) unlink (mac_new_name);
2388 return rename (mac_old_name, mac_new_name);
2392 #undef fopen
2393 extern FILE *fopen (const char *name, const char *mode);
2394 FILE *
2395 sys_fopen (const char *name, const char *mode)
2397 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
2398 int len;
2399 char mac_pathname[MAXPATHLEN+1];
2401 if (find_true_pathname (name, true_pathname, MAXPATHLEN+1) == -1)
2402 return 0;
2404 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
2405 if (len > -1)
2406 fully_resolved_name[len] = '\0';
2407 else
2408 strcpy (fully_resolved_name, true_pathname);
2410 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
2411 return 0;
2412 else
2414 #ifdef __MRC__
2415 if (mode[0] == 'w' || mode[0] == 'a')
2416 fsetfileinfo (mac_pathname, MAC_EMACS_CREATOR_CODE, 'TEXT');
2417 #endif /* not __MRC__ */
2418 return fopen (mac_pathname, mode);
2423 extern Boolean mac_wait_next_event P_ ((EventRecord *, UInt32, Boolean));
2426 select (nfds, rfds, wfds, efds, timeout)
2427 int nfds;
2428 SELECT_TYPE *rfds, *wfds, *efds;
2429 EMACS_TIME *timeout;
2431 OSStatus err = noErr;
2433 /* Can only handle wait for keyboard input. */
2434 if (nfds > 1 || wfds || efds)
2435 return -1;
2437 /* Try detect_input_pending before ReceiveNextEvent in the same
2438 BLOCK_INPUT block, in case that some input has already been read
2439 asynchronously. */
2440 BLOCK_INPUT;
2441 ENABLE_WAKEUP_FROM_RNE;
2442 if (!detect_input_pending ())
2444 #if TARGET_API_MAC_CARBON
2445 EventTimeout timeoutval =
2446 (timeout
2447 ? (EMACS_SECS (*timeout) * kEventDurationSecond
2448 + EMACS_USECS (*timeout) * kEventDurationMicrosecond)
2449 : kEventDurationForever);
2451 if (timeoutval == 0.0)
2452 err = eventLoopTimedOutErr;
2453 else
2454 err = ReceiveNextEvent (0, NULL, timeoutval,
2455 kEventLeaveInQueue, NULL);
2456 #else /* not TARGET_API_MAC_CARBON */
2457 EventRecord e;
2458 UInt32 sleep_time = EMACS_SECS (*timeout) * 60 +
2459 ((EMACS_USECS (*timeout) * 60) / 1000000);
2461 if (sleep_time == 0)
2462 err = -9875; /* eventLoopTimedOutErr */
2463 else
2465 if (mac_wait_next_event (&e, sleep_time, false))
2466 err = noErr;
2467 else
2468 err = -9875; /* eventLoopTimedOutErr */
2470 #endif /* not TARGET_API_MAC_CARBON */
2472 DISABLE_WAKEUP_FROM_RNE;
2473 UNBLOCK_INPUT;
2475 if (err == noErr)
2477 /* Pretend that `select' is interrupted by a signal. */
2478 detect_input_pending ();
2479 errno = EINTR;
2480 return -1;
2482 else
2484 if (rfds)
2485 FD_ZERO (rfds);
2486 return 0;
2491 /* Simulation of SIGALRM. The stub for function signal stores the
2492 signal handler function in alarm_signal_func if a SIGALRM is
2493 encountered. */
2495 #include <signal.h>
2496 #include "syssignal.h"
2498 static TMTask mac_atimer_task;
2500 static QElemPtr mac_atimer_qlink = (QElemPtr) &mac_atimer_task;
2502 static int signal_mask = 0;
2504 #ifdef __MRC__
2505 __sigfun alarm_signal_func = (__sigfun) 0;
2506 #elif __MWERKS__
2507 __signal_func_ptr alarm_signal_func = (__signal_func_ptr) 0;
2508 #else /* not __MRC__ and not __MWERKS__ */
2509 You lose!!!
2510 #endif /* not __MRC__ and not __MWERKS__ */
2512 #undef signal
2513 #ifdef __MRC__
2514 extern __sigfun signal (int signal, __sigfun signal_func);
2515 __sigfun
2516 sys_signal (int signal_num, __sigfun signal_func)
2517 #elif __MWERKS__
2518 extern __signal_func_ptr signal (int signal, __signal_func_ptr signal_func);
2519 __signal_func_ptr
2520 sys_signal (int signal_num, __signal_func_ptr signal_func)
2521 #else /* not __MRC__ and not __MWERKS__ */
2522 You lose!!!
2523 #endif /* not __MRC__ and not __MWERKS__ */
2525 if (signal_num != SIGALRM)
2526 return signal (signal_num, signal_func);
2527 else
2529 #ifdef __MRC__
2530 __sigfun old_signal_func;
2531 #elif __MWERKS__
2532 __signal_func_ptr old_signal_func;
2533 #else
2534 You lose!!!
2535 #endif
2536 old_signal_func = alarm_signal_func;
2537 alarm_signal_func = signal_func;
2538 return old_signal_func;
2543 static pascal void
2544 mac_atimer_handler (qlink)
2545 TMTaskPtr qlink;
2547 if (alarm_signal_func)
2548 (alarm_signal_func) (SIGALRM);
2552 static void
2553 set_mac_atimer (count)
2554 long count;
2556 static TimerUPP mac_atimer_handlerUPP = NULL;
2558 if (mac_atimer_handlerUPP == NULL)
2559 mac_atimer_handlerUPP = NewTimerUPP (mac_atimer_handler);
2560 mac_atimer_task.tmCount = 0;
2561 mac_atimer_task.tmAddr = mac_atimer_handlerUPP;
2562 mac_atimer_qlink = (QElemPtr) &mac_atimer_task;
2563 InsTime (mac_atimer_qlink);
2564 if (count)
2565 PrimeTime (mac_atimer_qlink, count);
2570 remove_mac_atimer (remaining_count)
2571 long *remaining_count;
2573 if (mac_atimer_qlink)
2575 RmvTime (mac_atimer_qlink);
2576 if (remaining_count)
2577 *remaining_count = mac_atimer_task.tmCount;
2578 mac_atimer_qlink = NULL;
2580 return 0;
2582 else
2583 return -1;
2588 sigblock (int mask)
2590 int old_mask = signal_mask;
2592 signal_mask |= mask;
2594 if ((old_mask ^ signal_mask) & sigmask (SIGALRM))
2595 remove_mac_atimer (NULL);
2597 return old_mask;
2602 sigsetmask (int mask)
2604 int old_mask = signal_mask;
2606 signal_mask = mask;
2608 if ((old_mask ^ signal_mask) & sigmask (SIGALRM))
2609 if (signal_mask & sigmask (SIGALRM))
2610 remove_mac_atimer (NULL);
2611 else
2612 set_mac_atimer (mac_atimer_task.tmCount);
2614 return old_mask;
2619 alarm (int seconds)
2621 long remaining_count;
2623 if (remove_mac_atimer (&remaining_count) == 0)
2625 set_mac_atimer (seconds * 1000);
2627 return remaining_count / 1000;
2629 else
2631 mac_atimer_task.tmCount = seconds * 1000;
2633 return 0;
2639 setitimer (which, value, ovalue)
2640 int which;
2641 const struct itimerval *value;
2642 struct itimerval *ovalue;
2644 long remaining_count;
2645 long count = (EMACS_SECS (value->it_value) * 1000
2646 + (EMACS_USECS (value->it_value) + 999) / 1000);
2648 if (remove_mac_atimer (&remaining_count) == 0)
2650 if (ovalue)
2652 bzero (ovalue, sizeof (*ovalue));
2653 EMACS_SET_SECS_USECS (ovalue->it_value, remaining_count / 1000,
2654 (remaining_count % 1000) * 1000);
2656 set_mac_atimer (count);
2658 else
2659 mac_atimer_task.tmCount = count;
2661 return 0;
2665 /* gettimeofday should return the amount of time (in a timeval
2666 structure) since midnight today. The toolbox function Microseconds
2667 returns the number of microseconds (in a UnsignedWide value) since
2668 the machine was booted. Also making this complicated is WideAdd,
2669 WideSubtract, etc. take wide values. */
2672 gettimeofday (tp)
2673 struct timeval *tp;
2675 static inited = 0;
2676 static wide wall_clock_at_epoch, clicks_at_epoch;
2677 UnsignedWide uw_microseconds;
2678 wide w_microseconds;
2679 time_t sys_time (time_t *);
2681 /* If this function is called for the first time, record the number
2682 of seconds since midnight and the number of microseconds since
2683 boot at the time of this first call. */
2684 if (!inited)
2686 time_t systime;
2687 inited = 1;
2688 systime = sys_time (NULL);
2689 /* Store microseconds since midnight in wall_clock_at_epoch. */
2690 WideMultiply (systime, 1000000L, &wall_clock_at_epoch);
2691 Microseconds (&uw_microseconds);
2692 /* Store microseconds since boot in clicks_at_epoch. */
2693 clicks_at_epoch.hi = uw_microseconds.hi;
2694 clicks_at_epoch.lo = uw_microseconds.lo;
2697 /* Get time since boot */
2698 Microseconds (&uw_microseconds);
2700 /* Convert to time since midnight*/
2701 w_microseconds.hi = uw_microseconds.hi;
2702 w_microseconds.lo = uw_microseconds.lo;
2703 WideSubtract (&w_microseconds, &clicks_at_epoch);
2704 WideAdd (&w_microseconds, &wall_clock_at_epoch);
2705 tp->tv_sec = WideDivide (&w_microseconds, 1000000L, &tp->tv_usec);
2707 return 0;
2711 #ifdef __MRC__
2712 unsigned int
2713 sleep (unsigned int seconds)
2715 unsigned long time_up;
2716 EventRecord e;
2718 time_up = TickCount () + seconds * 60;
2719 while (TickCount () < time_up)
2721 /* Accept no event; just wait. by T.I. */
2722 WaitNextEvent (0, &e, 30, NULL);
2725 return (0);
2727 #endif /* __MRC__ */
2730 /* The time functions adjust time values according to the difference
2731 between the Unix and CW epoches. */
2733 #undef gmtime
2734 extern struct tm *gmtime (const time_t *);
2735 struct tm *
2736 sys_gmtime (const time_t *timer)
2738 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
2740 return gmtime (&unix_time);
2744 #undef localtime
2745 extern struct tm *localtime (const time_t *);
2746 struct tm *
2747 sys_localtime (const time_t *timer)
2749 #if __MSL__ >= 0x6000
2750 time_t unix_time = *timer;
2751 #else
2752 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
2753 #endif
2755 return localtime (&unix_time);
2759 #undef ctime
2760 extern char *ctime (const time_t *);
2761 char *
2762 sys_ctime (const time_t *timer)
2764 #if __MSL__ >= 0x6000
2765 time_t unix_time = *timer;
2766 #else
2767 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
2768 #endif
2770 return ctime (&unix_time);
2774 #undef time
2775 extern time_t time (time_t *);
2776 time_t
2777 sys_time (time_t *timer)
2779 #if __MSL__ >= 0x6000
2780 time_t mac_time = time (NULL);
2781 #else
2782 time_t mac_time = time (NULL) - CW_OR_MPW_UNIX_EPOCH_DIFF;
2783 #endif
2785 if (timer)
2786 *timer = mac_time;
2788 return mac_time;
2792 /* no subprocesses, empty wait */
2795 wait (int pid)
2797 return 0;
2801 void
2802 croak (char *badfunc)
2804 printf ("%s not yet implemented\r\n", badfunc);
2805 exit (1);
2809 char *
2810 mktemp (char *template)
2812 int len, k;
2813 static seqnum = 0;
2815 len = strlen (template);
2816 k = len - 1;
2817 while (k >= 0 && template[k] == 'X')
2818 k--;
2820 k++; /* make k index of first 'X' */
2822 if (k < len)
2824 /* Zero filled, number of digits equal to the number of X's. */
2825 sprintf (&template[k], "%0*d", len-k, seqnum++);
2827 return template;
2829 else
2830 return 0;
2834 /* Emulate getpwuid, getpwnam and others. */
2836 #define PASSWD_FIELD_SIZE 256
2838 static char my_passwd_name[PASSWD_FIELD_SIZE];
2839 static char my_passwd_dir[MAXPATHLEN+1];
2841 static struct passwd my_passwd =
2843 my_passwd_name,
2844 my_passwd_dir,
2847 static struct group my_group =
2849 /* There are no groups on the mac, so we just return "root" as the
2850 group name. */
2851 "root",
2855 /* Initialized by main () in macterm.c to pathname of emacs directory. */
2857 char emacs_passwd_dir[MAXPATHLEN+1];
2859 char *
2860 getwd (char *);
2862 void
2863 init_emacs_passwd_dir ()
2865 int found = false;
2867 if (getwd (emacs_passwd_dir) && getwd (my_passwd_dir))
2869 /* Need pathname of first ancestor that begins with "emacs"
2870 since Mac emacs application is somewhere in the emacs-*
2871 tree. */
2872 int len = strlen (emacs_passwd_dir);
2873 int j = len - 1;
2874 /* j points to the "/" following the directory name being
2875 compared. */
2876 int i = j - 1;
2877 while (i >= 0 && !found)
2879 while (i >= 0 && emacs_passwd_dir[i] != '/')
2880 i--;
2881 if (emacs_passwd_dir[i] == '/' && i+5 < len)
2882 found = (strncmp (&(emacs_passwd_dir[i+1]), "emacs", 5) == 0);
2883 if (found)
2884 emacs_passwd_dir[j+1] = '\0';
2885 else
2887 j = i;
2888 i = j - 1;
2893 if (!found)
2895 /* Setting to "/" probably won't work but set it to something
2896 anyway. */
2897 strcpy (emacs_passwd_dir, "/");
2898 strcpy (my_passwd_dir, "/");
2903 static struct passwd emacs_passwd =
2905 "emacs",
2906 emacs_passwd_dir,
2909 static int my_passwd_inited = 0;
2912 static void
2913 init_my_passwd ()
2915 char **owner_name;
2917 /* Note: my_passwd_dir initialized in int_emacs_passwd_dir to
2918 directory where Emacs was started. */
2920 owner_name = (char **) GetResource ('STR ',-16096);
2921 if (owner_name)
2923 HLock (owner_name);
2924 BlockMove ((unsigned char *) *owner_name,
2925 (unsigned char *) my_passwd_name,
2926 *owner_name[0]+1);
2927 HUnlock (owner_name);
2928 p2cstr ((unsigned char *) my_passwd_name);
2930 else
2931 my_passwd_name[0] = 0;
2935 struct passwd *
2936 getpwuid (uid_t uid)
2938 if (!my_passwd_inited)
2940 init_my_passwd ();
2941 my_passwd_inited = 1;
2944 return &my_passwd;
2948 struct group *
2949 getgrgid (gid_t gid)
2951 return &my_group;
2955 struct passwd *
2956 getpwnam (const char *name)
2958 if (strcmp (name, "emacs") == 0)
2959 return &emacs_passwd;
2961 if (!my_passwd_inited)
2963 init_my_passwd ();
2964 my_passwd_inited = 1;
2967 return &my_passwd;
2971 /* The functions fork, kill, sigsetmask, sigblock, request_sigio,
2972 setpgrp, setpriority, and unrequest_sigio are defined to be empty
2973 as in msdos.c. */
2977 fork ()
2979 return -1;
2984 kill (int x, int y)
2986 return -1;
2990 void
2991 sys_subshell ()
2993 error ("Can't spawn subshell");
2997 void
2998 request_sigio (void)
3003 void
3004 unrequest_sigio (void)
3010 setpgrp ()
3012 return 0;
3016 /* No pipes yet. */
3019 pipe (int _fildes[2])
3021 errno = EACCES;
3022 return -1;
3026 /* Hard and symbolic links. */
3029 symlink (const char *name1, const char *name2)
3031 errno = ENOENT;
3032 return -1;
3037 link (const char *name1, const char *name2)
3039 errno = ENOENT;
3040 return -1;
3043 #endif /* ! MAC_OSX */
3045 /* Determine the path name of the file specified by VREFNUM, DIRID,
3046 and NAME and place that in the buffer PATH of length
3047 MAXPATHLEN. */
3048 static int
3049 path_from_vol_dir_name (char *path, int man_path_len, short vol_ref_num,
3050 long dir_id, ConstStr255Param name)
3052 Str255 dir_name;
3053 CInfoPBRec cipb;
3054 OSErr err;
3056 if (strlen (name) > man_path_len)
3057 return 0;
3059 memcpy (dir_name, name, name[0]+1);
3060 memcpy (path, name, name[0]+1);
3061 p2cstr (path);
3063 cipb.dirInfo.ioDrParID = dir_id;
3064 cipb.dirInfo.ioNamePtr = dir_name;
3068 cipb.dirInfo.ioVRefNum = vol_ref_num;
3069 cipb.dirInfo.ioFDirIndex = -1;
3070 cipb.dirInfo.ioDrDirID = cipb.dirInfo.ioDrParID;
3071 /* go up to parent each time */
3073 err = PBGetCatInfo (&cipb, false);
3074 if (err != noErr)
3075 return 0;
3077 p2cstr (dir_name);
3078 if (strlen (dir_name) + strlen (path) + 1 >= man_path_len)
3079 return 0;
3081 strcat (dir_name, ":");
3082 strcat (dir_name, path);
3083 /* attach to front since we're going up directory tree */
3084 strcpy (path, dir_name);
3086 while (cipb.dirInfo.ioDrDirID != fsRtDirID);
3087 /* stop when we see the volume's root directory */
3089 return 1; /* success */
3093 #ifndef MAC_OSX
3095 static OSErr
3096 posix_pathname_to_fsspec (ufn, fs)
3097 const char *ufn;
3098 FSSpec *fs;
3100 Str255 mac_pathname;
3102 if (posix_to_mac_pathname (ufn, mac_pathname, sizeof (mac_pathname)) == 0)
3103 return fnfErr;
3104 else
3106 c2pstr (mac_pathname);
3107 return FSMakeFSSpec (0, 0, mac_pathname, fs);
3111 static OSErr
3112 fsspec_to_posix_pathname (fs, ufn, ufnbuflen)
3113 const FSSpec *fs;
3114 char *ufn;
3115 int ufnbuflen;
3117 char mac_pathname[MAXPATHLEN];
3119 if (path_from_vol_dir_name (mac_pathname, sizeof (mac_pathname) - 1,
3120 fs->vRefNum, fs->parID, fs->name)
3121 && mac_to_posix_pathname (mac_pathname, ufn, ufnbuflen))
3122 return noErr;
3123 else
3124 return fnfErr;
3128 readlink (const char *path, char *buf, int bufsiz)
3130 char mac_sym_link_name[MAXPATHLEN+1];
3131 OSErr err;
3132 FSSpec fsspec;
3133 Boolean target_is_folder, was_aliased;
3134 Str255 directory_name, mac_pathname;
3135 CInfoPBRec cipb;
3137 if (posix_to_mac_pathname (path, mac_sym_link_name, MAXPATHLEN+1) == 0)
3138 return -1;
3140 c2pstr (mac_sym_link_name);
3141 err = FSMakeFSSpec (0, 0, mac_sym_link_name, &fsspec);
3142 if (err != noErr)
3144 errno = ENOENT;
3145 return -1;
3148 err = ResolveAliasFile (&fsspec, true, &target_is_folder, &was_aliased);
3149 if (err != noErr || !was_aliased)
3151 errno = ENOENT;
3152 return -1;
3155 if (path_from_vol_dir_name (mac_pathname, 255, fsspec.vRefNum, fsspec.parID,
3156 fsspec.name) == 0)
3158 errno = ENOENT;
3159 return -1;
3162 if (mac_to_posix_pathname (mac_pathname, buf, bufsiz) == 0)
3164 errno = ENOENT;
3165 return -1;
3168 return strlen (buf);
3172 /* Convert a path to one with aliases fully expanded. */
3174 static int
3175 find_true_pathname (const char *path, char *buf, int bufsiz)
3177 char *q, temp[MAXPATHLEN+1];
3178 const char *p;
3179 int len;
3181 if (bufsiz <= 0 || path == 0 || path[0] == '\0')
3182 return -1;
3184 buf[0] = '\0';
3186 p = path;
3187 if (*p == '/')
3188 q = strchr (p + 1, '/');
3189 else
3190 q = strchr (p, '/');
3191 len = 0; /* loop may not be entered, e.g., for "/" */
3193 while (q)
3195 strcpy (temp, buf);
3196 strncat (temp, p, q - p);
3197 len = readlink (temp, buf, bufsiz);
3198 if (len <= -1)
3200 if (strlen (temp) + 1 > bufsiz)
3201 return -1;
3202 strcpy (buf, temp);
3204 strcat (buf, "/");
3205 len++;
3206 p = q + 1;
3207 q = strchr(p, '/');
3210 if (len + strlen (p) + 1 >= bufsiz)
3211 return -1;
3213 strcat (buf, p);
3214 return len + strlen (p);
3218 mode_t
3219 umask (mode_t numask)
3221 static mode_t mask = 022;
3222 mode_t oldmask = mask;
3223 mask = numask;
3224 return oldmask;
3229 chmod (const char *path, mode_t mode)
3231 /* say it always succeed for now */
3232 return 0;
3237 fchmod (int fd, mode_t mode)
3239 /* say it always succeed for now */
3240 return 0;
3245 fchown (int fd, uid_t owner, gid_t group)
3247 /* say it always succeed for now */
3248 return 0;
3253 dup (int oldd)
3255 #ifdef __MRC__
3256 return fcntl (oldd, F_DUPFD, 0);
3257 #elif __MWERKS__
3258 /* current implementation of fcntl in fcntl.mac.c simply returns old
3259 descriptor */
3260 return fcntl (oldd, F_DUPFD);
3261 #else
3262 You lose!!!
3263 #endif
3267 /* This is from the original sysdep.c. Emulate BSD dup2. First close
3268 newd if it already exists. Then, attempt to dup oldd. If not
3269 successful, call dup2 recursively until we are, then close the
3270 unsuccessful ones. */
3273 dup2 (int oldd, int newd)
3275 int fd, ret;
3277 close (newd);
3279 fd = dup (oldd);
3280 if (fd == -1)
3281 return -1;
3282 if (fd == newd)
3283 return newd;
3284 ret = dup2 (oldd, newd);
3285 close (fd);
3286 return ret;
3290 /* let it fail for now */
3292 char *
3293 sbrk (int incr)
3295 return (char *) -1;
3300 fsync (int fd)
3302 return 0;
3307 ioctl (int d, int request, void *argp)
3309 return -1;
3313 #ifdef __MRC__
3315 isatty (int fildes)
3317 if (fildes >=0 && fildes <= 2)
3318 return 1;
3319 else
3320 return 0;
3325 getgid ()
3327 return 100;
3332 getegid ()
3334 return 100;
3339 getuid ()
3341 return 200;
3346 geteuid ()
3348 return 200;
3350 #endif /* __MRC__ */
3353 #ifdef __MWERKS__
3354 #if __MSL__ < 0x6000
3355 #undef getpid
3357 getpid ()
3359 return 9999;
3361 #endif
3362 #endif /* __MWERKS__ */
3364 #endif /* ! MAC_OSX */
3367 /* Return the path to the directory in which Emacs can create
3368 temporary files. The MacOS "temporary items" directory cannot be
3369 used because it removes the file written by a process when it
3370 exits. In that sense it's more like "/dev/null" than "/tmp" (but
3371 again not exactly). And of course Emacs needs to read back the
3372 files written by its subprocesses. So here we write the files to a
3373 directory "Emacs" in the Preferences Folder. This directory is
3374 created if it does not exist. */
3376 char *
3377 get_temp_dir_name ()
3379 static char *temp_dir_name = NULL;
3380 short vol_ref_num;
3381 long dir_id;
3382 OSErr err;
3383 Str255 full_path;
3384 char unix_dir_name[MAXPATHLEN+1];
3385 DIR *dir;
3387 /* Cache directory name with pointer temp_dir_name.
3388 Look for it only the first time. */
3389 if (!temp_dir_name)
3391 err = FindFolder (kOnSystemDisk, kPreferencesFolderType, kCreateFolder,
3392 &vol_ref_num, &dir_id);
3393 if (err != noErr)
3394 return NULL;
3396 if (!path_from_vol_dir_name (full_path, 255, vol_ref_num, dir_id, "\p"))
3397 return NULL;
3399 if (strlen (full_path) + 6 <= MAXPATHLEN)
3400 strcat (full_path, "Emacs:");
3401 else
3402 return NULL;
3404 if (!mac_to_posix_pathname (full_path, unix_dir_name, MAXPATHLEN+1))
3405 return NULL;
3407 dir = opendir (unix_dir_name); /* check whether temp directory exists */
3408 if (dir)
3409 closedir (dir);
3410 else if (mkdir (unix_dir_name, 0700) != 0) /* create it if not */
3411 return NULL;
3413 temp_dir_name = (char *) malloc (strlen (unix_dir_name) + 1);
3414 strcpy (temp_dir_name, unix_dir_name);
3417 return temp_dir_name;
3420 #ifndef MAC_OSX
3422 /* Allocate and construct an array of pointers to strings from a list
3423 of strings stored in a 'STR#' resource. The returned pointer array
3424 is stored in the style of argv and environ: if the 'STR#' resource
3425 contains numString strings, a pointer array with numString+1
3426 elements is returned in which the last entry contains a null
3427 pointer. The pointer to the pointer array is passed by pointer in
3428 parameter t. The resource ID of the 'STR#' resource is passed in
3429 parameter StringListID.
3432 void
3433 get_string_list (char ***t, short string_list_id)
3435 Handle h;
3436 Ptr p;
3437 int i, num_strings;
3439 h = GetResource ('STR#', string_list_id);
3440 if (h)
3442 HLock (h);
3443 p = *h;
3444 num_strings = * (short *) p;
3445 p += sizeof(short);
3446 *t = (char **) malloc (sizeof (char *) * (num_strings + 1));
3447 for (i = 0; i < num_strings; i++)
3449 short length = *p++;
3450 (*t)[i] = (char *) malloc (length + 1);
3451 strncpy ((*t)[i], p, length);
3452 (*t)[i][length] = '\0';
3453 p += length;
3455 (*t)[num_strings] = 0;
3456 HUnlock (h);
3458 else
3460 /* Return no string in case GetResource fails. Bug fixed by
3461 Ikegami Tsutomu. Caused MPW build to crash without sym -on
3462 option (no sym -on implies -opt local). */
3463 *t = (char **) malloc (sizeof (char *));
3464 (*t)[0] = 0;
3469 static char *
3470 get_path_to_system_folder ()
3472 short vol_ref_num;
3473 long dir_id;
3474 OSErr err;
3475 Str255 full_path;
3476 static char system_folder_unix_name[MAXPATHLEN+1];
3477 DIR *dir;
3479 err = FindFolder (kOnSystemDisk, kSystemFolderType, kDontCreateFolder,
3480 &vol_ref_num, &dir_id);
3481 if (err != noErr)
3482 return NULL;
3484 if (!path_from_vol_dir_name (full_path, 255, vol_ref_num, dir_id, "\p"))
3485 return NULL;
3487 if (!mac_to_posix_pathname (full_path, system_folder_unix_name,
3488 MAXPATHLEN+1))
3489 return NULL;
3491 return system_folder_unix_name;
3495 char **environ;
3497 #define ENVIRON_STRING_LIST_ID 128
3499 /* Get environment variable definitions from STR# resource. */
3501 void
3502 init_environ ()
3504 int i;
3506 get_string_list (&environ, ENVIRON_STRING_LIST_ID);
3508 i = 0;
3509 while (environ[i])
3510 i++;
3512 /* Make HOME directory the one Emacs starts up in if not specified
3513 by resource. */
3514 if (getenv ("HOME") == NULL)
3516 environ = (char **) realloc (environ, sizeof (char *) * (i + 2));
3517 if (environ)
3519 environ[i] = (char *) malloc (strlen (my_passwd_dir) + 6);
3520 if (environ[i])
3522 strcpy (environ[i], "HOME=");
3523 strcat (environ[i], my_passwd_dir);
3525 environ[i+1] = 0;
3526 i++;
3530 /* Make HOME directory the one Emacs starts up in if not specified
3531 by resource. */
3532 if (getenv ("MAIL") == NULL)
3534 environ = (char **) realloc (environ, sizeof (char *) * (i + 2));
3535 if (environ)
3537 char * path_to_system_folder = get_path_to_system_folder ();
3538 environ[i] = (char *) malloc (strlen (path_to_system_folder) + 22);
3539 if (environ[i])
3541 strcpy (environ[i], "MAIL=");
3542 strcat (environ[i], path_to_system_folder);
3543 strcat (environ[i], "Eudora Folder/In");
3545 environ[i+1] = 0;
3551 /* Return the value of the environment variable NAME. */
3553 char *
3554 getenv (const char *name)
3556 int length = strlen(name);
3557 char **e;
3559 for (e = environ; *e != 0; e++)
3560 if (strncmp(*e, name, length) == 0 && (*e)[length] == '=')
3561 return &(*e)[length + 1];
3563 if (strcmp (name, "TMPDIR") == 0)
3564 return get_temp_dir_name ();
3566 return 0;
3570 #ifdef __MRC__
3571 /* see Interfaces&Libraries:Interfaces:CIncludes:signal.h */
3572 char *sys_siglist[] =
3574 "Zero is not a signal!!!",
3575 "Abort", /* 1 */
3576 "Interactive user interrupt", /* 2 */ "?",
3577 "Floating point exception", /* 4 */ "?", "?", "?",
3578 "Illegal instruction", /* 8 */ "?", "?", "?", "?", "?", "?", "?",
3579 "Segment violation", /* 16 */ "?", "?", "?", "?", "?", "?", "?",
3580 "?", "?", "?", "?", "?", "?", "?", "?",
3581 "Terminal" /* 32 */
3583 #elif __MWERKS__
3584 char *sys_siglist[] =
3586 "Zero is not a signal!!!",
3587 "Abort",
3588 "Floating point exception",
3589 "Illegal instruction",
3590 "Interactive user interrupt",
3591 "Segment violation",
3592 "Terminal"
3594 #else /* not __MRC__ and not __MWERKS__ */
3595 You lose!!!
3596 #endif /* not __MRC__ and not __MWERKS__ */
3599 #include <utsname.h>
3602 uname (struct utsname *name)
3604 char **system_name;
3605 system_name = GetString (-16413); /* IM - Resource Manager Reference */
3606 if (system_name)
3608 BlockMove (*system_name, name->nodename, (*system_name)[0]+1);
3609 p2cstr (name->nodename);
3610 return 0;
3612 else
3613 return -1;
3617 /* Event class of HLE sent to subprocess. */
3618 const OSType kEmacsSubprocessSend = 'ESND';
3620 /* Event class of HLE sent back from subprocess. */
3621 const OSType kEmacsSubprocessReply = 'ERPY';
3624 char *
3625 mystrchr (char *s, char c)
3627 while (*s && *s != c)
3629 if (*s == '\\')
3630 s++;
3631 s++;
3634 if (*s)
3636 *s = '\0';
3637 return s;
3639 else
3640 return NULL;
3644 char *
3645 mystrtok (char *s)
3647 while (*s)
3648 s++;
3650 return s + 1;
3654 void
3655 mystrcpy (char *to, char *from)
3657 while (*from)
3659 if (*from == '\\')
3660 from++;
3661 *to++ = *from++;
3663 *to = '\0';
3667 /* Start a Mac subprocess. Arguments for it is passed in argv (null
3668 terminated). The process should run with the default directory
3669 "workdir", read input from "infn", and write output and error to
3670 "outfn" and "errfn", resp. The Process Manager call
3671 LaunchApplication is used to start the subprocess. We use high
3672 level events as the mechanism to pass arguments to the subprocess
3673 and to make Emacs wait for the subprocess to terminate and pass
3674 back a result code. The bulk of the code here packs the arguments
3675 into one message to be passed together with the high level event.
3676 Emacs also sometimes starts a subprocess using a shell to perform
3677 wildcard filename expansion. Since we don't really have a shell on
3678 the Mac, this case is detected and the starting of the shell is
3679 by-passed. We really need to add code here to do filename
3680 expansion to support such functionality.
3682 We can't use this strategy in Carbon because the High Level Event
3683 APIs are not available. */
3686 run_mac_command (argv, workdir, infn, outfn, errfn)
3687 unsigned char **argv;
3688 const char *workdir;
3689 const char *infn, *outfn, *errfn;
3691 #if TARGET_API_MAC_CARBON
3692 return -1;
3693 #else /* not TARGET_API_MAC_CARBON */
3694 char macappname[MAXPATHLEN+1], macworkdir[MAXPATHLEN+1];
3695 char macinfn[MAXPATHLEN+1], macoutfn[MAXPATHLEN+1], macerrfn[MAXPATHLEN+1];
3696 int paramlen, argc, newargc, j, retries;
3697 char **newargv, *param, *p;
3698 OSErr iErr;
3699 FSSpec spec;
3700 LaunchParamBlockRec lpbr;
3701 EventRecord send_event, reply_event;
3702 RgnHandle cursor_region_handle;
3703 TargetID targ;
3704 unsigned long ref_con, len;
3706 if (posix_to_mac_pathname (workdir, macworkdir, MAXPATHLEN+1) == 0)
3707 return -1;
3708 if (posix_to_mac_pathname (infn, macinfn, MAXPATHLEN+1) == 0)
3709 return -1;
3710 if (posix_to_mac_pathname (outfn, macoutfn, MAXPATHLEN+1) == 0)
3711 return -1;
3712 if (posix_to_mac_pathname (errfn, macerrfn, MAXPATHLEN+1) == 0)
3713 return -1;
3715 paramlen = strlen (macworkdir) + strlen (macinfn) + strlen (macoutfn)
3716 + strlen (macerrfn) + 4; /* count nulls at end of strings */
3718 argc = 0;
3719 while (argv[argc])
3720 argc++;
3722 if (argc == 0)
3723 return -1;
3725 /* If a subprocess is invoked with a shell, we receive 3 arguments
3726 of the form: "<path to emacs bins>/sh" "-c" "<path to emacs
3727 bins>/<command> <command args>" */
3728 j = strlen (argv[0]);
3729 if (j >= 3 && strcmp (argv[0]+j-3, "/sh") == 0
3730 && argc == 3 && strcmp (argv[1], "-c") == 0)
3732 char *command, *t, tempmacpathname[MAXPATHLEN+1];
3734 /* The arguments for the command in argv[2] are separated by
3735 spaces. Count them and put the count in newargc. */
3736 command = (char *) alloca (strlen (argv[2])+2);
3737 strcpy (command, argv[2]);
3738 if (command[strlen (command) - 1] != ' ')
3739 strcat (command, " ");
3741 t = command;
3742 newargc = 0;
3743 t = mystrchr (t, ' ');
3744 while (t)
3746 newargc++;
3747 t = mystrchr (t+1, ' ');
3750 newargv = (char **) alloca (sizeof (char *) * newargc);
3752 t = command;
3753 for (j = 0; j < newargc; j++)
3755 newargv[j] = (char *) alloca (strlen (t) + 1);
3756 mystrcpy (newargv[j], t);
3758 t = mystrtok (t);
3759 paramlen += strlen (newargv[j]) + 1;
3762 if (strncmp (newargv[0], "~emacs/", 7) == 0)
3764 if (posix_to_mac_pathname (newargv[0], tempmacpathname, MAXPATHLEN+1)
3765 == 0)
3766 return -1;
3768 else
3769 { /* sometimes Emacs call "sh" without a path for the command */
3770 #if 0
3771 char *t = (char *) alloca (strlen (newargv[0]) + 7 + 1);
3772 strcpy (t, "~emacs/");
3773 strcat (t, newargv[0]);
3774 #endif /* 0 */
3775 Lisp_Object path;
3776 openp (Vexec_path, build_string (newargv[0]), Vexec_suffixes, &path,
3777 make_number (X_OK));
3779 if (NILP (path))
3780 return -1;
3781 if (posix_to_mac_pathname (SDATA (path), tempmacpathname,
3782 MAXPATHLEN+1) == 0)
3783 return -1;
3785 strcpy (macappname, tempmacpathname);
3787 else
3789 if (posix_to_mac_pathname (argv[0], macappname, MAXPATHLEN+1) == 0)
3790 return -1;
3792 newargv = (char **) alloca (sizeof (char *) * argc);
3793 newargc = argc;
3794 for (j = 1; j < argc; j++)
3796 if (strncmp (argv[j], "~emacs/", 7) == 0)
3798 char *t = strchr (argv[j], ' ');
3799 if (t)
3801 char tempcmdname[MAXPATHLEN+1], tempmaccmdname[MAXPATHLEN+1];
3802 strncpy (tempcmdname, argv[j], t-argv[j]);
3803 tempcmdname[t-argv[j]] = '\0';
3804 if (posix_to_mac_pathname (tempcmdname, tempmaccmdname,
3805 MAXPATHLEN+1) == 0)
3806 return -1;
3807 newargv[j] = (char *) alloca (strlen (tempmaccmdname)
3808 + strlen (t) + 1);
3809 strcpy (newargv[j], tempmaccmdname);
3810 strcat (newargv[j], t);
3812 else
3814 char tempmaccmdname[MAXPATHLEN+1];
3815 if (posix_to_mac_pathname (argv[j], tempmaccmdname,
3816 MAXPATHLEN+1) == 0)
3817 return -1;
3818 newargv[j] = (char *) alloca (strlen (tempmaccmdname)+1);
3819 strcpy (newargv[j], tempmaccmdname);
3822 else
3823 newargv[j] = argv[j];
3824 paramlen += strlen (newargv[j]) + 1;
3828 /* After expanding all the arguments, we now know the length of the
3829 parameter block to be sent to the subprocess as a message
3830 attached to the HLE. */
3831 param = (char *) malloc (paramlen + 1);
3832 if (!param)
3833 return -1;
3835 p = param;
3836 *p++ = newargc;
3837 /* first byte of message contains number of arguments for command */
3838 strcpy (p, macworkdir);
3839 p += strlen (macworkdir);
3840 *p++ = '\0';
3841 /* null terminate strings sent so it's possible to use strcpy over there */
3842 strcpy (p, macinfn);
3843 p += strlen (macinfn);
3844 *p++ = '\0';
3845 strcpy (p, macoutfn);
3846 p += strlen (macoutfn);
3847 *p++ = '\0';
3848 strcpy (p, macerrfn);
3849 p += strlen (macerrfn);
3850 *p++ = '\0';
3851 for (j = 1; j < newargc; j++)
3853 strcpy (p, newargv[j]);
3854 p += strlen (newargv[j]);
3855 *p++ = '\0';
3858 c2pstr (macappname);
3860 iErr = FSMakeFSSpec (0, 0, macappname, &spec);
3862 if (iErr != noErr)
3864 free (param);
3865 return -1;
3868 lpbr.launchBlockID = extendedBlock;
3869 lpbr.launchEPBLength = extendedBlockLen;
3870 lpbr.launchControlFlags = launchContinue + launchNoFileFlags;
3871 lpbr.launchAppSpec = &spec;
3872 lpbr.launchAppParameters = NULL;
3874 iErr = LaunchApplication (&lpbr); /* call the subprocess */
3875 if (iErr != noErr)
3877 free (param);
3878 return -1;
3881 send_event.what = kHighLevelEvent;
3882 send_event.message = kEmacsSubprocessSend;
3883 /* Event ID stored in "where" unused */
3885 retries = 3;
3886 /* OS may think current subprocess has terminated if previous one
3887 terminated recently. */
3890 iErr = PostHighLevelEvent (&send_event, &lpbr.launchProcessSN, 0, param,
3891 paramlen + 1, receiverIDisPSN);
3893 while (iErr == sessClosedErr && retries-- > 0);
3895 if (iErr != noErr)
3897 free (param);
3898 return -1;
3901 cursor_region_handle = NewRgn ();
3903 /* Wait for the subprocess to finish, when it will send us a ERPY
3904 high level event. */
3905 while (1)
3906 if (WaitNextEvent (highLevelEventMask, &reply_event, 180,
3907 cursor_region_handle)
3908 && reply_event.message == kEmacsSubprocessReply)
3909 break;
3911 /* The return code is sent through the refCon */
3912 iErr = AcceptHighLevelEvent (&targ, &ref_con, NULL, &len);
3913 if (iErr != noErr)
3915 DisposeHandle ((Handle) cursor_region_handle);
3916 free (param);
3917 return -1;
3920 DisposeHandle ((Handle) cursor_region_handle);
3921 free (param);
3923 return ref_con;
3924 #endif /* not TARGET_API_MAC_CARBON */
3928 DIR *
3929 opendir (const char *dirname)
3931 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
3932 char mac_pathname[MAXPATHLEN+1], vol_name[MAXPATHLEN+1];
3933 DIR *dirp;
3934 CInfoPBRec cipb;
3935 HVolumeParam vpb;
3936 int len, vol_name_len;
3938 if (find_true_pathname (dirname, true_pathname, MAXPATHLEN+1) == -1)
3939 return 0;
3941 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
3942 if (len > -1)
3943 fully_resolved_name[len] = '\0';
3944 else
3945 strcpy (fully_resolved_name, true_pathname);
3947 dirp = (DIR *) malloc (sizeof(DIR));
3948 if (!dirp)
3949 return 0;
3951 /* Handle special case when dirname is "/": sets up for readir to
3952 get all mount volumes. */
3953 if (strcmp (fully_resolved_name, "/") == 0)
3955 dirp->getting_volumes = 1; /* special all mounted volumes DIR struct */
3956 dirp->current_index = 1; /* index for first volume */
3957 return dirp;
3960 /* Handle typical cases: not accessing all mounted volumes. */
3961 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
3962 return 0;
3964 /* Emacs calls opendir without the trailing '/', Mac needs trailing ':' */
3965 len = strlen (mac_pathname);
3966 if (mac_pathname[len - 1] != ':' && len < MAXPATHLEN)
3967 strcat (mac_pathname, ":");
3969 /* Extract volume name */
3970 vol_name_len = strchr (mac_pathname, ':') - mac_pathname;
3971 strncpy (vol_name, mac_pathname, vol_name_len);
3972 vol_name[vol_name_len] = '\0';
3973 strcat (vol_name, ":");
3975 c2pstr (mac_pathname);
3976 cipb.hFileInfo.ioNamePtr = mac_pathname;
3977 /* using full pathname so vRefNum and DirID ignored */
3978 cipb.hFileInfo.ioVRefNum = 0;
3979 cipb.hFileInfo.ioDirID = 0;
3980 cipb.hFileInfo.ioFDirIndex = 0;
3981 /* set to 0 to get information about specific dir or file */
3983 errno = PBGetCatInfo (&cipb, false);
3984 if (errno != noErr)
3986 errno = ENOENT;
3987 return 0;
3990 if (!(cipb.hFileInfo.ioFlAttrib & 0x10)) /* bit 4 = 1 for directories */
3991 return 0; /* not a directory */
3993 dirp->dir_id = cipb.dirInfo.ioDrDirID; /* used later in readdir */
3994 dirp->getting_volumes = 0;
3995 dirp->current_index = 1; /* index for first file/directory */
3997 c2pstr (vol_name);
3998 vpb.ioNamePtr = vol_name;
3999 /* using full pathname so vRefNum and DirID ignored */
4000 vpb.ioVRefNum = 0;
4001 vpb.ioVolIndex = -1;
4002 errno = PBHGetVInfo ((union HParamBlockRec *) &vpb, false);
4003 if (errno != noErr)
4005 errno = ENOENT;
4006 return 0;
4009 dirp->vol_ref_num = vpb.ioVRefNum;
4011 return dirp;
4015 closedir (DIR *dp)
4017 free (dp);
4019 return 0;
4023 struct dirent *
4024 readdir (DIR *dp)
4026 HParamBlockRec hpblock;
4027 CInfoPBRec cipb;
4028 static struct dirent s_dirent;
4029 static Str255 s_name;
4030 int done;
4031 char *p;
4033 /* Handle the root directory containing the mounted volumes. Call
4034 PBHGetVInfo specifying an index to obtain the info for a volume.
4035 PBHGetVInfo returns an error when it receives an index beyond the
4036 last volume, at which time we should return a nil dirent struct
4037 pointer. */
4038 if (dp->getting_volumes)
4040 hpblock.volumeParam.ioNamePtr = s_name;
4041 hpblock.volumeParam.ioVRefNum = 0;
4042 hpblock.volumeParam.ioVolIndex = dp->current_index;
4044 errno = PBHGetVInfo (&hpblock, false);
4045 if (errno != noErr)
4047 errno = ENOENT;
4048 return 0;
4051 p2cstr (s_name);
4052 strcat (s_name, "/"); /* need "/" for stat to work correctly */
4054 dp->current_index++;
4056 s_dirent.d_ino = hpblock.volumeParam.ioVRefNum;
4057 s_dirent.d_name = s_name;
4059 return &s_dirent;
4061 else
4063 cipb.hFileInfo.ioVRefNum = dp->vol_ref_num;
4064 cipb.hFileInfo.ioNamePtr = s_name;
4065 /* location to receive filename returned */
4067 /* return only visible files */
4068 done = false;
4069 while (!done)
4071 cipb.hFileInfo.ioDirID = dp->dir_id;
4072 /* directory ID found by opendir */
4073 cipb.hFileInfo.ioFDirIndex = dp->current_index;
4075 errno = PBGetCatInfo (&cipb, false);
4076 if (errno != noErr)
4078 errno = ENOENT;
4079 return 0;
4082 /* insist on a visible entry */
4083 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* directory? */
4084 done = !(cipb.dirInfo.ioDrUsrWds.frFlags & fInvisible);
4085 else
4086 done = !(cipb.hFileInfo.ioFlFndrInfo.fdFlags & fInvisible);
4088 dp->current_index++;
4091 p2cstr (s_name);
4093 p = s_name;
4094 while (*p)
4096 if (*p == '/')
4097 *p = ':';
4098 p++;
4101 s_dirent.d_ino = cipb.dirInfo.ioDrDirID;
4102 /* value unimportant: non-zero for valid file */
4103 s_dirent.d_name = s_name;
4105 return &s_dirent;
4110 char *
4111 getwd (char *path)
4113 char mac_pathname[MAXPATHLEN+1];
4114 Str255 directory_name;
4115 OSErr errno;
4116 CInfoPBRec cipb;
4118 if (path_from_vol_dir_name (mac_pathname, 255, 0, 0, "\p") == 0)
4119 return NULL;
4121 if (mac_to_posix_pathname (mac_pathname, path, MAXPATHLEN+1) == 0)
4122 return 0;
4123 else
4124 return path;
4127 #endif /* ! MAC_OSX */
4130 void
4131 initialize_applescript ()
4133 AEDesc null_desc;
4134 OSAError osaerror;
4136 /* if open fails, as_scripting_component is set to NULL. Its
4137 subsequent use in OSA calls will fail with badComponentInstance
4138 error. */
4139 as_scripting_component = OpenDefaultComponent (kOSAComponentType,
4140 kAppleScriptSubtype);
4142 null_desc.descriptorType = typeNull;
4143 null_desc.dataHandle = 0;
4144 osaerror = OSAMakeContext (as_scripting_component, &null_desc,
4145 kOSANullScript, &as_script_context);
4146 if (osaerror)
4147 as_script_context = kOSANullScript;
4148 /* use default context if create fails */
4152 void
4153 terminate_applescript()
4155 OSADispose (as_scripting_component, as_script_context);
4156 CloseComponent (as_scripting_component);
4159 /* Convert a lisp string to the 4 byte character code. */
4161 OSType
4162 mac_get_code_from_arg(Lisp_Object arg, OSType defCode)
4164 OSType result;
4165 if (NILP(arg))
4167 result = defCode;
4169 else
4171 /* check type string */
4172 CHECK_STRING(arg);
4173 if (SBYTES (arg) != 4)
4175 error ("Wrong argument: need string of length 4 for code");
4177 result = EndianU32_BtoN (*((UInt32 *) SDATA (arg)));
4179 return result;
4182 /* Convert the 4 byte character code into a 4 byte string. */
4184 Lisp_Object
4185 mac_get_object_from_code(OSType defCode)
4187 UInt32 code = EndianU32_NtoB (defCode);
4189 return make_unibyte_string ((char *)&code, 4);
4193 DEFUN ("mac-get-file-creator", Fmac_get_file_creator, Smac_get_file_creator, 1, 1, 0,
4194 doc: /* Get the creator code of FILENAME as a four character string. */)
4195 (filename)
4196 Lisp_Object filename;
4198 OSStatus status;
4199 #ifdef MAC_OSX
4200 FSRef fref;
4201 #else
4202 FSSpec fss;
4203 #endif
4204 Lisp_Object result = Qnil;
4205 CHECK_STRING (filename);
4207 if (NILP(Ffile_exists_p(filename)) || !NILP(Ffile_directory_p(filename))) {
4208 return Qnil;
4210 filename = Fexpand_file_name (filename, Qnil);
4212 BLOCK_INPUT;
4213 #ifdef MAC_OSX
4214 status = FSPathMakeRef(SDATA(ENCODE_FILE(filename)), &fref, NULL);
4215 #else
4216 status = posix_pathname_to_fsspec (SDATA (ENCODE_FILE (filename)), &fss);
4217 #endif
4219 if (status == noErr)
4221 #ifdef MAC_OSX
4222 FSCatalogInfo catalogInfo;
4224 status = FSGetCatalogInfo(&fref, kFSCatInfoFinderInfo,
4225 &catalogInfo, NULL, NULL, NULL);
4226 #else
4227 FInfo finder_info;
4229 status = FSpGetFInfo (&fss, &finder_info);
4230 #endif
4231 if (status == noErr)
4233 #ifdef MAC_OSX
4234 result = mac_get_object_from_code(((FileInfo*)&catalogInfo.finderInfo)->fileCreator);
4235 #else
4236 result = mac_get_object_from_code (finder_info.fdCreator);
4237 #endif
4240 UNBLOCK_INPUT;
4241 if (status != noErr) {
4242 error ("Error while getting file information.");
4244 return result;
4247 DEFUN ("mac-get-file-type", Fmac_get_file_type, Smac_get_file_type, 1, 1, 0,
4248 doc: /* Get the type code of FILENAME as a four character string. */)
4249 (filename)
4250 Lisp_Object filename;
4252 OSStatus status;
4253 #ifdef MAC_OSX
4254 FSRef fref;
4255 #else
4256 FSSpec fss;
4257 #endif
4258 Lisp_Object result = Qnil;
4259 CHECK_STRING (filename);
4261 if (NILP(Ffile_exists_p(filename)) || !NILP(Ffile_directory_p(filename))) {
4262 return Qnil;
4264 filename = Fexpand_file_name (filename, Qnil);
4266 BLOCK_INPUT;
4267 #ifdef MAC_OSX
4268 status = FSPathMakeRef(SDATA(ENCODE_FILE(filename)), &fref, NULL);
4269 #else
4270 status = posix_pathname_to_fsspec (SDATA (ENCODE_FILE (filename)), &fss);
4271 #endif
4273 if (status == noErr)
4275 #ifdef MAC_OSX
4276 FSCatalogInfo catalogInfo;
4278 status = FSGetCatalogInfo(&fref, kFSCatInfoFinderInfo,
4279 &catalogInfo, NULL, NULL, NULL);
4280 #else
4281 FInfo finder_info;
4283 status = FSpGetFInfo (&fss, &finder_info);
4284 #endif
4285 if (status == noErr)
4287 #ifdef MAC_OSX
4288 result = mac_get_object_from_code(((FileInfo*)&catalogInfo.finderInfo)->fileType);
4289 #else
4290 result = mac_get_object_from_code (finder_info.fdType);
4291 #endif
4294 UNBLOCK_INPUT;
4295 if (status != noErr) {
4296 error ("Error while getting file information.");
4298 return result;
4301 DEFUN ("mac-set-file-creator", Fmac_set_file_creator, Smac_set_file_creator, 1, 2, 0,
4302 doc: /* Set creator code of file FILENAME to CODE.
4303 If non-nil, CODE must be a 4-character string. Otherwise, 'EMAx' is
4304 assumed. Return non-nil if successful. */)
4305 (filename, code)
4306 Lisp_Object filename, code;
4308 OSStatus status;
4309 #ifdef MAC_OSX
4310 FSRef fref;
4311 #else
4312 FSSpec fss;
4313 #endif
4314 OSType cCode;
4315 CHECK_STRING (filename);
4317 cCode = mac_get_code_from_arg(code, MAC_EMACS_CREATOR_CODE);
4319 if (NILP(Ffile_exists_p(filename)) || !NILP(Ffile_directory_p(filename))) {
4320 return Qnil;
4322 filename = Fexpand_file_name (filename, Qnil);
4324 BLOCK_INPUT;
4325 #ifdef MAC_OSX
4326 status = FSPathMakeRef(SDATA(ENCODE_FILE(filename)), &fref, NULL);
4327 #else
4328 status = posix_pathname_to_fsspec (SDATA (ENCODE_FILE (filename)), &fss);
4329 #endif
4331 if (status == noErr)
4333 #ifdef MAC_OSX
4334 FSCatalogInfo catalogInfo;
4335 FSRef parentDir;
4336 status = FSGetCatalogInfo(&fref, kFSCatInfoFinderInfo,
4337 &catalogInfo, NULL, NULL, &parentDir);
4338 #else
4339 FInfo finder_info;
4341 status = FSpGetFInfo (&fss, &finder_info);
4342 #endif
4343 if (status == noErr)
4345 #ifdef MAC_OSX
4346 ((FileInfo*)&catalogInfo.finderInfo)->fileCreator = cCode;
4347 status = FSSetCatalogInfo(&fref, kFSCatInfoFinderInfo, &catalogInfo);
4348 /* TODO: on Mac OS 10.2, we need to touch the parent dir, FNNotify? */
4349 #else
4350 finder_info.fdCreator = cCode;
4351 status = FSpSetFInfo (&fss, &finder_info);
4352 #endif
4355 UNBLOCK_INPUT;
4356 if (status != noErr) {
4357 error ("Error while setting creator information.");
4359 return Qt;
4362 DEFUN ("mac-set-file-type", Fmac_set_file_type, Smac_set_file_type, 2, 2, 0,
4363 doc: /* Set file code of file FILENAME to CODE.
4364 CODE must be a 4-character string. Return non-nil if successful. */)
4365 (filename, code)
4366 Lisp_Object filename, code;
4368 OSStatus status;
4369 #ifdef MAC_OSX
4370 FSRef fref;
4371 #else
4372 FSSpec fss;
4373 #endif
4374 OSType cCode;
4375 CHECK_STRING (filename);
4377 cCode = mac_get_code_from_arg(code, 0); /* Default to empty code*/
4379 if (NILP(Ffile_exists_p(filename)) || !NILP(Ffile_directory_p(filename))) {
4380 return Qnil;
4382 filename = Fexpand_file_name (filename, Qnil);
4384 BLOCK_INPUT;
4385 #ifdef MAC_OSX
4386 status = FSPathMakeRef(SDATA(ENCODE_FILE(filename)), &fref, NULL);
4387 #else
4388 status = posix_pathname_to_fsspec (SDATA (ENCODE_FILE (filename)), &fss);
4389 #endif
4391 if (status == noErr)
4393 #ifdef MAC_OSX
4394 FSCatalogInfo catalogInfo;
4395 FSRef parentDir;
4396 status = FSGetCatalogInfo(&fref, kFSCatInfoFinderInfo,
4397 &catalogInfo, NULL, NULL, &parentDir);
4398 #else
4399 FInfo finder_info;
4401 status = FSpGetFInfo (&fss, &finder_info);
4402 #endif
4403 if (status == noErr)
4405 #ifdef MAC_OSX
4406 ((FileInfo*)&catalogInfo.finderInfo)->fileType = cCode;
4407 status = FSSetCatalogInfo(&fref, kFSCatInfoFinderInfo, &catalogInfo);
4408 /* TODO: on Mac OS 10.2, we need to touch the parent dir, FNNotify? */
4409 #else
4410 finder_info.fdType = cCode;
4411 status = FSpSetFInfo (&fss, &finder_info);
4412 #endif
4415 UNBLOCK_INPUT;
4416 if (status != noErr) {
4417 error ("Error while setting creator information.");
4419 return Qt;
4423 /* Compile and execute the AppleScript SCRIPT and return the error
4424 status as function value. A zero is returned if compilation and
4425 execution is successful, in which case *RESULT is set to a Lisp
4426 string containing the resulting script value. Otherwise, the Mac
4427 error code is returned and *RESULT is set to an error Lisp string.
4428 For documentation on the MacOS scripting architecture, see Inside
4429 Macintosh - Interapplication Communications: Scripting
4430 Components. */
4432 static long
4433 do_applescript (script, result)
4434 Lisp_Object script, *result;
4436 AEDesc script_desc, result_desc, error_desc, *desc = NULL;
4437 OSErr error;
4438 OSAError osaerror;
4440 *result = Qnil;
4442 if (!as_scripting_component)
4443 initialize_applescript();
4445 error = AECreateDesc (typeChar, SDATA (script), SBYTES (script),
4446 &script_desc);
4447 if (error)
4448 return error;
4450 osaerror = OSADoScript (as_scripting_component, &script_desc, kOSANullScript,
4451 typeChar, kOSAModeNull, &result_desc);
4453 if (osaerror == noErr)
4454 /* success: retrieve resulting script value */
4455 desc = &result_desc;
4456 else if (osaerror == errOSAScriptError)
4457 /* error executing AppleScript: retrieve error message */
4458 if (!OSAScriptError (as_scripting_component, kOSAErrorMessage, typeChar,
4459 &error_desc))
4460 desc = &error_desc;
4462 if (desc)
4464 #if TARGET_API_MAC_CARBON
4465 *result = make_uninit_string (AEGetDescDataSize (desc));
4466 AEGetDescData (desc, SDATA (*result), SBYTES (*result));
4467 #else /* not TARGET_API_MAC_CARBON */
4468 *result = make_uninit_string (GetHandleSize (desc->dataHandle));
4469 memcpy (SDATA (*result), *(desc->dataHandle), SBYTES (*result));
4470 #endif /* not TARGET_API_MAC_CARBON */
4471 AEDisposeDesc (desc);
4474 AEDisposeDesc (&script_desc);
4476 return osaerror;
4480 DEFUN ("do-applescript", Fdo_applescript, Sdo_applescript, 1, 1, 0,
4481 doc: /* Compile and execute AppleScript SCRIPT and return the result.
4482 If compilation and execution are successful, the resulting script
4483 value is returned as a string. Otherwise the function aborts and
4484 displays the error message returned by the AppleScript scripting
4485 component. */)
4486 (script)
4487 Lisp_Object script;
4489 Lisp_Object result;
4490 long status;
4492 CHECK_STRING (script);
4494 BLOCK_INPUT;
4495 status = do_applescript (script, &result);
4496 UNBLOCK_INPUT;
4497 if (status == 0)
4498 return result;
4499 else if (!STRINGP (result))
4500 error ("AppleScript error %d", status);
4501 else
4502 error ("%s", SDATA (result));
4506 DEFUN ("mac-file-name-to-posix", Fmac_file_name_to_posix,
4507 Smac_file_name_to_posix, 1, 1, 0,
4508 doc: /* Convert Macintosh FILENAME to Posix form. */)
4509 (filename)
4510 Lisp_Object filename;
4512 char posix_filename[MAXPATHLEN+1];
4514 CHECK_STRING (filename);
4516 if (mac_to_posix_pathname (SDATA (filename), posix_filename, MAXPATHLEN))
4517 return build_string (posix_filename);
4518 else
4519 return Qnil;
4523 DEFUN ("posix-file-name-to-mac", Fposix_file_name_to_mac,
4524 Sposix_file_name_to_mac, 1, 1, 0,
4525 doc: /* Convert Posix FILENAME to Mac form. */)
4526 (filename)
4527 Lisp_Object filename;
4529 char mac_filename[MAXPATHLEN+1];
4531 CHECK_STRING (filename);
4533 if (posix_to_mac_pathname (SDATA (filename), mac_filename, MAXPATHLEN))
4534 return build_string (mac_filename);
4535 else
4536 return Qnil;
4540 DEFUN ("mac-coerce-ae-data", Fmac_coerce_ae_data, Smac_coerce_ae_data, 3, 3, 0,
4541 doc: /* Coerce Apple event data SRC-DATA of type SRC-TYPE to DST-TYPE.
4542 Each type should be a string of length 4 or the symbol
4543 `undecoded-file-name'. */)
4544 (src_type, src_data, dst_type)
4545 Lisp_Object src_type, src_data, dst_type;
4547 OSErr err;
4548 Lisp_Object result = Qnil;
4549 DescType src_desc_type, dst_desc_type;
4550 AEDesc dst_desc;
4552 CHECK_STRING (src_data);
4553 if (EQ (src_type, Qundecoded_file_name))
4554 src_desc_type = TYPE_FILE_NAME;
4555 else
4556 src_desc_type = mac_get_code_from_arg (src_type, 0);
4558 if (EQ (dst_type, Qundecoded_file_name))
4559 dst_desc_type = TYPE_FILE_NAME;
4560 else
4561 dst_desc_type = mac_get_code_from_arg (dst_type, 0);
4563 BLOCK_INPUT;
4564 err = AECoercePtr (src_desc_type, SDATA (src_data), SBYTES (src_data),
4565 dst_desc_type, &dst_desc);
4566 if (err == noErr)
4568 result = Fcdr (mac_aedesc_to_lisp (&dst_desc));
4569 AEDisposeDesc (&dst_desc);
4571 UNBLOCK_INPUT;
4573 return result;
4577 #if TARGET_API_MAC_CARBON
4578 static Lisp_Object Qxml, Qmime_charset;
4579 static Lisp_Object QNFD, QNFKD, QNFC, QNFKC, QHFS_plus_D, QHFS_plus_C;
4581 DEFUN ("mac-get-preference", Fmac_get_preference, Smac_get_preference, 1, 4, 0,
4582 doc: /* Return the application preference value for KEY.
4583 KEY is either a string specifying a preference key, or a list of key
4584 strings. If it is a list, the (i+1)-th element is used as a key for
4585 the CFDictionary value obtained by the i-th element. Return nil if
4586 lookup is failed at some stage.
4588 Optional arg APPLICATION is an application ID string. If omitted or
4589 nil, that stands for the current application.
4591 Optional arg FORMAT specifies the data format of the return value. If
4592 omitted or nil, each Core Foundation object is converted into a
4593 corresponding Lisp object as follows:
4595 Core Foundation Lisp Tag
4596 ------------------------------------------------------------
4597 CFString Multibyte string string
4598 CFNumber Integer or float number
4599 CFBoolean Symbol (t or nil) boolean
4600 CFDate List of three integers date
4601 (cf. `current-time')
4602 CFData Unibyte string data
4603 CFArray Vector array
4604 CFDictionary Alist or hash table dictionary
4605 (depending on HASH-BOUND)
4607 If it is t, a symbol that represents the type of the original Core
4608 Foundation object is prepended. If it is `xml', the value is returned
4609 as an XML representation.
4611 Optional arg HASH-BOUND specifies which kinds of the list objects,
4612 alists or hash tables, are used as the targets of the conversion from
4613 CFDictionary. If HASH-BOUND is a negative integer or nil, always
4614 generate alists. If HASH-BOUND >= 0, generate an alist if the number
4615 of keys in the dictionary is smaller than HASH-BOUND, and a hash table
4616 otherwise. */)
4617 (key, application, format, hash_bound)
4618 Lisp_Object key, application, format, hash_bound;
4620 CFStringRef app_id, key_str;
4621 CFPropertyListRef app_plist = NULL, plist;
4622 Lisp_Object result = Qnil, tmp;
4623 struct gcpro gcpro1, gcpro2;
4625 if (STRINGP (key))
4626 key = Fcons (key, Qnil);
4627 else
4629 CHECK_CONS (key);
4630 for (tmp = key; CONSP (tmp); tmp = XCDR (tmp))
4631 CHECK_STRING_CAR (tmp);
4632 CHECK_LIST_END (tmp, key);
4634 if (!NILP (application))
4635 CHECK_STRING (application);
4636 CHECK_SYMBOL (format);
4637 if (!NILP (hash_bound))
4638 CHECK_NUMBER (hash_bound);
4640 GCPRO2 (key, format);
4642 BLOCK_INPUT;
4644 app_id = kCFPreferencesCurrentApplication;
4645 if (!NILP (application))
4647 app_id = cfstring_create_with_string (application);
4648 if (app_id == NULL)
4649 goto out;
4651 if (!CFPreferencesAppSynchronize (app_id))
4652 goto out;
4654 key_str = cfstring_create_with_string (XCAR (key));
4655 if (key_str == NULL)
4656 goto out;
4657 app_plist = CFPreferencesCopyAppValue (key_str, app_id);
4658 CFRelease (key_str);
4659 if (app_plist == NULL)
4660 goto out;
4662 plist = app_plist;
4663 for (key = XCDR (key); CONSP (key); key = XCDR (key))
4665 if (CFGetTypeID (plist) != CFDictionaryGetTypeID ())
4666 break;
4667 key_str = cfstring_create_with_string (XCAR (key));
4668 if (key_str == NULL)
4669 goto out;
4670 plist = CFDictionaryGetValue (plist, key_str);
4671 CFRelease (key_str);
4672 if (plist == NULL)
4673 goto out;
4676 if (NILP (key))
4678 if (EQ (format, Qxml))
4680 CFDataRef data = CFPropertyListCreateXMLData (NULL, plist);
4681 if (data == NULL)
4682 goto out;
4683 result = cfdata_to_lisp (data);
4684 CFRelease (data);
4686 else
4687 result =
4688 cfproperty_list_to_lisp (plist, EQ (format, Qt),
4689 NILP (hash_bound) ? -1 : XINT (hash_bound));
4692 out:
4693 if (app_plist)
4694 CFRelease (app_plist);
4695 CFRelease (app_id);
4697 UNBLOCK_INPUT;
4699 UNGCPRO;
4701 return result;
4705 static CFStringEncoding
4706 get_cfstring_encoding_from_lisp (obj)
4707 Lisp_Object obj;
4709 CFStringRef iana_name;
4710 CFStringEncoding encoding = kCFStringEncodingInvalidId;
4712 if (NILP (obj))
4713 return kCFStringEncodingUnicode;
4715 if (INTEGERP (obj))
4716 return XINT (obj);
4718 if (SYMBOLP (obj) && !NILP (Fcoding_system_p (obj)))
4720 Lisp_Object coding_spec, plist;
4722 coding_spec = Fget (obj, Qcoding_system);
4723 plist = XVECTOR (coding_spec)->contents[3];
4724 obj = Fplist_get (XVECTOR (coding_spec)->contents[3], Qmime_charset);
4727 if (SYMBOLP (obj))
4728 obj = SYMBOL_NAME (obj);
4730 if (STRINGP (obj))
4732 iana_name = cfstring_create_with_string (obj);
4733 if (iana_name)
4735 encoding = CFStringConvertIANACharSetNameToEncoding (iana_name);
4736 CFRelease (iana_name);
4740 return encoding;
4743 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
4744 static CFStringRef
4745 cfstring_create_normalized (str, symbol)
4746 CFStringRef str;
4747 Lisp_Object symbol;
4749 int form = -1;
4750 TextEncodingVariant variant;
4751 float initial_mag = 0.0;
4752 CFStringRef result = NULL;
4754 if (EQ (symbol, QNFD))
4755 form = kCFStringNormalizationFormD;
4756 else if (EQ (symbol, QNFKD))
4757 form = kCFStringNormalizationFormKD;
4758 else if (EQ (symbol, QNFC))
4759 form = kCFStringNormalizationFormC;
4760 else if (EQ (symbol, QNFKC))
4761 form = kCFStringNormalizationFormKC;
4762 else if (EQ (symbol, QHFS_plus_D))
4764 variant = kUnicodeHFSPlusDecompVariant;
4765 initial_mag = 1.5;
4767 else if (EQ (symbol, QHFS_plus_C))
4769 variant = kUnicodeHFSPlusCompVariant;
4770 initial_mag = 1.0;
4773 if (form >= 0)
4775 CFMutableStringRef mut_str = CFStringCreateMutableCopy (NULL, 0, str);
4777 if (mut_str)
4779 CFStringNormalize (mut_str, form);
4780 result = mut_str;
4783 else if (initial_mag > 0.0)
4785 UnicodeToTextInfo uni = NULL;
4786 UnicodeMapping map;
4787 CFIndex length;
4788 UniChar *in_text, *buffer = NULL, *out_buf = NULL;
4789 OSStatus err = noErr;
4790 ByteCount out_read, out_size, out_len;
4792 map.unicodeEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault,
4793 kUnicodeNoSubset,
4794 kTextEncodingDefaultFormat);
4795 map.otherEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault,
4796 variant,
4797 kTextEncodingDefaultFormat);
4798 map.mappingVersion = kUnicodeUseLatestMapping;
4800 length = CFStringGetLength (str);
4801 out_size = (int)((float)length * initial_mag) * sizeof (UniChar);
4802 if (out_size < 32)
4803 out_size = 32;
4805 in_text = (UniChar *)CFStringGetCharactersPtr (str);
4806 if (in_text == NULL)
4808 buffer = xmalloc (sizeof (UniChar) * length);
4809 CFStringGetCharacters (str, CFRangeMake (0, length), buffer);
4810 in_text = buffer;
4813 if (in_text)
4814 err = CreateUnicodeToTextInfo (&map, &uni);
4815 while (err == noErr)
4817 out_buf = xmalloc (out_size);
4818 err = ConvertFromUnicodeToText (uni, length * sizeof (UniChar),
4819 in_text,
4820 kUnicodeDefaultDirectionMask,
4821 0, NULL, NULL, NULL,
4822 out_size, &out_read, &out_len,
4823 out_buf);
4824 if (err == noErr && out_read < length * sizeof (UniChar))
4826 xfree (out_buf);
4827 out_size += length;
4829 else
4830 break;
4832 if (err == noErr)
4833 result = CFStringCreateWithCharacters (NULL, out_buf,
4834 out_len / sizeof (UniChar));
4835 if (uni)
4836 DisposeUnicodeToTextInfo (&uni);
4837 if (out_buf)
4838 xfree (out_buf);
4839 if (buffer)
4840 xfree (buffer);
4842 else
4844 result = str;
4845 CFRetain (result);
4848 return result;
4850 #endif
4852 DEFUN ("mac-code-convert-string", Fmac_code_convert_string, Smac_code_convert_string, 3, 4, 0,
4853 doc: /* Convert STRING from SOURCE encoding to TARGET encoding.
4854 The conversion is performed using the converter provided by the system.
4855 Each encoding is specified by either a coding system symbol, a mime
4856 charset string, or an integer as a CFStringEncoding value. An encoding
4857 of nil means UTF-16 in native byte order, no byte order mark.
4858 On Mac OS X 10.2 and later, you can do Unicode Normalization by
4859 specifying the optional argument NORMALIZATION-FORM with a symbol NFD,
4860 NFKD, NFC, NFKC, HFS+D, or HFS+C.
4861 On successful conversion, return the result string, else return nil. */)
4862 (string, source, target, normalization_form)
4863 Lisp_Object string, source, target, normalization_form;
4865 Lisp_Object result = Qnil;
4866 struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
4867 CFStringEncoding src_encoding, tgt_encoding;
4868 CFStringRef str = NULL;
4870 CHECK_STRING (string);
4871 if (!INTEGERP (source) && !STRINGP (source))
4872 CHECK_SYMBOL (source);
4873 if (!INTEGERP (target) && !STRINGP (target))
4874 CHECK_SYMBOL (target);
4875 CHECK_SYMBOL (normalization_form);
4877 GCPRO4 (string, source, target, normalization_form);
4879 BLOCK_INPUT;
4881 src_encoding = get_cfstring_encoding_from_lisp (source);
4882 tgt_encoding = get_cfstring_encoding_from_lisp (target);
4884 /* We really want string_to_unibyte, but since it doesn't exist yet, we
4885 use string_as_unibyte which works as well, except for the fact that
4886 it's too permissive (it doesn't check that the multibyte string only
4887 contain single-byte chars). */
4888 string = Fstring_as_unibyte (string);
4889 if (src_encoding != kCFStringEncodingInvalidId
4890 && tgt_encoding != kCFStringEncodingInvalidId)
4891 str = CFStringCreateWithBytes (NULL, SDATA (string), SBYTES (string),
4892 src_encoding, !NILP (source));
4893 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
4894 if (str)
4896 CFStringRef saved_str = str;
4898 str = cfstring_create_normalized (saved_str, normalization_form);
4899 CFRelease (saved_str);
4901 #endif
4902 if (str)
4904 CFIndex str_len, buf_len;
4906 str_len = CFStringGetLength (str);
4907 if (CFStringGetBytes (str, CFRangeMake (0, str_len), tgt_encoding, 0,
4908 !NILP (target), NULL, 0, &buf_len) == str_len)
4910 result = make_uninit_string (buf_len);
4911 CFStringGetBytes (str, CFRangeMake (0, str_len), tgt_encoding, 0,
4912 !NILP (target), SDATA (result), buf_len, NULL);
4914 CFRelease (str);
4917 UNBLOCK_INPUT;
4919 UNGCPRO;
4921 return result;
4924 DEFUN ("mac-process-hi-command", Fmac_process_hi_command, Smac_process_hi_command, 1, 1, 0,
4925 doc: /* Send a HI command whose ID is COMMAND-ID to the command chain.
4926 COMMAND-ID must be a 4-character string. Some common command IDs are
4927 defined in the Carbon Event Manager. */)
4928 (command_id)
4929 Lisp_Object command_id;
4931 OSStatus err;
4932 HICommand command;
4934 bzero (&command, sizeof (HICommand));
4935 command.commandID = mac_get_code_from_arg (command_id, 0);
4937 BLOCK_INPUT;
4938 err = ProcessHICommand (&command);
4939 UNBLOCK_INPUT;
4941 if (err != noErr)
4942 error ("HI command (command ID: '%s') not handled.", SDATA (command_id));
4944 return Qnil;
4947 #endif /* TARGET_API_MAC_CARBON */
4950 static Lisp_Object
4951 mac_get_system_locale ()
4953 OSStatus err;
4954 LangCode lang;
4955 RegionCode region;
4956 LocaleRef locale;
4957 Str255 str;
4959 lang = GetScriptVariable (smSystemScript, smScriptLang);
4960 region = GetScriptManagerVariable (smRegionCode);
4961 err = LocaleRefFromLangOrRegionCode (lang, region, &locale);
4962 if (err == noErr)
4963 err = LocaleRefGetPartString (locale, kLocaleAllPartsMask,
4964 sizeof (str), str);
4965 if (err == noErr)
4966 return build_string (str);
4967 else
4968 return Qnil;
4972 #ifdef MAC_OSX
4974 extern int inhibit_window_system;
4975 extern int noninteractive;
4977 /* Unlike in X11, window events in Carbon do not come from sockets.
4978 So we cannot simply use `select' to monitor two kinds of inputs:
4979 window events and process outputs. We emulate such functionality
4980 by regarding fd 0 as the window event channel and simultaneously
4981 monitoring both kinds of input channels. It is implemented by
4982 dividing into some cases:
4983 1. The window event channel is not involved.
4984 -> Use `select'.
4985 2. Sockets are not involved.
4986 -> Use ReceiveNextEvent.
4987 3. [If SELECT_USE_CFSOCKET is set]
4988 Only the window event channel and socket read/write channels are
4989 involved, and timeout is not too short (greater than
4990 SELECT_TIMEOUT_THRESHOLD_RUNLOOP seconds).
4991 -> Create CFSocket for each socket and add it into the current
4992 event RunLoop so that the current event loop gets quit when
4993 the socket becomes ready. Then ReceiveNextEvent can wait for
4994 both kinds of inputs.
4995 4. Otherwise.
4996 -> Periodically poll the window input channel while repeatedly
4997 executing `select' with a short timeout
4998 (SELECT_POLLING_PERIOD_USEC microseconds). */
5000 #ifndef SELECT_USE_CFSOCKET
5001 #define SELECT_USE_CFSOCKET 1
5002 #endif
5004 #define SELECT_POLLING_PERIOD_USEC 100000
5005 #if SELECT_USE_CFSOCKET
5006 #define SELECT_TIMEOUT_THRESHOLD_RUNLOOP 0.2
5008 static void
5009 socket_callback (s, type, address, data, info)
5010 CFSocketRef s;
5011 CFSocketCallBackType type;
5012 CFDataRef address;
5013 const void *data;
5014 void *info;
5016 int fd = CFSocketGetNative (s);
5017 SELECT_TYPE *ofds = (SELECT_TYPE *)info;
5019 if ((type == kCFSocketReadCallBack && FD_ISSET (fd, &ofds[0]))
5020 || (type == kCFSocketConnectCallBack && FD_ISSET (fd, &ofds[1])))
5021 QuitEventLoop (GetCurrentEventLoop ());
5023 #endif /* SELECT_USE_CFSOCKET */
5025 static int
5026 select_and_poll_event (nfds, rfds, wfds, efds, timeout)
5027 int nfds;
5028 SELECT_TYPE *rfds, *wfds, *efds;
5029 EMACS_TIME *timeout;
5031 OSStatus err = noErr;
5032 int r = 0;
5034 /* Try detect_input_pending before ReceiveNextEvent in the same
5035 BLOCK_INPUT block, in case that some input has already been read
5036 asynchronously. */
5037 BLOCK_INPUT;
5038 ENABLE_WAKEUP_FROM_RNE;
5039 if (!detect_input_pending ())
5041 EMACS_TIME select_timeout;
5042 EventTimeout timeoutval =
5043 (timeout
5044 ? (EMACS_SECS (*timeout) * kEventDurationSecond
5045 + EMACS_USECS (*timeout) * kEventDurationMicrosecond)
5046 : kEventDurationForever);
5048 EMACS_SET_SECS_USECS (select_timeout, 0, 0);
5049 r = select (nfds, rfds, wfds, efds, &select_timeout);
5050 if (timeoutval == 0.0)
5051 err = eventLoopTimedOutErr;
5052 else if (r == 0)
5054 #if USE_CG_DRAWING
5055 mac_prepare_for_quickdraw (NULL);
5056 #endif
5057 err = ReceiveNextEvent (0, NULL, timeoutval,
5058 kEventLeaveInQueue, NULL);
5061 DISABLE_WAKEUP_FROM_RNE;
5062 UNBLOCK_INPUT;
5064 if (r != 0)
5065 return r;
5066 else if (err == noErr)
5068 /* Pretend that `select' is interrupted by a signal. */
5069 detect_input_pending ();
5070 errno = EINTR;
5071 return -1;
5073 else
5074 return 0;
5078 sys_select (nfds, rfds, wfds, efds, timeout)
5079 int nfds;
5080 SELECT_TYPE *rfds, *wfds, *efds;
5081 EMACS_TIME *timeout;
5083 OSStatus err = noErr;
5084 int r;
5085 EMACS_TIME select_timeout;
5086 static SELECT_TYPE ofds[3];
5088 if (inhibit_window_system || noninteractive
5089 || nfds < 1 || rfds == NULL || !FD_ISSET (0, rfds))
5090 return select (nfds, rfds, wfds, efds, timeout);
5092 FD_CLR (0, rfds);
5093 ofds[0] = *rfds;
5095 if (wfds)
5096 ofds[1] = *wfds;
5097 else
5098 FD_ZERO (&ofds[1]);
5100 if (efds)
5101 ofds[2] = *efds;
5102 else
5104 EventTimeout timeoutval =
5105 (timeout
5106 ? (EMACS_SECS (*timeout) * kEventDurationSecond
5107 + EMACS_USECS (*timeout) * kEventDurationMicrosecond)
5108 : kEventDurationForever);
5110 FD_SET (0, rfds); /* sentinel */
5113 nfds--;
5115 while (!(FD_ISSET (nfds, rfds) || (wfds && FD_ISSET (nfds, wfds))));
5116 nfds++;
5117 FD_CLR (0, rfds);
5119 if (nfds == 1)
5120 return select_and_poll_event (nfds, rfds, wfds, efds, timeout);
5122 /* Avoid initial overhead of RunLoop setup for the case that
5123 some input is already available. */
5124 EMACS_SET_SECS_USECS (select_timeout, 0, 0);
5125 r = select_and_poll_event (nfds, rfds, wfds, efds, &select_timeout);
5126 if (r != 0 || timeoutval == 0.0)
5127 return r;
5129 *rfds = ofds[0];
5130 if (wfds)
5131 *wfds = ofds[1];
5133 #if SELECT_USE_CFSOCKET
5134 if (timeoutval > 0 && timeoutval <= SELECT_TIMEOUT_THRESHOLD_RUNLOOP)
5135 goto poll_periodically;
5137 /* Try detect_input_pending before ReceiveNextEvent in the same
5138 BLOCK_INPUT block, in case that some input has already been
5139 read asynchronously. */
5140 BLOCK_INPUT;
5141 ENABLE_WAKEUP_FROM_RNE;
5142 if (!detect_input_pending ())
5144 int minfd, fd;
5145 CFRunLoopRef runloop =
5146 (CFRunLoopRef) GetCFRunLoopFromEventLoop (GetCurrentEventLoop ());
5147 static const CFSocketContext context = {0, ofds, NULL, NULL, NULL};
5148 static CFMutableDictionaryRef sources;
5150 if (sources == NULL)
5151 sources =
5152 CFDictionaryCreateMutable (NULL, 0, NULL,
5153 &kCFTypeDictionaryValueCallBacks);
5155 for (minfd = 1; ; minfd++) /* nfds-1 works as a sentinel. */
5156 if (FD_ISSET (minfd, rfds) || (wfds && FD_ISSET (minfd, wfds)))
5157 break;
5159 for (fd = minfd; fd < nfds; fd++)
5160 if (FD_ISSET (fd, rfds) || (wfds && FD_ISSET (fd, wfds)))
5162 void *key = (void *) fd;
5163 CFRunLoopSourceRef source =
5164 (CFRunLoopSourceRef) CFDictionaryGetValue (sources, key);
5166 if (source == NULL)
5168 CFSocketRef socket =
5169 CFSocketCreateWithNative (NULL, fd,
5170 (kCFSocketReadCallBack
5171 | kCFSocketConnectCallBack),
5172 socket_callback, &context);
5174 if (socket == NULL)
5175 continue;
5176 source = CFSocketCreateRunLoopSource (NULL, socket, 0);
5177 CFRelease (socket);
5178 if (source == NULL)
5179 continue;
5180 CFDictionaryAddValue (sources, key, source);
5181 CFRelease (source);
5183 CFRunLoopAddSource (runloop, source, kCFRunLoopDefaultMode);
5186 #if USE_CG_DRAWING
5187 mac_prepare_for_quickdraw (NULL);
5188 #endif
5189 err = ReceiveNextEvent (0, NULL, timeoutval,
5190 kEventLeaveInQueue, NULL);
5192 for (fd = minfd; fd < nfds; fd++)
5193 if (FD_ISSET (fd, rfds) || (wfds && FD_ISSET (fd, wfds)))
5195 void *key = (void *) fd;
5196 CFRunLoopSourceRef source =
5197 (CFRunLoopSourceRef) CFDictionaryGetValue (sources, key);
5199 CFRunLoopRemoveSource (runloop, source, kCFRunLoopDefaultMode);
5202 DISABLE_WAKEUP_FROM_RNE;
5203 UNBLOCK_INPUT;
5205 if (err == noErr || err == eventLoopQuitErr)
5207 EMACS_SET_SECS_USECS (select_timeout, 0, 0);
5208 return select_and_poll_event (nfds, rfds, wfds, efds,
5209 &select_timeout);
5211 else
5213 FD_ZERO (rfds);
5214 if (wfds)
5215 FD_ZERO (wfds);
5216 return 0;
5218 #endif /* SELECT_USE_CFSOCKET */
5221 poll_periodically:
5223 EMACS_TIME end_time, now, remaining_time;
5225 if (timeout)
5227 remaining_time = *timeout;
5228 EMACS_GET_TIME (now);
5229 EMACS_ADD_TIME (end_time, now, remaining_time);
5234 EMACS_SET_SECS_USECS (select_timeout, 0, SELECT_POLLING_PERIOD_USEC);
5235 if (timeout && EMACS_TIME_LT (remaining_time, select_timeout))
5236 select_timeout = remaining_time;
5237 r = select_and_poll_event (nfds, rfds, wfds, efds, &select_timeout);
5238 if (r != 0)
5239 return r;
5241 *rfds = ofds[0];
5242 if (wfds)
5243 *wfds = ofds[1];
5244 if (efds)
5245 *efds = ofds[2];
5247 if (timeout)
5249 EMACS_GET_TIME (now);
5250 EMACS_SUB_TIME (remaining_time, end_time, now);
5253 while (!timeout || EMACS_TIME_LT (now, end_time));
5255 EMACS_SET_SECS_USECS (select_timeout, 0, 0);
5256 return select_and_poll_event (nfds, rfds, wfds, efds, &select_timeout);
5260 /* Set up environment variables so that Emacs can correctly find its
5261 support files when packaged as an application bundle. Directories
5262 placed in /usr/local/share/emacs/<emacs-version>/, /usr/local/bin,
5263 and /usr/local/libexec/emacs/<emacs-version>/<system-configuration>
5264 by `make install' by default can instead be placed in
5265 .../Emacs.app/Contents/Resources/ and
5266 .../Emacs.app/Contents/MacOS/. Each of these environment variables
5267 is changed only if it is not already set. Presumably if the user
5268 sets an environment variable, he will want to use files in his path
5269 instead of ones in the application bundle. */
5270 void
5271 init_mac_osx_environment ()
5273 CFBundleRef bundle;
5274 CFURLRef bundleURL;
5275 CFStringRef cf_app_bundle_pathname;
5276 int app_bundle_pathname_len;
5277 char *app_bundle_pathname;
5278 char *p, *q;
5279 struct stat st;
5281 /* Initialize locale related variables. */
5282 mac_system_script_code =
5283 (ScriptCode) GetScriptManagerVariable (smSysScript);
5284 Vmac_system_locale = mac_get_system_locale ();
5286 /* Fetch the pathname of the application bundle as a C string into
5287 app_bundle_pathname. */
5289 bundle = CFBundleGetMainBundle ();
5290 if (!bundle || CFBundleGetIdentifier (bundle) == NULL)
5292 /* We could not find the bundle identifier. For now, prevent
5293 the fatal error by bringing it up in the terminal. */
5294 inhibit_window_system = 1;
5295 return;
5298 bundleURL = CFBundleCopyBundleURL (bundle);
5299 if (!bundleURL)
5300 return;
5302 cf_app_bundle_pathname = CFURLCopyFileSystemPath (bundleURL,
5303 kCFURLPOSIXPathStyle);
5304 app_bundle_pathname_len = CFStringGetLength (cf_app_bundle_pathname);
5305 app_bundle_pathname = (char *) alloca (app_bundle_pathname_len + 1);
5307 if (!CFStringGetCString (cf_app_bundle_pathname,
5308 app_bundle_pathname,
5309 app_bundle_pathname_len + 1,
5310 kCFStringEncodingISOLatin1))
5312 CFRelease (cf_app_bundle_pathname);
5313 return;
5316 CFRelease (cf_app_bundle_pathname);
5318 /* P should have sufficient room for the pathname of the bundle plus
5319 the subpath in it leading to the respective directories. Q
5320 should have three times that much room because EMACSLOADPATH can
5321 have the value "<path to lisp dir>:<path to leim dir>:<path to
5322 site-lisp dir>". */
5323 p = (char *) alloca (app_bundle_pathname_len + 50);
5324 q = (char *) alloca (3 * app_bundle_pathname_len + 150);
5325 if (!getenv ("EMACSLOADPATH"))
5327 q[0] = '\0';
5329 strcpy (p, app_bundle_pathname);
5330 strcat (p, "/Contents/Resources/lisp");
5331 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5332 strcat (q, p);
5334 strcpy (p, app_bundle_pathname);
5335 strcat (p, "/Contents/Resources/leim");
5336 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5338 if (q[0] != '\0')
5339 strcat (q, ":");
5340 strcat (q, p);
5343 strcpy (p, app_bundle_pathname);
5344 strcat (p, "/Contents/Resources/site-lisp");
5345 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5347 if (q[0] != '\0')
5348 strcat (q, ":");
5349 strcat (q, p);
5352 if (q[0] != '\0')
5353 setenv ("EMACSLOADPATH", q, 1);
5356 if (!getenv ("EMACSPATH"))
5358 q[0] = '\0';
5360 strcpy (p, app_bundle_pathname);
5361 strcat (p, "/Contents/MacOS/libexec");
5362 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5363 strcat (q, p);
5365 strcpy (p, app_bundle_pathname);
5366 strcat (p, "/Contents/MacOS/bin");
5367 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5369 if (q[0] != '\0')
5370 strcat (q, ":");
5371 strcat (q, p);
5374 if (q[0] != '\0')
5375 setenv ("EMACSPATH", q, 1);
5378 if (!getenv ("EMACSDATA"))
5380 strcpy (p, app_bundle_pathname);
5381 strcat (p, "/Contents/Resources/etc");
5382 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5383 setenv ("EMACSDATA", p, 1);
5386 if (!getenv ("EMACSDOC"))
5388 strcpy (p, app_bundle_pathname);
5389 strcat (p, "/Contents/Resources/etc");
5390 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5391 setenv ("EMACSDOC", p, 1);
5394 if (!getenv ("INFOPATH"))
5396 strcpy (p, app_bundle_pathname);
5397 strcat (p, "/Contents/Resources/info");
5398 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5399 setenv ("INFOPATH", p, 1);
5402 #endif /* MAC_OSX */
5404 #if TARGET_API_MAC_CARBON
5405 void
5406 mac_wakeup_from_rne ()
5408 if (wakeup_from_rne_enabled_p)
5409 /* Post a harmless event so as to wake up from
5410 ReceiveNextEvent. */
5411 mac_post_mouse_moved_event ();
5413 #endif
5415 void
5416 syms_of_mac ()
5418 Qundecoded_file_name = intern ("undecoded-file-name");
5419 staticpro (&Qundecoded_file_name);
5421 #if TARGET_API_MAC_CARBON
5422 Qstring = intern ("string"); staticpro (&Qstring);
5423 Qnumber = intern ("number"); staticpro (&Qnumber);
5424 Qboolean = intern ("boolean"); staticpro (&Qboolean);
5425 Qdate = intern ("date"); staticpro (&Qdate);
5426 Qdata = intern ("data"); staticpro (&Qdata);
5427 Qarray = intern ("array"); staticpro (&Qarray);
5428 Qdictionary = intern ("dictionary"); staticpro (&Qdictionary);
5430 Qxml = intern ("xml");
5431 staticpro (&Qxml);
5433 Qmime_charset = intern ("mime-charset");
5434 staticpro (&Qmime_charset);
5436 QNFD = intern ("NFD"); staticpro (&QNFD);
5437 QNFKD = intern ("NFKD"); staticpro (&QNFKD);
5438 QNFC = intern ("NFC"); staticpro (&QNFC);
5439 QNFKC = intern ("NFKC"); staticpro (&QNFKC);
5440 QHFS_plus_D = intern ("HFS+D"); staticpro (&QHFS_plus_D);
5441 QHFS_plus_C = intern ("HFS+C"); staticpro (&QHFS_plus_C);
5442 #endif
5445 int i;
5447 for (i = 0; i < sizeof (ae_attr_table) / sizeof (ae_attr_table[0]); i++)
5449 ae_attr_table[i].symbol = intern (ae_attr_table[i].name);
5450 staticpro (&ae_attr_table[i].symbol);
5454 defsubr (&Smac_coerce_ae_data);
5455 #if TARGET_API_MAC_CARBON
5456 defsubr (&Smac_get_preference);
5457 defsubr (&Smac_code_convert_string);
5458 defsubr (&Smac_process_hi_command);
5459 #endif
5461 defsubr (&Smac_set_file_creator);
5462 defsubr (&Smac_set_file_type);
5463 defsubr (&Smac_get_file_creator);
5464 defsubr (&Smac_get_file_type);
5465 defsubr (&Sdo_applescript);
5466 defsubr (&Smac_file_name_to_posix);
5467 defsubr (&Sposix_file_name_to_mac);
5469 DEFVAR_INT ("mac-system-script-code", &mac_system_script_code,
5470 doc: /* The system script code. */);
5471 mac_system_script_code = (ScriptCode) GetScriptManagerVariable (smSysScript);
5473 DEFVAR_LISP ("mac-system-locale", &Vmac_system_locale,
5474 doc: /* The system locale identifier string.
5475 This is not a POSIX locale ID, but an ICU locale ID. So encoding
5476 information is not included. */);
5477 Vmac_system_locale = mac_get_system_locale ();
5480 /* arch-tag: 29d30c1f-0c6b-4f88-8a6d-0558d7f9dbff
5481 (do not change this comment) */