(mouse-drag-region-1): When remapping mouse-1 to mouse-2, go back to
[bpt/emacs.git] / src / mac.c
1 /* Unix emulation routines for GNU Emacs on the Mac OS.
2 Copyright (C) 2000, 2001, 2002, 2003, 2004,
3 2005 Free Software Foundation, Inc.
4
5 This file is part of GNU Emacs.
6
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.
11
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.
16
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. */
21
22 /* Contributed by Andrew Choi (akochoi@mac.com). */
23
24 #include <config.h>
25
26 #include <stdio.h>
27 #include <errno.h>
28
29 #include "lisp.h"
30 #include "process.h"
31 #undef init_process
32 #include "systime.h"
33 #include "sysselect.h"
34 #include "blockinput.h"
35
36 #include "macterm.h"
37
38 #include "charset.h"
39 #include "coding.h"
40 #if !TARGET_API_MAC_CARBON
41 #include <Files.h>
42 #include <MacTypes.h>
43 #include <TextUtils.h>
44 #include <Folders.h>
45 #include <Resources.h>
46 #include <Aliases.h>
47 #include <FixMath.h>
48 #include <Timer.h>
49 #include <OSA.h>
50 #include <AppleScript.h>
51 #include <Scrap.h>
52 #include <Events.h>
53 #include <Processes.h>
54 #include <EPPC.h>
55 #include <MacLocales.h>
56 #include <Endian.h>
57 #endif /* not TARGET_API_MAC_CARBON */
58
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
70
71 /* The system script code. */
72 static int mac_system_script_code;
73
74 /* The system locale identifier string. */
75 static Lisp_Object Vmac_system_locale;
76
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;
81
82 static OSErr posix_pathname_to_fsspec P_ ((const char *, FSSpec *));
83 static OSErr fsspec_to_posix_pathname P_ ((const FSSpec *, char *, int));
84
85 /* When converting from Mac to Unix pathnames, /'s in folder names are
86 converted to :'s. This function, used in copying folder names,
87 performs a strncat and converts all character a to b in the copy of
88 the string s2 appended to the end of s1. */
89
90 void
91 string_cat_and_replace (char *s1, const char *s2, int n, char a, char b)
92 {
93 int l1 = strlen (s1);
94 int l2 = strlen (s2);
95 char *p = s1 + l1;
96 int i;
97
98 strncat (s1, s2, n);
99 for (i = 0; i < l2; i++)
100 {
101 if (*p == a)
102 *p = b;
103 p++;
104 }
105 }
106
107
108 /* Convert a Mac pathname to Posix form. A Mac full pathname is one
109 that does not begin with a ':' and contains at least one ':'. A Mac
110 full pathname causes a '/' to be prepended to the Posix pathname.
111 The algorithm for the rest of the pathname is as follows:
112 For each segment between two ':',
113 if it is non-null, copy as is and then add a '/' at the end,
114 otherwise, insert a "../" into the Posix pathname.
115 Returns 1 if successful; 0 if fails. */
116
117 int
118 mac_to_posix_pathname (const char *mfn, char *ufn, int ufnbuflen)
119 {
120 const char *p, *q, *pe;
121
122 strcpy (ufn, "");
123
124 if (*mfn == '\0')
125 return 1;
126
127 p = strchr (mfn, ':');
128 if (p != 0 && p != mfn) /* full pathname */
129 strcat (ufn, "/");
130
131 p = mfn;
132 if (*p == ':')
133 p++;
134
135 pe = mfn + strlen (mfn);
136 while (p < pe)
137 {
138 q = strchr (p, ':');
139 if (q)
140 {
141 if (q == p)
142 { /* two consecutive ':' */
143 if (strlen (ufn) + 3 >= ufnbuflen)
144 return 0;
145 strcat (ufn, "../");
146 }
147 else
148 {
149 if (strlen (ufn) + (q - p) + 1 >= ufnbuflen)
150 return 0;
151 string_cat_and_replace (ufn, p, q - p, '/', ':');
152 strcat (ufn, "/");
153 }
154 p = q + 1;
155 }
156 else
157 {
158 if (strlen (ufn) + (pe - p) >= ufnbuflen)
159 return 0;
160 string_cat_and_replace (ufn, p, pe - p, '/', ':');
161 /* no separator for last one */
162 p = pe;
163 }
164 }
165
166 return 1;
167 }
168
169
170 extern char *get_temp_dir_name ();
171
172
173 /* Convert a Posix pathname to Mac form. Approximately reverse of the
174 above in algorithm. */
175
176 int
177 posix_to_mac_pathname (const char *ufn, char *mfn, int mfnbuflen)
178 {
179 const char *p, *q, *pe;
180 char expanded_pathname[MAXPATHLEN+1];
181
182 strcpy (mfn, "");
183
184 if (*ufn == '\0')
185 return 1;
186
187 p = ufn;
188
189 /* Check for and handle volume names. Last comparison: strangely
190 somewhere "/.emacs" is passed. A temporary fix for now. */
191 if (*p == '/' && strchr (p+1, '/') == NULL && strcmp (p, "/.emacs") != 0)
192 {
193 if (strlen (p) + 1 > mfnbuflen)
194 return 0;
195 strcpy (mfn, p+1);
196 strcat (mfn, ":");
197 return 1;
198 }
199
200 /* expand to emacs dir found by init_emacs_passwd_dir */
201 if (strncmp (p, "~emacs/", 7) == 0)
202 {
203 struct passwd *pw = getpwnam ("emacs");
204 p += 7;
205 if (strlen (pw->pw_dir) + strlen (p) > MAXPATHLEN)
206 return 0;
207 strcpy (expanded_pathname, pw->pw_dir);
208 strcat (expanded_pathname, p);
209 p = expanded_pathname;
210 /* now p points to the pathname with emacs dir prefix */
211 }
212 else if (strncmp (p, "/tmp/", 5) == 0)
213 {
214 char *t = get_temp_dir_name ();
215 p += 5;
216 if (strlen (t) + strlen (p) > MAXPATHLEN)
217 return 0;
218 strcpy (expanded_pathname, t);
219 strcat (expanded_pathname, p);
220 p = expanded_pathname;
221 /* now p points to the pathname with emacs dir prefix */
222 }
223 else if (*p != '/') /* relative pathname */
224 strcat (mfn, ":");
225
226 if (*p == '/')
227 p++;
228
229 pe = p + strlen (p);
230 while (p < pe)
231 {
232 q = strchr (p, '/');
233 if (q)
234 {
235 if (q - p == 2 && *p == '.' && *(p+1) == '.')
236 {
237 if (strlen (mfn) + 1 >= mfnbuflen)
238 return 0;
239 strcat (mfn, ":");
240 }
241 else
242 {
243 if (strlen (mfn) + (q - p) + 1 >= mfnbuflen)
244 return 0;
245 string_cat_and_replace (mfn, p, q - p, ':', '/');
246 strcat (mfn, ":");
247 }
248 p = q + 1;
249 }
250 else
251 {
252 if (strlen (mfn) + (pe - p) >= mfnbuflen)
253 return 0;
254 string_cat_and_replace (mfn, p, pe - p, ':', '/');
255 p = pe;
256 }
257 }
258
259 return 1;
260 }
261
262 \f
263 /***********************************************************************
264 Conversions on Apple event objects
265 ***********************************************************************/
266
267 static Lisp_Object Qundecoded_file_name;
268
269 static Lisp_Object
270 mac_aelist_to_lisp (desc_list)
271 AEDescList *desc_list;
272 {
273 OSErr err;
274 long count;
275 Lisp_Object result, elem;
276 DescType desc_type;
277 Size size;
278 AEKeyword keyword;
279 AEDesc desc;
280
281 err = AECountItems (desc_list, &count);
282 if (err != noErr)
283 return Qnil;
284 result = Qnil;
285 while (count > 0)
286 {
287 err = AESizeOfNthItem (desc_list, count, &desc_type, &size);
288 if (err == noErr)
289 switch (desc_type)
290 {
291 case typeAEList:
292 case typeAERecord:
293 case typeAppleEvent:
294 err = AEGetNthDesc (desc_list, count, typeWildCard,
295 &keyword, &desc);
296 if (err != noErr)
297 break;
298 elem = mac_aelist_to_lisp (&desc);
299 AEDisposeDesc (&desc);
300 break;
301
302 default:
303 if (desc_type == typeNull)
304 elem = Qnil;
305 else
306 {
307 elem = make_uninit_string (size);
308 err = AEGetNthPtr (desc_list, count, typeWildCard, &keyword,
309 &desc_type, SDATA (elem), size, &size);
310 }
311 if (err != noErr)
312 break;
313 desc_type = EndianU32_NtoB (desc_type);
314 elem = Fcons (make_unibyte_string ((char *) &desc_type, 4), elem);
315 break;
316 }
317
318 if (err != noErr)
319 elem = Qnil;
320 else if (desc_list->descriptorType != typeAEList)
321 {
322 keyword = EndianU32_NtoB (keyword);
323 elem = Fcons (make_unibyte_string ((char *) &keyword, 4), elem);
324 }
325
326 result = Fcons (elem, result);
327 count--;
328 }
329
330 desc_type = EndianU32_NtoB (desc_list->descriptorType);
331 return Fcons (make_unibyte_string ((char *) &desc_type, 4), result);
332 }
333
334 Lisp_Object
335 mac_aedesc_to_lisp (desc)
336 AEDesc *desc;
337 {
338 OSErr err = noErr;
339 DescType desc_type = desc->descriptorType;
340 Lisp_Object result;
341
342 switch (desc_type)
343 {
344 case typeNull:
345 result = Qnil;
346 break;
347
348 case typeAEList:
349 case typeAERecord:
350 case typeAppleEvent:
351 return mac_aelist_to_lisp (desc);
352 #if 0
353 /* The following one is much simpler, but creates and disposes
354 of Apple event descriptors many times. */
355 {
356 long count;
357 Lisp_Object elem;
358 AEKeyword keyword;
359 AEDesc desc1;
360
361 err = AECountItems (desc, &count);
362 if (err != noErr)
363 break;
364 result = Qnil;
365 while (count > 0)
366 {
367 err = AEGetNthDesc (desc, count, typeWildCard, &keyword, &desc1);
368 if (err != noErr)
369 break;
370 elem = mac_aedesc_to_lisp (&desc1);
371 AEDisposeDesc (&desc1);
372 if (desc_type != typeAEList)
373 {
374 keyword = EndianU32_NtoB (keyword);
375 elem = Fcons (make_unibyte_string ((char *) &keyword, 4), elem);
376 }
377 result = Fcons (elem, result);
378 count--;
379 }
380 }
381 #endif
382 break;
383
384 default:
385 #if TARGET_API_MAC_CARBON
386 result = make_uninit_string (AEGetDescDataSize (desc));
387 err = AEGetDescData (desc, SDATA (result), SBYTES (result));
388 #else
389 result = make_uninit_string (GetHandleSize (desc->dataHandle));
390 memcpy (SDATA (result), *(desc->dataHandle), SBYTES (result));
391 #endif
392 break;
393 }
394
395 if (err != noErr)
396 return Qnil;
397
398 desc_type = EndianU32_NtoB (desc_type);
399 return Fcons (make_unibyte_string ((char *) &desc_type, 4), result);
400 }
401
402 static pascal OSErr
403 mac_coerce_file_name_ptr (type_code, data_ptr, data_size,
404 to_type, handler_refcon, result)
405 DescType type_code;
406 const void *data_ptr;
407 Size data_size;
408 DescType to_type;
409 long handler_refcon;
410 AEDesc *result;
411 {
412 OSErr err;
413
414 if (type_code == TYPE_FILE_NAME)
415 /* Coercion from undecoded file name. */
416 switch (to_type)
417 {
418 case typeAlias:
419 case typeFSS:
420 case typeFSRef:
421 #ifdef MAC_OSX
422 case typeFileURL:
423 #endif
424 {
425 #ifdef MAC_OSX
426 CFStringRef str;
427 CFURLRef url = NULL;
428 CFDataRef data = NULL;
429
430 str = CFStringCreateWithBytes (NULL, data_ptr, data_size,
431 kCFStringEncodingUTF8, false);
432 if (str)
433 {
434 url = CFURLCreateWithFileSystemPath (NULL, str,
435 kCFURLPOSIXPathStyle, false);
436 CFRelease (str);
437 }
438 if (url)
439 {
440 data = CFURLCreateData (NULL, url, kCFStringEncodingUTF8, true);
441 CFRelease (url);
442 }
443 if (data)
444 {
445 err = AECoercePtr (typeFileURL, CFDataGetBytePtr (data),
446 CFDataGetLength (data), to_type, result);
447 CFRelease (data);
448 }
449 else
450 err = memFullErr;
451 #else
452 FSSpec fs;
453 char *buf;
454
455 buf = xmalloc (data_size + 1);
456 if (buf)
457 {
458 memcpy (buf, data_ptr, data_size);
459 buf[data_size] = '\0';
460 err = posix_pathname_to_fsspec (buf, &fs);
461 xfree (buf);
462 }
463 else
464 err = memFullErr;
465 if (err == noErr)
466 err = AECoercePtr (typeFSS, &fs, sizeof (FSSpec),
467 to_type, result);
468 #endif
469 }
470 break;
471
472 case TYPE_FILE_NAME:
473 case typeWildCard:
474 err = AECreateDesc (TYPE_FILE_NAME, data_ptr, data_size, result);
475 break;
476
477 default:
478 err = errAECoercionFail;
479 break;
480 }
481 else if (to_type == TYPE_FILE_NAME)
482 /* Coercion to undecoded file name. */
483 switch (type_code)
484 {
485 case typeAlias:
486 case typeFSS:
487 case typeFSRef:
488 #ifdef MAC_OSX
489 case typeFileURL:
490 #endif
491 {
492 AEDesc desc;
493 #ifdef MAC_OSX
494 Size size;
495 char *buf;
496 CFURLRef url = NULL;
497 CFStringRef str = NULL;
498 CFDataRef data = NULL;
499
500 err = AECoercePtr (type_code, data_ptr, data_size,
501 typeFileURL, &desc);
502 if (err == noErr)
503 {
504 size = AEGetDescDataSize (&desc);
505 buf = xmalloc (size);
506 if (buf)
507 {
508 err = AEGetDescData (&desc, buf, size);
509 if (err == noErr)
510 url = CFURLCreateWithBytes (NULL, buf, size,
511 kCFStringEncodingUTF8, NULL);
512 xfree (buf);
513 }
514 AEDisposeDesc (&desc);
515 }
516 if (url)
517 {
518 str = CFURLCopyFileSystemPath (url, kCFURLPOSIXPathStyle);
519 CFRelease (url);
520 }
521 if (str)
522 {
523 data =
524 CFStringCreateExternalRepresentation (NULL, str,
525 kCFStringEncodingUTF8,
526 '\0');
527 CFRelease (str);
528 }
529 if (data)
530 {
531 err = AECreateDesc (TYPE_FILE_NAME, CFDataGetBytePtr (data),
532 CFDataGetLength (data), result);
533 CFRelease (data);
534 }
535 else
536 err = memFullErr;
537 #else
538 FSSpec fs;
539 char file_name[MAXPATHLEN];
540
541 err = AECoercePtr (type_code, data_ptr, data_size,
542 typeFSS, &desc);
543 if (err == noErr)
544 {
545 #if TARGET_API_MAC_CARBON
546 err = AEGetDescData (&desc, &fs, sizeof (FSSpec));
547 #else
548 fs = *(FSSpec *)(*(desc.dataHandle));
549 #endif
550 if (err == noErr)
551 err = fsspec_to_posix_pathname (&fs, file_name,
552 sizeof (file_name) - 1);
553 if (err == noErr)
554 err = AECreateDesc (TYPE_FILE_NAME, file_name,
555 strlen (file_name), result);
556 AEDisposeDesc (&desc);
557 }
558 #endif
559 }
560 break;
561
562 default:
563 err = errAECoercionFail;
564 break;
565 }
566 else
567 abort ();
568
569 if (err != noErr)
570 return errAECoercionFail;
571 return noErr;
572 }
573
574 static pascal OSErr
575 mac_coerce_file_name_desc (from_desc, to_type, handler_refcon, result)
576 const AEDesc *from_desc;
577 DescType to_type;
578 long handler_refcon;
579 AEDesc *result;
580 {
581 OSErr err = noErr;
582 DescType from_type = from_desc->descriptorType;
583
584 if (from_type == TYPE_FILE_NAME)
585 {
586 if (to_type != TYPE_FILE_NAME && to_type != typeWildCard
587 && to_type != typeAlias && to_type != typeFSS
588 && to_type != typeFSRef
589 #ifdef MAC_OSX
590 && to_type != typeFileURL
591 #endif
592 )
593 return errAECoercionFail;
594 }
595 else if (to_type == TYPE_FILE_NAME)
596 {
597 if (from_type != typeAlias && from_type != typeFSS
598 && from_type != typeFSRef
599 #ifdef MAC_OSX
600 && from_type != typeFileURL
601 #endif
602 )
603 return errAECoercionFail;
604 }
605 else
606 abort ();
607
608 if (from_type == to_type || to_type == typeWildCard)
609 err = AEDuplicateDesc (from_desc, result);
610 else
611 {
612 char *data_ptr;
613 Size data_size;
614
615 #if TARGET_API_MAC_CARBON
616 data_size = AEGetDescDataSize (from_desc);
617 #else
618 data_size = GetHandleSize (from_desc->dataHandle);
619 #endif
620 data_ptr = xmalloc (data_size);
621 if (data_ptr)
622 {
623 #if TARGET_API_MAC_CARBON
624 err = AEGetDescData (from_desc, data_ptr, data_size);
625 #else
626 memcpy (data_ptr, *(from_desc->dataHandle), data_size);
627 #endif
628 if (err == noErr)
629 err = mac_coerce_file_name_ptr (from_type, data_ptr,
630 data_size, to_type,
631 handler_refcon, result);
632 xfree (data_ptr);
633 }
634 else
635 err = memFullErr;
636 }
637
638 if (err != noErr)
639 return errAECoercionFail;
640 return noErr;
641 }
642
643 OSErr
644 init_coercion_handler ()
645 {
646 OSErr err;
647
648 static AECoercePtrUPP coerce_file_name_ptrUPP = NULL;
649 static AECoerceDescUPP coerce_file_name_descUPP = NULL;
650
651 if (coerce_file_name_ptrUPP == NULL)
652 {
653 coerce_file_name_ptrUPP = NewAECoercePtrUPP (mac_coerce_file_name_ptr);
654 coerce_file_name_descUPP = NewAECoerceDescUPP (mac_coerce_file_name_desc);
655 }
656
657 err = AEInstallCoercionHandler (TYPE_FILE_NAME, typeWildCard,
658 (AECoercionHandlerUPP)
659 coerce_file_name_ptrUPP, 0, false, false);
660 if (err == noErr)
661 err = AEInstallCoercionHandler (typeWildCard, TYPE_FILE_NAME,
662 (AECoercionHandlerUPP)
663 coerce_file_name_ptrUPP, 0, false, false);
664 if (err == noErr)
665 err = AEInstallCoercionHandler (TYPE_FILE_NAME, typeWildCard,
666 coerce_file_name_descUPP, 0, true, false);
667 if (err == noErr)
668 err = AEInstallCoercionHandler (typeWildCard, TYPE_FILE_NAME,
669 coerce_file_name_descUPP, 0, true, false);
670 return err;
671 }
672
673 #if TARGET_API_MAC_CARBON
674 OSErr
675 create_apple_event_from_event_ref (event, num_params, names, types, result)
676 EventRef event;
677 UInt32 num_params;
678 EventParamName *names;
679 EventParamType *types;
680 AppleEvent *result;
681 {
682 OSErr err;
683 static const ProcessSerialNumber psn = {0, kCurrentProcess};
684 AEAddressDesc address_desc;
685 UInt32 i, size;
686 CFStringRef string;
687 CFDataRef data;
688 char *buf;
689
690 err = AECreateDesc (typeProcessSerialNumber, &psn,
691 sizeof (ProcessSerialNumber), &address_desc);
692 if (err == noErr)
693 {
694 err = AECreateAppleEvent (0, 0, /* Dummy class and ID. */
695 &address_desc, /* NULL is not allowed
696 on Mac OS Classic. */
697 kAutoGenerateReturnID,
698 kAnyTransactionID, result);
699 AEDisposeDesc (&address_desc);
700 }
701 if (err != noErr)
702 return err;
703
704 for (i = 0; i < num_params; i++)
705 switch (types[i])
706 {
707 #ifdef MAC_OSX
708 case typeCFStringRef:
709 err = GetEventParameter (event, names[i], typeCFStringRef, NULL,
710 sizeof (CFStringRef), NULL, &string);
711 if (err != noErr)
712 break;
713 data = CFStringCreateExternalRepresentation (NULL, string,
714 kCFStringEncodingUTF8,
715 '?');
716 if (data == NULL)
717 break;
718 /* typeUTF8Text is not available on Mac OS X 10.1. */
719 AEPutParamPtr (result, names[i], 'utf8',
720 CFDataGetBytePtr (data), CFDataGetLength (data));
721 CFRelease (data);
722 break;
723 #endif
724
725 default:
726 err = GetEventParameter (event, names[i], types[i], NULL,
727 0, &size, NULL);
728 if (err != noErr)
729 break;
730 buf = xmalloc (size);
731 if (buf == NULL)
732 break;
733 err = GetEventParameter (event, names[i], types[i], NULL,
734 size, NULL, buf);
735 if (err == noErr)
736 AEPutParamPtr (result, names[i], types[i], buf, size);
737 xfree (buf);
738 break;
739 }
740
741 return noErr;
742 }
743 #endif
744
745 \f
746 /***********************************************************************
747 Conversion between Lisp and Core Foundation objects
748 ***********************************************************************/
749
750 #if TARGET_API_MAC_CARBON
751 static Lisp_Object Qstring, Qnumber, Qboolean, Qdate, Qdata;
752 static Lisp_Object Qarray, Qdictionary;
753
754 struct cfdict_context
755 {
756 Lisp_Object *result;
757 int with_tag, hash_bound;
758 };
759
760 /* C string to CFString. */
761
762 CFStringRef
763 cfstring_create_with_utf8_cstring (c_str)
764 const char *c_str;
765 {
766 CFStringRef str;
767
768 str = CFStringCreateWithCString (NULL, c_str, kCFStringEncodingUTF8);
769 if (str == NULL)
770 /* Failed to interpret as UTF 8. Fall back on Mac Roman. */
771 str = CFStringCreateWithCString (NULL, c_str, kCFStringEncodingMacRoman);
772
773 return str;
774 }
775
776
777 /* Lisp string to CFString. */
778
779 CFStringRef
780 cfstring_create_with_string (s)
781 Lisp_Object s;
782 {
783 CFStringRef string = NULL;
784
785 if (STRING_MULTIBYTE (s))
786 {
787 char *p, *end = SDATA (s) + SBYTES (s);
788
789 for (p = SDATA (s); p < end; p++)
790 if (!isascii (*p))
791 {
792 s = ENCODE_UTF_8 (s);
793 break;
794 }
795 string = CFStringCreateWithBytes (NULL, SDATA (s), SBYTES (s),
796 kCFStringEncodingUTF8, false);
797 }
798
799 if (string == NULL)
800 /* Failed to interpret as UTF 8. Fall back on Mac Roman. */
801 string = CFStringCreateWithBytes (NULL, SDATA (s), SBYTES (s),
802 kCFStringEncodingMacRoman, false);
803
804 return string;
805 }
806
807
808 /* From CFData to a lisp string. Always returns a unibyte string. */
809
810 Lisp_Object
811 cfdata_to_lisp (data)
812 CFDataRef data;
813 {
814 CFIndex len = CFDataGetLength (data);
815 Lisp_Object result = make_uninit_string (len);
816
817 CFDataGetBytes (data, CFRangeMake (0, len), SDATA (result));
818
819 return result;
820 }
821
822
823 /* From CFString to a lisp string. Returns a unibyte string
824 containing a UTF-8 byte sequence. */
825
826 Lisp_Object
827 cfstring_to_lisp_nodecode (string)
828 CFStringRef string;
829 {
830 Lisp_Object result = Qnil;
831 const char *s = CFStringGetCStringPtr (string, kCFStringEncodingUTF8);
832
833 if (s)
834 result = make_unibyte_string (s, strlen (s));
835 else
836 {
837 CFDataRef data =
838 CFStringCreateExternalRepresentation (NULL, string,
839 kCFStringEncodingUTF8, '?');
840
841 if (data)
842 {
843 result = cfdata_to_lisp (data);
844 CFRelease (data);
845 }
846 }
847
848 return result;
849 }
850
851
852 /* From CFString to a lisp string. Never returns a unibyte string
853 (even if it only contains ASCII characters).
854 This may cause GC during code conversion. */
855
856 Lisp_Object
857 cfstring_to_lisp (string)
858 CFStringRef string;
859 {
860 Lisp_Object result = cfstring_to_lisp_nodecode (string);
861
862 if (!NILP (result))
863 {
864 result = code_convert_string_norecord (result, Qutf_8, 0);
865 /* This may be superfluous. Just to make sure that the result
866 is a multibyte string. */
867 result = string_to_multibyte (result);
868 }
869
870 return result;
871 }
872
873
874 /* CFNumber to a lisp integer or a lisp float. */
875
876 Lisp_Object
877 cfnumber_to_lisp (number)
878 CFNumberRef number;
879 {
880 Lisp_Object result = Qnil;
881 #if BITS_PER_EMACS_INT > 32
882 SInt64 int_val;
883 CFNumberType emacs_int_type = kCFNumberSInt64Type;
884 #else
885 SInt32 int_val;
886 CFNumberType emacs_int_type = kCFNumberSInt32Type;
887 #endif
888 double float_val;
889
890 if (CFNumberGetValue (number, emacs_int_type, &int_val)
891 && !FIXNUM_OVERFLOW_P (int_val))
892 result = make_number (int_val);
893 else
894 if (CFNumberGetValue (number, kCFNumberDoubleType, &float_val))
895 result = make_float (float_val);
896 return result;
897 }
898
899
900 /* CFDate to a list of three integers as in a return value of
901 `current-time'. */
902
903 Lisp_Object
904 cfdate_to_lisp (date)
905 CFDateRef date;
906 {
907 static const CFGregorianDate epoch_gdate = {1970, 1, 1, 0, 0, 0.0};
908 static CFAbsoluteTime epoch = 0.0, sec;
909 int high, low;
910
911 if (epoch == 0.0)
912 epoch = CFGregorianDateGetAbsoluteTime (epoch_gdate, NULL);
913
914 sec = CFDateGetAbsoluteTime (date) - epoch;
915 high = sec / 65536.0;
916 low = sec - high * 65536.0;
917
918 return list3 (make_number (high), make_number (low), make_number (0));
919 }
920
921
922 /* CFBoolean to a lisp symbol, `t' or `nil'. */
923
924 Lisp_Object
925 cfboolean_to_lisp (boolean)
926 CFBooleanRef boolean;
927 {
928 return CFBooleanGetValue (boolean) ? Qt : Qnil;
929 }
930
931
932 /* Any Core Foundation object to a (lengthy) lisp string. */
933
934 Lisp_Object
935 cfobject_desc_to_lisp (object)
936 CFTypeRef object;
937 {
938 Lisp_Object result = Qnil;
939 CFStringRef desc = CFCopyDescription (object);
940
941 if (desc)
942 {
943 result = cfstring_to_lisp (desc);
944 CFRelease (desc);
945 }
946
947 return result;
948 }
949
950
951 /* Callback functions for cfproperty_list_to_lisp. */
952
953 static void
954 cfdictionary_add_to_list (key, value, context)
955 const void *key;
956 const void *value;
957 void *context;
958 {
959 struct cfdict_context *cxt = (struct cfdict_context *)context;
960
961 *cxt->result =
962 Fcons (Fcons (cfstring_to_lisp (key),
963 cfproperty_list_to_lisp (value, cxt->with_tag,
964 cxt->hash_bound)),
965 *cxt->result);
966 }
967
968 static void
969 cfdictionary_puthash (key, value, context)
970 const void *key;
971 const void *value;
972 void *context;
973 {
974 Lisp_Object lisp_key = cfstring_to_lisp (key);
975 struct cfdict_context *cxt = (struct cfdict_context *)context;
976 struct Lisp_Hash_Table *h = XHASH_TABLE (*(cxt->result));
977 unsigned hash_code;
978
979 hash_lookup (h, lisp_key, &hash_code);
980 hash_put (h, lisp_key,
981 cfproperty_list_to_lisp (value, cxt->with_tag, cxt->hash_bound),
982 hash_code);
983 }
984
985
986 /* Convert CFPropertyList PLIST to a lisp object. If WITH_TAG is
987 non-zero, a symbol that represents the type of the original Core
988 Foundation object is prepended. HASH_BOUND specifies which kinds
989 of the lisp objects, alists or hash tables, are used as the targets
990 of the conversion from CFDictionary. If HASH_BOUND is negative,
991 always generate alists. If HASH_BOUND >= 0, generate an alist if
992 the number of keys in the dictionary is smaller than HASH_BOUND,
993 and a hash table otherwise. */
994
995 Lisp_Object
996 cfproperty_list_to_lisp (plist, with_tag, hash_bound)
997 CFPropertyListRef plist;
998 int with_tag, hash_bound;
999 {
1000 CFTypeID type_id = CFGetTypeID (plist);
1001 Lisp_Object tag = Qnil, result = Qnil;
1002 struct gcpro gcpro1, gcpro2;
1003
1004 GCPRO2 (tag, result);
1005
1006 if (type_id == CFStringGetTypeID ())
1007 {
1008 tag = Qstring;
1009 result = cfstring_to_lisp (plist);
1010 }
1011 else if (type_id == CFNumberGetTypeID ())
1012 {
1013 tag = Qnumber;
1014 result = cfnumber_to_lisp (plist);
1015 }
1016 else if (type_id == CFBooleanGetTypeID ())
1017 {
1018 tag = Qboolean;
1019 result = cfboolean_to_lisp (plist);
1020 }
1021 else if (type_id == CFDateGetTypeID ())
1022 {
1023 tag = Qdate;
1024 result = cfdate_to_lisp (plist);
1025 }
1026 else if (type_id == CFDataGetTypeID ())
1027 {
1028 tag = Qdata;
1029 result = cfdata_to_lisp (plist);
1030 }
1031 else if (type_id == CFArrayGetTypeID ())
1032 {
1033 CFIndex index, count = CFArrayGetCount (plist);
1034
1035 tag = Qarray;
1036 result = Fmake_vector (make_number (count), Qnil);
1037 for (index = 0; index < count; index++)
1038 XVECTOR (result)->contents[index] =
1039 cfproperty_list_to_lisp (CFArrayGetValueAtIndex (plist, index),
1040 with_tag, hash_bound);
1041 }
1042 else if (type_id == CFDictionaryGetTypeID ())
1043 {
1044 struct cfdict_context context;
1045 CFIndex count = CFDictionaryGetCount (plist);
1046
1047 tag = Qdictionary;
1048 context.result = &result;
1049 context.with_tag = with_tag;
1050 context.hash_bound = hash_bound;
1051 if (hash_bound < 0 || count < hash_bound)
1052 {
1053 result = Qnil;
1054 CFDictionaryApplyFunction (plist, cfdictionary_add_to_list,
1055 &context);
1056 }
1057 else
1058 {
1059 result = make_hash_table (Qequal,
1060 make_number (count),
1061 make_float (DEFAULT_REHASH_SIZE),
1062 make_float (DEFAULT_REHASH_THRESHOLD),
1063 Qnil, Qnil, Qnil);
1064 CFDictionaryApplyFunction (plist, cfdictionary_puthash,
1065 &context);
1066 }
1067 }
1068 else
1069 abort ();
1070
1071 UNGCPRO;
1072
1073 if (with_tag)
1074 result = Fcons (tag, result);
1075
1076 return result;
1077 }
1078 #endif
1079
1080 \f
1081 /***********************************************************************
1082 Emulation of the X Resource Manager
1083 ***********************************************************************/
1084
1085 /* Parser functions for resource lines. Each function takes an
1086 address of a variable whose value points to the head of a string.
1087 The value will be advanced so that it points to the next character
1088 of the parsed part when the function returns.
1089
1090 A resource name such as "Emacs*font" is parsed into a non-empty
1091 list called `quarks'. Each element is either a Lisp string that
1092 represents a concrete component, a Lisp symbol LOOSE_BINDING
1093 (actually Qlambda) that represents any number (>=0) of intervening
1094 components, or a Lisp symbol SINGLE_COMPONENT (actually Qquote)
1095 that represents as any single component. */
1096
1097 #define P (*p)
1098
1099 #define LOOSE_BINDING Qlambda /* '*' ("L"oose) */
1100 #define SINGLE_COMPONENT Qquote /* '?' ("Q"uestion) */
1101
1102 static void
1103 skip_white_space (p)
1104 char **p;
1105 {
1106 /* WhiteSpace = {<space> | <horizontal tab>} */
1107 while (*P == ' ' || *P == '\t')
1108 P++;
1109 }
1110
1111 static int
1112 parse_comment (p)
1113 char **p;
1114 {
1115 /* Comment = "!" {<any character except null or newline>} */
1116 if (*P == '!')
1117 {
1118 P++;
1119 while (*P)
1120 if (*P++ == '\n')
1121 break;
1122 return 1;
1123 }
1124 else
1125 return 0;
1126 }
1127
1128 /* Don't interpret filename. Just skip until the newline. */
1129 static int
1130 parse_include_file (p)
1131 char **p;
1132 {
1133 /* IncludeFile = "#" WhiteSpace "include" WhiteSpace FileName WhiteSpace */
1134 if (*P == '#')
1135 {
1136 P++;
1137 while (*P)
1138 if (*P++ == '\n')
1139 break;
1140 return 1;
1141 }
1142 else
1143 return 0;
1144 }
1145
1146 static char
1147 parse_binding (p)
1148 char **p;
1149 {
1150 /* Binding = "." | "*" */
1151 if (*P == '.' || *P == '*')
1152 {
1153 char binding = *P++;
1154
1155 while (*P == '.' || *P == '*')
1156 if (*P++ == '*')
1157 binding = '*';
1158 return binding;
1159 }
1160 else
1161 return '\0';
1162 }
1163
1164 static Lisp_Object
1165 parse_component (p)
1166 char **p;
1167 {
1168 /* Component = "?" | ComponentName
1169 ComponentName = NameChar {NameChar}
1170 NameChar = "a"-"z" | "A"-"Z" | "0"-"9" | "_" | "-" */
1171 if (*P == '?')
1172 {
1173 P++;
1174 return SINGLE_COMPONENT;
1175 }
1176 else if (isalnum (*P) || *P == '_' || *P == '-')
1177 {
1178 char *start = P++;
1179
1180 while (isalnum (*P) || *P == '_' || *P == '-')
1181 P++;
1182
1183 return make_unibyte_string (start, P - start);
1184 }
1185 else
1186 return Qnil;
1187 }
1188
1189 static Lisp_Object
1190 parse_resource_name (p)
1191 char **p;
1192 {
1193 Lisp_Object result = Qnil, component;
1194 char binding;
1195
1196 /* ResourceName = [Binding] {Component Binding} ComponentName */
1197 if (parse_binding (p) == '*')
1198 result = Fcons (LOOSE_BINDING, result);
1199
1200 component = parse_component (p);
1201 if (NILP (component))
1202 return Qnil;
1203
1204 result = Fcons (component, result);
1205 while ((binding = parse_binding (p)) != '\0')
1206 {
1207 if (binding == '*')
1208 result = Fcons (LOOSE_BINDING, result);
1209 component = parse_component (p);
1210 if (NILP (component))
1211 return Qnil;
1212 else
1213 result = Fcons (component, result);
1214 }
1215
1216 /* The final component should not be '?'. */
1217 if (EQ (component, SINGLE_COMPONENT))
1218 return Qnil;
1219
1220 return Fnreverse (result);
1221 }
1222
1223 static Lisp_Object
1224 parse_value (p)
1225 char **p;
1226 {
1227 char *q, *buf;
1228 Lisp_Object seq = Qnil, result;
1229 int buf_len, total_len = 0, len, continue_p;
1230
1231 q = strchr (P, '\n');
1232 buf_len = q ? q - P : strlen (P);
1233 buf = xmalloc (buf_len);
1234
1235 while (1)
1236 {
1237 q = buf;
1238 continue_p = 0;
1239 while (*P)
1240 {
1241 if (*P == '\n')
1242 {
1243 P++;
1244 break;
1245 }
1246 else if (*P == '\\')
1247 {
1248 P++;
1249 if (*P == '\0')
1250 break;
1251 else if (*P == '\n')
1252 {
1253 P++;
1254 continue_p = 1;
1255 break;
1256 }
1257 else if (*P == 'n')
1258 {
1259 *q++ = '\n';
1260 P++;
1261 }
1262 else if ('0' <= P[0] && P[0] <= '7'
1263 && '0' <= P[1] && P[1] <= '7'
1264 && '0' <= P[2] && P[2] <= '7')
1265 {
1266 *q++ = (P[0] - '0' << 6) + (P[1] - '0' << 3) + (P[2] - '0');
1267 P += 3;
1268 }
1269 else
1270 *q++ = *P++;
1271 }
1272 else
1273 *q++ = *P++;
1274 }
1275 len = q - buf;
1276 seq = Fcons (make_unibyte_string (buf, len), seq);
1277 total_len += len;
1278
1279 if (continue_p)
1280 {
1281 q = strchr (P, '\n');
1282 len = q ? q - P : strlen (P);
1283 if (len > buf_len)
1284 {
1285 xfree (buf);
1286 buf_len = len;
1287 buf = xmalloc (buf_len);
1288 }
1289 }
1290 else
1291 break;
1292 }
1293 xfree (buf);
1294
1295 if (SBYTES (XCAR (seq)) == total_len)
1296 return make_string (SDATA (XCAR (seq)), total_len);
1297 else
1298 {
1299 buf = xmalloc (total_len);
1300 q = buf + total_len;
1301 for (; CONSP (seq); seq = XCDR (seq))
1302 {
1303 len = SBYTES (XCAR (seq));
1304 q -= len;
1305 memcpy (q, SDATA (XCAR (seq)), len);
1306 }
1307 result = make_string (buf, total_len);
1308 xfree (buf);
1309 return result;
1310 }
1311 }
1312
1313 static Lisp_Object
1314 parse_resource_line (p)
1315 char **p;
1316 {
1317 Lisp_Object quarks, value;
1318
1319 /* ResourceLine = Comment | IncludeFile | ResourceSpec | <empty line> */
1320 if (parse_comment (p) || parse_include_file (p))
1321 return Qnil;
1322
1323 /* ResourceSpec = WhiteSpace ResourceName WhiteSpace ":" WhiteSpace Value */
1324 skip_white_space (p);
1325 quarks = parse_resource_name (p);
1326 if (NILP (quarks))
1327 goto cleanup;
1328 skip_white_space (p);
1329 if (*P != ':')
1330 goto cleanup;
1331 P++;
1332 skip_white_space (p);
1333 value = parse_value (p);
1334 return Fcons (quarks, value);
1335
1336 cleanup:
1337 /* Skip the remaining data as a dummy value. */
1338 parse_value (p);
1339 return Qnil;
1340 }
1341
1342 #undef P
1343
1344 /* Equivalents of X Resource Manager functions.
1345
1346 An X Resource Database acts as a collection of resource names and
1347 associated values. It is implemented as a trie on quarks. Namely,
1348 each edge is labeled by either a string, LOOSE_BINDING, or
1349 SINGLE_COMPONENT. Each node has a node id, which is a unique
1350 nonnegative integer, and the root node id is 0. A database is
1351 implemented as a hash table that maps a pair (SRC-NODE-ID .
1352 EDGE-LABEL) to DEST-NODE-ID. It also holds a maximum node id used
1353 in the table as a value for HASHKEY_MAX_NID. A value associated to
1354 a node is recorded as a value for the node id.
1355
1356 A database also has a cache for past queries as a value for
1357 HASHKEY_QUERY_CACHE. It is another hash table that maps
1358 "NAME-STRING\0CLASS-STRING" to the result of the query. */
1359
1360 #define HASHKEY_MAX_NID (make_number (0))
1361 #define HASHKEY_QUERY_CACHE (make_number (-1))
1362
1363 static XrmDatabase
1364 xrm_create_database ()
1365 {
1366 XrmDatabase database;
1367
1368 database = make_hash_table (Qequal, make_number (DEFAULT_HASH_SIZE),
1369 make_float (DEFAULT_REHASH_SIZE),
1370 make_float (DEFAULT_REHASH_THRESHOLD),
1371 Qnil, Qnil, Qnil);
1372 Fputhash (HASHKEY_MAX_NID, make_number (0), database);
1373 Fputhash (HASHKEY_QUERY_CACHE, Qnil, database);
1374
1375 return database;
1376 }
1377
1378 static void
1379 xrm_q_put_resource (database, quarks, value)
1380 XrmDatabase database;
1381 Lisp_Object quarks, value;
1382 {
1383 struct Lisp_Hash_Table *h = XHASH_TABLE (database);
1384 unsigned hash_code;
1385 int max_nid, i;
1386 Lisp_Object node_id, key;
1387
1388 max_nid = XINT (Fgethash (HASHKEY_MAX_NID, database, Qnil));
1389
1390 XSETINT (node_id, 0);
1391 for (; CONSP (quarks); quarks = XCDR (quarks))
1392 {
1393 key = Fcons (node_id, XCAR (quarks));
1394 i = hash_lookup (h, key, &hash_code);
1395 if (i < 0)
1396 {
1397 max_nid++;
1398 XSETINT (node_id, max_nid);
1399 hash_put (h, key, node_id, hash_code);
1400 }
1401 else
1402 node_id = HASH_VALUE (h, i);
1403 }
1404 Fputhash (node_id, value, database);
1405
1406 Fputhash (HASHKEY_MAX_NID, make_number (max_nid), database);
1407 Fputhash (HASHKEY_QUERY_CACHE, Qnil, database);
1408 }
1409
1410 /* Merge multiple resource entries specified by DATA into a resource
1411 database DATABASE. DATA points to the head of a null-terminated
1412 string consisting of multiple resource lines. It's like a
1413 combination of XrmGetStringDatabase and XrmMergeDatabases. */
1414
1415 void
1416 xrm_merge_string_database (database, data)
1417 XrmDatabase database;
1418 char *data;
1419 {
1420 Lisp_Object quarks_value;
1421
1422 while (*data)
1423 {
1424 quarks_value = parse_resource_line (&data);
1425 if (!NILP (quarks_value))
1426 xrm_q_put_resource (database,
1427 XCAR (quarks_value), XCDR (quarks_value));
1428 }
1429 }
1430
1431 static Lisp_Object
1432 xrm_q_get_resource_1 (database, node_id, quark_name, quark_class)
1433 XrmDatabase database;
1434 Lisp_Object node_id, quark_name, quark_class;
1435 {
1436 struct Lisp_Hash_Table *h = XHASH_TABLE (database);
1437 Lisp_Object key, labels[3], value;
1438 int i, k;
1439
1440 if (!CONSP (quark_name))
1441 return Fgethash (node_id, database, Qnil);
1442
1443 /* First, try tight bindings */
1444 labels[0] = XCAR (quark_name);
1445 labels[1] = XCAR (quark_class);
1446 labels[2] = SINGLE_COMPONENT;
1447
1448 key = Fcons (node_id, Qnil);
1449 for (k = 0; k < sizeof (labels) / sizeof (*labels); k++)
1450 {
1451 XSETCDR (key, labels[k]);
1452 i = hash_lookup (h, key, NULL);
1453 if (i >= 0)
1454 {
1455 value = xrm_q_get_resource_1 (database, HASH_VALUE (h, i),
1456 XCDR (quark_name), XCDR (quark_class));
1457 if (!NILP (value))
1458 return value;
1459 }
1460 }
1461
1462 /* Then, try loose bindings */
1463 XSETCDR (key, LOOSE_BINDING);
1464 i = hash_lookup (h, key, NULL);
1465 if (i >= 0)
1466 {
1467 value = xrm_q_get_resource_1 (database, HASH_VALUE (h, i),
1468 quark_name, quark_class);
1469 if (!NILP (value))
1470 return value;
1471 else
1472 return xrm_q_get_resource_1 (database, node_id,
1473 XCDR (quark_name), XCDR (quark_class));
1474 }
1475 else
1476 return Qnil;
1477 }
1478
1479 static Lisp_Object
1480 xrm_q_get_resource (database, quark_name, quark_class)
1481 XrmDatabase database;
1482 Lisp_Object quark_name, quark_class;
1483 {
1484 return xrm_q_get_resource_1 (database, make_number (0),
1485 quark_name, quark_class);
1486 }
1487
1488 /* Retrieve a resource value for the specified NAME and CLASS from the
1489 resource database DATABASE. It corresponds to XrmGetResource. */
1490
1491 Lisp_Object
1492 xrm_get_resource (database, name, class)
1493 XrmDatabase database;
1494 char *name, *class;
1495 {
1496 Lisp_Object key, query_cache, quark_name, quark_class, tmp;
1497 int i, nn, nc;
1498 struct Lisp_Hash_Table *h;
1499 unsigned hash_code;
1500
1501 nn = strlen (name);
1502 nc = strlen (class);
1503 key = make_uninit_string (nn + nc + 1);
1504 strcpy (SDATA (key), name);
1505 strncpy (SDATA (key) + nn + 1, class, nc);
1506
1507 query_cache = Fgethash (HASHKEY_QUERY_CACHE, database, Qnil);
1508 if (NILP (query_cache))
1509 {
1510 query_cache = make_hash_table (Qequal, make_number (DEFAULT_HASH_SIZE),
1511 make_float (DEFAULT_REHASH_SIZE),
1512 make_float (DEFAULT_REHASH_THRESHOLD),
1513 Qnil, Qnil, Qnil);
1514 Fputhash (HASHKEY_QUERY_CACHE, query_cache, database);
1515 }
1516 h = XHASH_TABLE (query_cache);
1517 i = hash_lookup (h, key, &hash_code);
1518 if (i >= 0)
1519 return HASH_VALUE (h, i);
1520
1521 quark_name = parse_resource_name (&name);
1522 if (*name != '\0')
1523 return Qnil;
1524 for (tmp = quark_name, nn = 0; CONSP (tmp); tmp = XCDR (tmp), nn++)
1525 if (!STRINGP (XCAR (tmp)))
1526 return Qnil;
1527
1528 quark_class = parse_resource_name (&class);
1529 if (*class != '\0')
1530 return Qnil;
1531 for (tmp = quark_class, nc = 0; CONSP (tmp); tmp = XCDR (tmp), nc++)
1532 if (!STRINGP (XCAR (tmp)))
1533 return Qnil;
1534
1535 if (nn != nc)
1536 return Qnil;
1537 else
1538 {
1539 tmp = xrm_q_get_resource (database, quark_name, quark_class);
1540 hash_put (h, key, tmp, hash_code);
1541 return tmp;
1542 }
1543 }
1544
1545 #if TARGET_API_MAC_CARBON
1546 static Lisp_Object
1547 xrm_cfproperty_list_to_value (plist)
1548 CFPropertyListRef plist;
1549 {
1550 CFTypeID type_id = CFGetTypeID (plist);
1551
1552 if (type_id == CFStringGetTypeID ())
1553 return cfstring_to_lisp (plist);
1554 else if (type_id == CFNumberGetTypeID ())
1555 {
1556 CFStringRef string;
1557 Lisp_Object result = Qnil;
1558
1559 string = CFStringCreateWithFormat (NULL, NULL, CFSTR ("%@"), plist);
1560 if (string)
1561 {
1562 result = cfstring_to_lisp (string);
1563 CFRelease (string);
1564 }
1565 return result;
1566 }
1567 else if (type_id == CFBooleanGetTypeID ())
1568 return build_string (CFBooleanGetValue (plist) ? "true" : "false");
1569 else if (type_id == CFDataGetTypeID ())
1570 return cfdata_to_lisp (plist);
1571 else
1572 return Qnil;
1573 }
1574 #endif
1575
1576 /* Create a new resource database from the preferences for the
1577 application APPLICATION. APPLICATION is either a string that
1578 specifies an application ID, or NULL that represents the current
1579 application. */
1580
1581 XrmDatabase
1582 xrm_get_preference_database (application)
1583 char *application;
1584 {
1585 #if TARGET_API_MAC_CARBON
1586 CFStringRef app_id, *keys, user_doms[2], host_doms[2];
1587 CFMutableSetRef key_set = NULL;
1588 CFArrayRef key_array;
1589 CFIndex index, count;
1590 char *res_name;
1591 XrmDatabase database;
1592 Lisp_Object quarks = Qnil, value = Qnil;
1593 CFPropertyListRef plist;
1594 int iu, ih;
1595 struct gcpro gcpro1, gcpro2, gcpro3;
1596
1597 user_doms[0] = kCFPreferencesCurrentUser;
1598 user_doms[1] = kCFPreferencesAnyUser;
1599 host_doms[0] = kCFPreferencesCurrentHost;
1600 host_doms[1] = kCFPreferencesAnyHost;
1601
1602 database = xrm_create_database ();
1603
1604 GCPRO3 (database, quarks, value);
1605
1606 BLOCK_INPUT;
1607
1608 app_id = kCFPreferencesCurrentApplication;
1609 if (application)
1610 {
1611 app_id = cfstring_create_with_utf8_cstring (application);
1612 if (app_id == NULL)
1613 goto out;
1614 }
1615
1616 key_set = CFSetCreateMutable (NULL, 0, &kCFCopyStringSetCallBacks);
1617 if (key_set == NULL)
1618 goto out;
1619 for (iu = 0; iu < sizeof (user_doms) / sizeof (*user_doms) ; iu++)
1620 for (ih = 0; ih < sizeof (host_doms) / sizeof (*host_doms); ih++)
1621 {
1622 key_array = CFPreferencesCopyKeyList (app_id, user_doms[iu],
1623 host_doms[ih]);
1624 if (key_array)
1625 {
1626 count = CFArrayGetCount (key_array);
1627 for (index = 0; index < count; index++)
1628 CFSetAddValue (key_set,
1629 CFArrayGetValueAtIndex (key_array, index));
1630 CFRelease (key_array);
1631 }
1632 }
1633
1634 count = CFSetGetCount (key_set);
1635 keys = xmalloc (sizeof (CFStringRef) * count);
1636 if (keys == NULL)
1637 goto out;
1638 CFSetGetValues (key_set, (const void **)keys);
1639 for (index = 0; index < count; index++)
1640 {
1641 res_name = SDATA (cfstring_to_lisp_nodecode (keys[index]));
1642 quarks = parse_resource_name (&res_name);
1643 if (!(NILP (quarks) || *res_name))
1644 {
1645 plist = CFPreferencesCopyAppValue (keys[index], app_id);
1646 value = xrm_cfproperty_list_to_value (plist);
1647 CFRelease (plist);
1648 if (!NILP (value))
1649 xrm_q_put_resource (database, quarks, value);
1650 }
1651 }
1652
1653 xfree (keys);
1654 out:
1655 if (key_set)
1656 CFRelease (key_set);
1657 CFRelease (app_id);
1658
1659 UNBLOCK_INPUT;
1660
1661 UNGCPRO;
1662
1663 return database;
1664 #else
1665 return xrm_create_database ();
1666 #endif
1667 }
1668
1669 \f
1670 #ifndef MAC_OSX
1671
1672 /* The following functions with "sys_" prefix are stubs to Unix
1673 functions that have already been implemented by CW or MPW. The
1674 calls to them in Emacs source course are #define'd to call the sys_
1675 versions by the header files s-mac.h. In these stubs pathnames are
1676 converted between their Unix and Mac forms. */
1677
1678
1679 /* Unix epoch is Jan 1, 1970 while Mac epoch is Jan 1, 1904: 66 years
1680 + 17 leap days. These are for adjusting time values returned by
1681 MacOS Toolbox functions. */
1682
1683 #define MAC_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
1684
1685 #ifdef __MWERKS__
1686 #if __MSL__ < 0x6000
1687 /* CW Pro 5 epoch is Jan 1, 1900 (aaarghhhhh!); remember, 1900 is not
1688 a leap year! This is for adjusting time_t values returned by MSL
1689 functions. */
1690 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 70 + 17) * 24 * 60 * 60)
1691 #else /* __MSL__ >= 0x6000 */
1692 /* CW changes Pro 6 to follow Unix! */
1693 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
1694 #endif /* __MSL__ >= 0x6000 */
1695 #elif __MRC__
1696 /* MPW library functions follow Unix (confused?). */
1697 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
1698 #else /* not __MRC__ */
1699 You lose!!!
1700 #endif /* not __MRC__ */
1701
1702
1703 /* Define our own stat function for both MrC and CW. The reason for
1704 doing this: "stat" is both the name of a struct and function name:
1705 can't use the same trick like that for sys_open, sys_close, etc. to
1706 redirect Emacs's calls to our own version that converts Unix style
1707 filenames to Mac style filename because all sorts of compilation
1708 errors will be generated if stat is #define'd to be sys_stat. */
1709
1710 int
1711 stat_noalias (const char *path, struct stat *buf)
1712 {
1713 char mac_pathname[MAXPATHLEN+1];
1714 CInfoPBRec cipb;
1715
1716 if (posix_to_mac_pathname (path, mac_pathname, MAXPATHLEN+1) == 0)
1717 return -1;
1718
1719 c2pstr (mac_pathname);
1720 cipb.hFileInfo.ioNamePtr = mac_pathname;
1721 cipb.hFileInfo.ioVRefNum = 0;
1722 cipb.hFileInfo.ioDirID = 0;
1723 cipb.hFileInfo.ioFDirIndex = 0;
1724 /* set to 0 to get information about specific dir or file */
1725
1726 errno = PBGetCatInfo (&cipb, false);
1727 if (errno == -43) /* -43: fnfErr defined in Errors.h */
1728 errno = ENOENT;
1729 if (errno != noErr)
1730 return -1;
1731
1732 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* bit 4 = 1 for directories */
1733 {
1734 buf->st_mode = S_IFDIR | S_IREAD | S_IEXEC;
1735
1736 if (!(cipb.hFileInfo.ioFlAttrib & 0x1))
1737 buf->st_mode |= S_IWRITE; /* bit 1 = 1 for locked files/directories */
1738 buf->st_ino = cipb.dirInfo.ioDrDirID;
1739 buf->st_dev = cipb.dirInfo.ioVRefNum;
1740 buf->st_size = cipb.dirInfo.ioDrNmFls;
1741 /* size of dir = number of files and dirs */
1742 buf->st_atime
1743 = buf->st_mtime
1744 = cipb.dirInfo.ioDrMdDat - MAC_UNIX_EPOCH_DIFF;
1745 buf->st_ctime = cipb.dirInfo.ioDrCrDat - MAC_UNIX_EPOCH_DIFF;
1746 }
1747 else
1748 {
1749 buf->st_mode = S_IFREG | S_IREAD;
1750 if (!(cipb.hFileInfo.ioFlAttrib & 0x1))
1751 buf->st_mode |= S_IWRITE; /* bit 1 = 1 for locked files/directories */
1752 if (cipb.hFileInfo.ioFlFndrInfo.fdType == 'APPL')
1753 buf->st_mode |= S_IEXEC;
1754 buf->st_ino = cipb.hFileInfo.ioDirID;
1755 buf->st_dev = cipb.hFileInfo.ioVRefNum;
1756 buf->st_size = cipb.hFileInfo.ioFlLgLen;
1757 buf->st_atime
1758 = buf->st_mtime
1759 = cipb.hFileInfo.ioFlMdDat - MAC_UNIX_EPOCH_DIFF;
1760 buf->st_ctime = cipb.hFileInfo.ioFlCrDat - MAC_UNIX_EPOCH_DIFF;
1761 }
1762
1763 if (cipb.hFileInfo.ioFlFndrInfo.fdFlags & 0x8000)
1764 {
1765 /* identify alias files as symlinks */
1766 buf->st_mode &= ~S_IFREG;
1767 buf->st_mode |= S_IFLNK;
1768 }
1769
1770 buf->st_nlink = 1;
1771 buf->st_uid = getuid ();
1772 buf->st_gid = getgid ();
1773 buf->st_rdev = 0;
1774
1775 return 0;
1776 }
1777
1778
1779 int
1780 lstat (const char *path, struct stat *buf)
1781 {
1782 int result;
1783 char true_pathname[MAXPATHLEN+1];
1784
1785 /* Try looking for the file without resolving aliases first. */
1786 if ((result = stat_noalias (path, buf)) >= 0)
1787 return result;
1788
1789 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1790 return -1;
1791
1792 return stat_noalias (true_pathname, buf);
1793 }
1794
1795
1796 int
1797 stat (const char *path, struct stat *sb)
1798 {
1799 int result;
1800 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
1801 int len;
1802
1803 if ((result = stat_noalias (path, sb)) >= 0 &&
1804 ! (sb->st_mode & S_IFLNK))
1805 return result;
1806
1807 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1808 return -1;
1809
1810 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
1811 if (len > -1)
1812 {
1813 fully_resolved_name[len] = '\0';
1814 /* in fact our readlink terminates strings */
1815 return lstat (fully_resolved_name, sb);
1816 }
1817 else
1818 return lstat (true_pathname, sb);
1819 }
1820
1821
1822 #if __MRC__
1823 /* CW defines fstat in stat.mac.c while MPW does not provide this
1824 function. Without the information of how to get from a file
1825 descriptor in MPW StdCLib to a Mac OS file spec, it should be hard
1826 to implement this function. Fortunately, there is only one place
1827 where this function is called in our configuration: in fileio.c,
1828 where only the st_dev and st_ino fields are used to determine
1829 whether two fildes point to different i-nodes to prevent copying
1830 a file onto itself equal. What we have here probably needs
1831 improvement. */
1832
1833 int
1834 fstat (int fildes, struct stat *buf)
1835 {
1836 buf->st_dev = 0;
1837 buf->st_ino = fildes;
1838 buf->st_mode = S_IFREG; /* added by T.I. for the copy-file */
1839 return 0; /* success */
1840 }
1841 #endif /* __MRC__ */
1842
1843
1844 int
1845 mkdir (const char *dirname, int mode)
1846 {
1847 #pragma unused(mode)
1848
1849 HFileParam hfpb;
1850 char true_pathname[MAXPATHLEN+1], mac_pathname[MAXPATHLEN+1];
1851
1852 if (find_true_pathname (dirname, true_pathname, MAXPATHLEN+1) == -1)
1853 return -1;
1854
1855 if (posix_to_mac_pathname (true_pathname, mac_pathname, MAXPATHLEN+1) == 0)
1856 return -1;
1857
1858 c2pstr (mac_pathname);
1859 hfpb.ioNamePtr = mac_pathname;
1860 hfpb.ioVRefNum = 0; /* ignored unless name is invalid */
1861 hfpb.ioDirID = 0; /* parent is the root */
1862
1863 errno = PBDirCreate ((HParmBlkPtr) &hfpb, false);
1864 /* just return the Mac OSErr code for now */
1865 return errno == noErr ? 0 : -1;
1866 }
1867
1868
1869 #undef rmdir
1870 sys_rmdir (const char *dirname)
1871 {
1872 HFileParam hfpb;
1873 char mac_pathname[MAXPATHLEN+1];
1874
1875 if (posix_to_mac_pathname (dirname, mac_pathname, MAXPATHLEN+1) == 0)
1876 return -1;
1877
1878 c2pstr (mac_pathname);
1879 hfpb.ioNamePtr = mac_pathname;
1880 hfpb.ioVRefNum = 0; /* ignored unless name is invalid */
1881 hfpb.ioDirID = 0; /* parent is the root */
1882
1883 errno = PBHDelete ((HParmBlkPtr) &hfpb, false);
1884 return errno == noErr ? 0 : -1;
1885 }
1886
1887
1888 #ifdef __MRC__
1889 /* No implementation yet. */
1890 int
1891 execvp (const char *path, ...)
1892 {
1893 return -1;
1894 }
1895 #endif /* __MRC__ */
1896
1897
1898 int
1899 utime (const char *path, const struct utimbuf *times)
1900 {
1901 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
1902 int len;
1903 char mac_pathname[MAXPATHLEN+1];
1904 CInfoPBRec cipb;
1905
1906 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1907 return -1;
1908
1909 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
1910 if (len > -1)
1911 fully_resolved_name[len] = '\0';
1912 else
1913 strcpy (fully_resolved_name, true_pathname);
1914
1915 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
1916 return -1;
1917
1918 c2pstr (mac_pathname);
1919 cipb.hFileInfo.ioNamePtr = mac_pathname;
1920 cipb.hFileInfo.ioVRefNum = 0;
1921 cipb.hFileInfo.ioDirID = 0;
1922 cipb.hFileInfo.ioFDirIndex = 0;
1923 /* set to 0 to get information about specific dir or file */
1924
1925 errno = PBGetCatInfo (&cipb, false);
1926 if (errno != noErr)
1927 return -1;
1928
1929 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* bit 4 = 1 for directories */
1930 {
1931 if (times)
1932 cipb.dirInfo.ioDrMdDat = times->modtime + MAC_UNIX_EPOCH_DIFF;
1933 else
1934 GetDateTime (&cipb.dirInfo.ioDrMdDat);
1935 }
1936 else
1937 {
1938 if (times)
1939 cipb.hFileInfo.ioFlMdDat = times->modtime + MAC_UNIX_EPOCH_DIFF;
1940 else
1941 GetDateTime (&cipb.hFileInfo.ioFlMdDat);
1942 }
1943
1944 errno = PBSetCatInfo (&cipb, false);
1945 return errno == noErr ? 0 : -1;
1946 }
1947
1948
1949 #ifndef F_OK
1950 #define F_OK 0
1951 #endif
1952 #ifndef X_OK
1953 #define X_OK 1
1954 #endif
1955 #ifndef W_OK
1956 #define W_OK 2
1957 #endif
1958
1959 /* Like stat, but test for access mode in hfpb.ioFlAttrib */
1960 int
1961 access (const char *path, int mode)
1962 {
1963 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
1964 int len;
1965 char mac_pathname[MAXPATHLEN+1];
1966 CInfoPBRec cipb;
1967
1968 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1969 return -1;
1970
1971 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
1972 if (len > -1)
1973 fully_resolved_name[len] = '\0';
1974 else
1975 strcpy (fully_resolved_name, true_pathname);
1976
1977 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
1978 return -1;
1979
1980 c2pstr (mac_pathname);
1981 cipb.hFileInfo.ioNamePtr = mac_pathname;
1982 cipb.hFileInfo.ioVRefNum = 0;
1983 cipb.hFileInfo.ioDirID = 0;
1984 cipb.hFileInfo.ioFDirIndex = 0;
1985 /* set to 0 to get information about specific dir or file */
1986
1987 errno = PBGetCatInfo (&cipb, false);
1988 if (errno != noErr)
1989 return -1;
1990
1991 if (mode == F_OK) /* got this far, file exists */
1992 return 0;
1993
1994 if (mode & X_OK)
1995 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* path refers to a directory */
1996 return 0;
1997 else
1998 {
1999 if (cipb.hFileInfo.ioFlFndrInfo.fdType == 'APPL')
2000 return 0;
2001 else
2002 return -1;
2003 }
2004
2005 if (mode & W_OK)
2006 return (cipb.hFileInfo.ioFlAttrib & 0x1) ? -1 : 0;
2007 /* don't allow if lock bit is on */
2008
2009 return -1;
2010 }
2011
2012
2013 #define DEV_NULL_FD 0x10000
2014
2015 #undef open
2016 int
2017 sys_open (const char *path, int oflag)
2018 {
2019 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
2020 int len;
2021 char mac_pathname[MAXPATHLEN+1];
2022
2023 if (strcmp (path, "/dev/null") == 0)
2024 return DEV_NULL_FD; /* some bogus fd to be ignored in write */
2025
2026 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
2027 return -1;
2028
2029 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
2030 if (len > -1)
2031 fully_resolved_name[len] = '\0';
2032 else
2033 strcpy (fully_resolved_name, true_pathname);
2034
2035 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
2036 return -1;
2037 else
2038 {
2039 #ifdef __MRC__
2040 int res = open (mac_pathname, oflag);
2041 /* if (oflag == O_WRONLY || oflag == O_RDWR) */
2042 if (oflag & O_CREAT)
2043 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
2044 return res;
2045 #else /* not __MRC__ */
2046 return open (mac_pathname, oflag);
2047 #endif /* not __MRC__ */
2048 }
2049 }
2050
2051
2052 #undef creat
2053 int
2054 sys_creat (const char *path, mode_t mode)
2055 {
2056 char true_pathname[MAXPATHLEN+1];
2057 int len;
2058 char mac_pathname[MAXPATHLEN+1];
2059
2060 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
2061 return -1;
2062
2063 if (!posix_to_mac_pathname (true_pathname, mac_pathname, MAXPATHLEN+1))
2064 return -1;
2065 else
2066 {
2067 #ifdef __MRC__
2068 int result = creat (mac_pathname);
2069 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
2070 return result;
2071 #else /* not __MRC__ */
2072 return creat (mac_pathname, mode);
2073 #endif /* not __MRC__ */
2074 }
2075 }
2076
2077
2078 #undef unlink
2079 int
2080 sys_unlink (const char *path)
2081 {
2082 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
2083 int len;
2084 char mac_pathname[MAXPATHLEN+1];
2085
2086 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
2087 return -1;
2088
2089 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
2090 if (len > -1)
2091 fully_resolved_name[len] = '\0';
2092 else
2093 strcpy (fully_resolved_name, true_pathname);
2094
2095 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
2096 return -1;
2097 else
2098 return unlink (mac_pathname);
2099 }
2100
2101
2102 #undef read
2103 int
2104 sys_read (int fildes, char *buf, int count)
2105 {
2106 if (fildes == 0) /* this should not be used for console input */
2107 return -1;
2108 else
2109 #if __MSL__ >= 0x6000
2110 return _read (fildes, buf, count);
2111 #else
2112 return read (fildes, buf, count);
2113 #endif
2114 }
2115
2116
2117 #undef write
2118 int
2119 sys_write (int fildes, const char *buf, int count)
2120 {
2121 if (fildes == DEV_NULL_FD)
2122 return count;
2123 else
2124 #if __MSL__ >= 0x6000
2125 return _write (fildes, buf, count);
2126 #else
2127 return write (fildes, buf, count);
2128 #endif
2129 }
2130
2131
2132 #undef rename
2133 int
2134 sys_rename (const char * old_name, const char * new_name)
2135 {
2136 char true_old_pathname[MAXPATHLEN+1], true_new_pathname[MAXPATHLEN+1];
2137 char fully_resolved_old_name[MAXPATHLEN+1];
2138 int len;
2139 char mac_old_name[MAXPATHLEN+1], mac_new_name[MAXPATHLEN+1];
2140
2141 if (find_true_pathname (old_name, true_old_pathname, MAXPATHLEN+1) == -1)
2142 return -1;
2143
2144 len = readlink (true_old_pathname, fully_resolved_old_name, MAXPATHLEN);
2145 if (len > -1)
2146 fully_resolved_old_name[len] = '\0';
2147 else
2148 strcpy (fully_resolved_old_name, true_old_pathname);
2149
2150 if (find_true_pathname (new_name, true_new_pathname, MAXPATHLEN+1) == -1)
2151 return -1;
2152
2153 if (strcmp (fully_resolved_old_name, true_new_pathname) == 0)
2154 return 0;
2155
2156 if (!posix_to_mac_pathname (fully_resolved_old_name,
2157 mac_old_name,
2158 MAXPATHLEN+1))
2159 return -1;
2160
2161 if (!posix_to_mac_pathname(true_new_pathname, mac_new_name, MAXPATHLEN+1))
2162 return -1;
2163
2164 /* If a file with new_name already exists, rename deletes the old
2165 file in Unix. CW version fails in these situation. So we add a
2166 call to unlink here. */
2167 (void) unlink (mac_new_name);
2168
2169 return rename (mac_old_name, mac_new_name);
2170 }
2171
2172
2173 #undef fopen
2174 extern FILE *fopen (const char *name, const char *mode);
2175 FILE *
2176 sys_fopen (const char *name, const char *mode)
2177 {
2178 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
2179 int len;
2180 char mac_pathname[MAXPATHLEN+1];
2181
2182 if (find_true_pathname (name, true_pathname, MAXPATHLEN+1) == -1)
2183 return 0;
2184
2185 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
2186 if (len > -1)
2187 fully_resolved_name[len] = '\0';
2188 else
2189 strcpy (fully_resolved_name, true_pathname);
2190
2191 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
2192 return 0;
2193 else
2194 {
2195 #ifdef __MRC__
2196 if (mode[0] == 'w' || mode[0] == 'a')
2197 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
2198 #endif /* not __MRC__ */
2199 return fopen (mac_pathname, mode);
2200 }
2201 }
2202
2203
2204 #include "keyboard.h"
2205 extern Boolean mac_wait_next_event (EventRecord *, UInt32, Boolean);
2206
2207 int
2208 select (n, rfds, wfds, efds, timeout)
2209 int n;
2210 SELECT_TYPE *rfds;
2211 SELECT_TYPE *wfds;
2212 SELECT_TYPE *efds;
2213 struct timeval *timeout;
2214 {
2215 OSErr err;
2216 #if TARGET_API_MAC_CARBON
2217 EventTimeout timeout_sec =
2218 (timeout
2219 ? (EMACS_SECS (*timeout) * kEventDurationSecond
2220 + EMACS_USECS (*timeout) * kEventDurationMicrosecond)
2221 : kEventDurationForever);
2222
2223 BLOCK_INPUT;
2224 err = ReceiveNextEvent (0, NULL, timeout_sec, kEventLeaveInQueue, NULL);
2225 UNBLOCK_INPUT;
2226 #else /* not TARGET_API_MAC_CARBON */
2227 EventRecord e;
2228 UInt32 sleep_time = EMACS_SECS (*timeout) * 60 +
2229 ((EMACS_USECS (*timeout) * 60) / 1000000);
2230
2231 /* Can only handle wait for keyboard input. */
2232 if (n > 1 || wfds || efds)
2233 return -1;
2234
2235 /* Also return true if an event other than a keyDown has occurred.
2236 This causes kbd_buffer_get_event in keyboard.c to call
2237 read_avail_input which in turn calls XTread_socket to poll for
2238 these events. Otherwise these never get processed except but a
2239 very slow poll timer. */
2240 if (mac_wait_next_event (&e, sleep_time, false))
2241 err = noErr;
2242 else
2243 err = -9875; /* eventLoopTimedOutErr */
2244 #endif /* not TARGET_API_MAC_CARBON */
2245
2246 if (FD_ISSET (0, rfds))
2247 if (err == noErr)
2248 return 1;
2249 else
2250 {
2251 FD_ZERO (rfds);
2252 return 0;
2253 }
2254 else
2255 if (err == noErr)
2256 {
2257 if (input_polling_used ())
2258 {
2259 /* It could be confusing if a real alarm arrives while
2260 processing the fake one. Turn it off and let the
2261 handler reset it. */
2262 extern void poll_for_input_1 P_ ((void));
2263 int old_poll_suppress_count = poll_suppress_count;
2264 poll_suppress_count = 1;
2265 poll_for_input_1 ();
2266 poll_suppress_count = old_poll_suppress_count;
2267 }
2268 errno = EINTR;
2269 return -1;
2270 }
2271 else
2272 return 0;
2273 }
2274
2275
2276 /* Simulation of SIGALRM. The stub for function signal stores the
2277 signal handler function in alarm_signal_func if a SIGALRM is
2278 encountered. */
2279
2280 #include <signal.h>
2281 #include "syssignal.h"
2282
2283 static TMTask mac_atimer_task;
2284
2285 static QElemPtr mac_atimer_qlink = (QElemPtr) &mac_atimer_task;
2286
2287 static int signal_mask = 0;
2288
2289 #ifdef __MRC__
2290 __sigfun alarm_signal_func = (__sigfun) 0;
2291 #elif __MWERKS__
2292 __signal_func_ptr alarm_signal_func = (__signal_func_ptr) 0;
2293 #else /* not __MRC__ and not __MWERKS__ */
2294 You lose!!!
2295 #endif /* not __MRC__ and not __MWERKS__ */
2296
2297 #undef signal
2298 #ifdef __MRC__
2299 extern __sigfun signal (int signal, __sigfun signal_func);
2300 __sigfun
2301 sys_signal (int signal_num, __sigfun signal_func)
2302 #elif __MWERKS__
2303 extern __signal_func_ptr signal (int signal, __signal_func_ptr signal_func);
2304 __signal_func_ptr
2305 sys_signal (int signal_num, __signal_func_ptr signal_func)
2306 #else /* not __MRC__ and not __MWERKS__ */
2307 You lose!!!
2308 #endif /* not __MRC__ and not __MWERKS__ */
2309 {
2310 if (signal_num != SIGALRM)
2311 return signal (signal_num, signal_func);
2312 else
2313 {
2314 #ifdef __MRC__
2315 __sigfun old_signal_func;
2316 #elif __MWERKS__
2317 __signal_func_ptr old_signal_func;
2318 #else
2319 You lose!!!
2320 #endif
2321 old_signal_func = alarm_signal_func;
2322 alarm_signal_func = signal_func;
2323 return old_signal_func;
2324 }
2325 }
2326
2327
2328 static pascal void
2329 mac_atimer_handler (qlink)
2330 TMTaskPtr qlink;
2331 {
2332 if (alarm_signal_func)
2333 (alarm_signal_func) (SIGALRM);
2334 }
2335
2336
2337 static void
2338 set_mac_atimer (count)
2339 long count;
2340 {
2341 static TimerUPP mac_atimer_handlerUPP = NULL;
2342
2343 if (mac_atimer_handlerUPP == NULL)
2344 mac_atimer_handlerUPP = NewTimerUPP (mac_atimer_handler);
2345 mac_atimer_task.tmCount = 0;
2346 mac_atimer_task.tmAddr = mac_atimer_handlerUPP;
2347 mac_atimer_qlink = (QElemPtr) &mac_atimer_task;
2348 InsTime (mac_atimer_qlink);
2349 if (count)
2350 PrimeTime (mac_atimer_qlink, count);
2351 }
2352
2353
2354 int
2355 remove_mac_atimer (remaining_count)
2356 long *remaining_count;
2357 {
2358 if (mac_atimer_qlink)
2359 {
2360 RmvTime (mac_atimer_qlink);
2361 if (remaining_count)
2362 *remaining_count = mac_atimer_task.tmCount;
2363 mac_atimer_qlink = NULL;
2364
2365 return 0;
2366 }
2367 else
2368 return -1;
2369 }
2370
2371
2372 int
2373 sigblock (int mask)
2374 {
2375 int old_mask = signal_mask;
2376
2377 signal_mask |= mask;
2378
2379 if ((old_mask ^ signal_mask) & sigmask (SIGALRM))
2380 remove_mac_atimer (NULL);
2381
2382 return old_mask;
2383 }
2384
2385
2386 int
2387 sigsetmask (int mask)
2388 {
2389 int old_mask = signal_mask;
2390
2391 signal_mask = mask;
2392
2393 if ((old_mask ^ signal_mask) & sigmask (SIGALRM))
2394 if (signal_mask & sigmask (SIGALRM))
2395 remove_mac_atimer (NULL);
2396 else
2397 set_mac_atimer (mac_atimer_task.tmCount);
2398
2399 return old_mask;
2400 }
2401
2402
2403 int
2404 alarm (int seconds)
2405 {
2406 long remaining_count;
2407
2408 if (remove_mac_atimer (&remaining_count) == 0)
2409 {
2410 set_mac_atimer (seconds * 1000);
2411
2412 return remaining_count / 1000;
2413 }
2414 else
2415 {
2416 mac_atimer_task.tmCount = seconds * 1000;
2417
2418 return 0;
2419 }
2420 }
2421
2422
2423 int
2424 setitimer (which, value, ovalue)
2425 int which;
2426 const struct itimerval *value;
2427 struct itimerval *ovalue;
2428 {
2429 long remaining_count;
2430 long count = (EMACS_SECS (value->it_value) * 1000
2431 + (EMACS_USECS (value->it_value) + 999) / 1000);
2432
2433 if (remove_mac_atimer (&remaining_count) == 0)
2434 {
2435 if (ovalue)
2436 {
2437 bzero (ovalue, sizeof (*ovalue));
2438 EMACS_SET_SECS_USECS (ovalue->it_value, remaining_count / 1000,
2439 (remaining_count % 1000) * 1000);
2440 }
2441 set_mac_atimer (count);
2442 }
2443 else
2444 mac_atimer_task.tmCount = count;
2445
2446 return 0;
2447 }
2448
2449
2450 /* gettimeofday should return the amount of time (in a timeval
2451 structure) since midnight today. The toolbox function Microseconds
2452 returns the number of microseconds (in a UnsignedWide value) since
2453 the machine was booted. Also making this complicated is WideAdd,
2454 WideSubtract, etc. take wide values. */
2455
2456 int
2457 gettimeofday (tp)
2458 struct timeval *tp;
2459 {
2460 static inited = 0;
2461 static wide wall_clock_at_epoch, clicks_at_epoch;
2462 UnsignedWide uw_microseconds;
2463 wide w_microseconds;
2464 time_t sys_time (time_t *);
2465
2466 /* If this function is called for the first time, record the number
2467 of seconds since midnight and the number of microseconds since
2468 boot at the time of this first call. */
2469 if (!inited)
2470 {
2471 time_t systime;
2472 inited = 1;
2473 systime = sys_time (NULL);
2474 /* Store microseconds since midnight in wall_clock_at_epoch. */
2475 WideMultiply (systime, 1000000L, &wall_clock_at_epoch);
2476 Microseconds (&uw_microseconds);
2477 /* Store microseconds since boot in clicks_at_epoch. */
2478 clicks_at_epoch.hi = uw_microseconds.hi;
2479 clicks_at_epoch.lo = uw_microseconds.lo;
2480 }
2481
2482 /* Get time since boot */
2483 Microseconds (&uw_microseconds);
2484
2485 /* Convert to time since midnight*/
2486 w_microseconds.hi = uw_microseconds.hi;
2487 w_microseconds.lo = uw_microseconds.lo;
2488 WideSubtract (&w_microseconds, &clicks_at_epoch);
2489 WideAdd (&w_microseconds, &wall_clock_at_epoch);
2490 tp->tv_sec = WideDivide (&w_microseconds, 1000000L, &tp->tv_usec);
2491
2492 return 0;
2493 }
2494
2495
2496 #ifdef __MRC__
2497 unsigned int
2498 sleep (unsigned int seconds)
2499 {
2500 unsigned long time_up;
2501 EventRecord e;
2502
2503 time_up = TickCount () + seconds * 60;
2504 while (TickCount () < time_up)
2505 {
2506 /* Accept no event; just wait. by T.I. */
2507 WaitNextEvent (0, &e, 30, NULL);
2508 }
2509
2510 return (0);
2511 }
2512 #endif /* __MRC__ */
2513
2514
2515 /* The time functions adjust time values according to the difference
2516 between the Unix and CW epoches. */
2517
2518 #undef gmtime
2519 extern struct tm *gmtime (const time_t *);
2520 struct tm *
2521 sys_gmtime (const time_t *timer)
2522 {
2523 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
2524
2525 return gmtime (&unix_time);
2526 }
2527
2528
2529 #undef localtime
2530 extern struct tm *localtime (const time_t *);
2531 struct tm *
2532 sys_localtime (const time_t *timer)
2533 {
2534 #if __MSL__ >= 0x6000
2535 time_t unix_time = *timer;
2536 #else
2537 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
2538 #endif
2539
2540 return localtime (&unix_time);
2541 }
2542
2543
2544 #undef ctime
2545 extern char *ctime (const time_t *);
2546 char *
2547 sys_ctime (const time_t *timer)
2548 {
2549 #if __MSL__ >= 0x6000
2550 time_t unix_time = *timer;
2551 #else
2552 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
2553 #endif
2554
2555 return ctime (&unix_time);
2556 }
2557
2558
2559 #undef time
2560 extern time_t time (time_t *);
2561 time_t
2562 sys_time (time_t *timer)
2563 {
2564 #if __MSL__ >= 0x6000
2565 time_t mac_time = time (NULL);
2566 #else
2567 time_t mac_time = time (NULL) - CW_OR_MPW_UNIX_EPOCH_DIFF;
2568 #endif
2569
2570 if (timer)
2571 *timer = mac_time;
2572
2573 return mac_time;
2574 }
2575
2576
2577 /* no subprocesses, empty wait */
2578
2579 int
2580 wait (int pid)
2581 {
2582 return 0;
2583 }
2584
2585
2586 void
2587 croak (char *badfunc)
2588 {
2589 printf ("%s not yet implemented\r\n", badfunc);
2590 exit (1);
2591 }
2592
2593
2594 char *
2595 mktemp (char *template)
2596 {
2597 int len, k;
2598 static seqnum = 0;
2599
2600 len = strlen (template);
2601 k = len - 1;
2602 while (k >= 0 && template[k] == 'X')
2603 k--;
2604
2605 k++; /* make k index of first 'X' */
2606
2607 if (k < len)
2608 {
2609 /* Zero filled, number of digits equal to the number of X's. */
2610 sprintf (&template[k], "%0*d", len-k, seqnum++);
2611
2612 return template;
2613 }
2614 else
2615 return 0;
2616 }
2617
2618
2619 /* Emulate getpwuid, getpwnam and others. */
2620
2621 #define PASSWD_FIELD_SIZE 256
2622
2623 static char my_passwd_name[PASSWD_FIELD_SIZE];
2624 static char my_passwd_dir[MAXPATHLEN+1];
2625
2626 static struct passwd my_passwd =
2627 {
2628 my_passwd_name,
2629 my_passwd_dir,
2630 };
2631
2632 static struct group my_group =
2633 {
2634 /* There are no groups on the mac, so we just return "root" as the
2635 group name. */
2636 "root",
2637 };
2638
2639
2640 /* Initialized by main () in macterm.c to pathname of emacs directory. */
2641
2642 char emacs_passwd_dir[MAXPATHLEN+1];
2643
2644 char *
2645 getwd (char *);
2646
2647 void
2648 init_emacs_passwd_dir ()
2649 {
2650 int found = false;
2651
2652 if (getwd (emacs_passwd_dir) && getwd (my_passwd_dir))
2653 {
2654 /* Need pathname of first ancestor that begins with "emacs"
2655 since Mac emacs application is somewhere in the emacs-*
2656 tree. */
2657 int len = strlen (emacs_passwd_dir);
2658 int j = len - 1;
2659 /* j points to the "/" following the directory name being
2660 compared. */
2661 int i = j - 1;
2662 while (i >= 0 && !found)
2663 {
2664 while (i >= 0 && emacs_passwd_dir[i] != '/')
2665 i--;
2666 if (emacs_passwd_dir[i] == '/' && i+5 < len)
2667 found = (strncmp (&(emacs_passwd_dir[i+1]), "emacs", 5) == 0);
2668 if (found)
2669 emacs_passwd_dir[j+1] = '\0';
2670 else
2671 {
2672 j = i;
2673 i = j - 1;
2674 }
2675 }
2676 }
2677
2678 if (!found)
2679 {
2680 /* Setting to "/" probably won't work but set it to something
2681 anyway. */
2682 strcpy (emacs_passwd_dir, "/");
2683 strcpy (my_passwd_dir, "/");
2684 }
2685 }
2686
2687
2688 static struct passwd emacs_passwd =
2689 {
2690 "emacs",
2691 emacs_passwd_dir,
2692 };
2693
2694 static int my_passwd_inited = 0;
2695
2696
2697 static void
2698 init_my_passwd ()
2699 {
2700 char **owner_name;
2701
2702 /* Note: my_passwd_dir initialized in int_emacs_passwd_dir to
2703 directory where Emacs was started. */
2704
2705 owner_name = (char **) GetResource ('STR ',-16096);
2706 if (owner_name)
2707 {
2708 HLock (owner_name);
2709 BlockMove ((unsigned char *) *owner_name,
2710 (unsigned char *) my_passwd_name,
2711 *owner_name[0]+1);
2712 HUnlock (owner_name);
2713 p2cstr ((unsigned char *) my_passwd_name);
2714 }
2715 else
2716 my_passwd_name[0] = 0;
2717 }
2718
2719
2720 struct passwd *
2721 getpwuid (uid_t uid)
2722 {
2723 if (!my_passwd_inited)
2724 {
2725 init_my_passwd ();
2726 my_passwd_inited = 1;
2727 }
2728
2729 return &my_passwd;
2730 }
2731
2732
2733 struct group *
2734 getgrgid (gid_t gid)
2735 {
2736 return &my_group;
2737 }
2738
2739
2740 struct passwd *
2741 getpwnam (const char *name)
2742 {
2743 if (strcmp (name, "emacs") == 0)
2744 return &emacs_passwd;
2745
2746 if (!my_passwd_inited)
2747 {
2748 init_my_passwd ();
2749 my_passwd_inited = 1;
2750 }
2751
2752 return &my_passwd;
2753 }
2754
2755
2756 /* The functions fork, kill, sigsetmask, sigblock, request_sigio,
2757 setpgrp, setpriority, and unrequest_sigio are defined to be empty
2758 as in msdos.c. */
2759
2760
2761 int
2762 fork ()
2763 {
2764 return -1;
2765 }
2766
2767
2768 int
2769 kill (int x, int y)
2770 {
2771 return -1;
2772 }
2773
2774
2775 void
2776 sys_subshell ()
2777 {
2778 error ("Can't spawn subshell");
2779 }
2780
2781
2782 void
2783 request_sigio (void)
2784 {
2785 }
2786
2787
2788 void
2789 unrequest_sigio (void)
2790 {
2791 }
2792
2793
2794 int
2795 setpgrp ()
2796 {
2797 return 0;
2798 }
2799
2800
2801 /* No pipes yet. */
2802
2803 int
2804 pipe (int _fildes[2])
2805 {
2806 errno = EACCES;
2807 return -1;
2808 }
2809
2810
2811 /* Hard and symbolic links. */
2812
2813 int
2814 symlink (const char *name1, const char *name2)
2815 {
2816 errno = ENOENT;
2817 return -1;
2818 }
2819
2820
2821 int
2822 link (const char *name1, const char *name2)
2823 {
2824 errno = ENOENT;
2825 return -1;
2826 }
2827
2828 #endif /* ! MAC_OSX */
2829
2830 /* Determine the path name of the file specified by VREFNUM, DIRID,
2831 and NAME and place that in the buffer PATH of length
2832 MAXPATHLEN. */
2833 int
2834 path_from_vol_dir_name (char *path, int man_path_len, short vol_ref_num,
2835 long dir_id, ConstStr255Param name)
2836 {
2837 Str255 dir_name;
2838 CInfoPBRec cipb;
2839 OSErr err;
2840
2841 if (strlen (name) > man_path_len)
2842 return 0;
2843
2844 memcpy (dir_name, name, name[0]+1);
2845 memcpy (path, name, name[0]+1);
2846 p2cstr (path);
2847
2848 cipb.dirInfo.ioDrParID = dir_id;
2849 cipb.dirInfo.ioNamePtr = dir_name;
2850
2851 do
2852 {
2853 cipb.dirInfo.ioVRefNum = vol_ref_num;
2854 cipb.dirInfo.ioFDirIndex = -1;
2855 cipb.dirInfo.ioDrDirID = cipb.dirInfo.ioDrParID;
2856 /* go up to parent each time */
2857
2858 err = PBGetCatInfo (&cipb, false);
2859 if (err != noErr)
2860 return 0;
2861
2862 p2cstr (dir_name);
2863 if (strlen (dir_name) + strlen (path) + 1 >= man_path_len)
2864 return 0;
2865
2866 strcat (dir_name, ":");
2867 strcat (dir_name, path);
2868 /* attach to front since we're going up directory tree */
2869 strcpy (path, dir_name);
2870 }
2871 while (cipb.dirInfo.ioDrDirID != fsRtDirID);
2872 /* stop when we see the volume's root directory */
2873
2874 return 1; /* success */
2875 }
2876
2877
2878 static OSErr
2879 posix_pathname_to_fsspec (ufn, fs)
2880 const char *ufn;
2881 FSSpec *fs;
2882 {
2883 Str255 mac_pathname;
2884
2885 if (posix_to_mac_pathname (ufn, mac_pathname, sizeof (mac_pathname)) == 0)
2886 return fnfErr;
2887 else
2888 {
2889 c2pstr (mac_pathname);
2890 return FSMakeFSSpec (0, 0, mac_pathname, fs);
2891 }
2892 }
2893
2894 static OSErr
2895 fsspec_to_posix_pathname (fs, ufn, ufnbuflen)
2896 const FSSpec *fs;
2897 char *ufn;
2898 int ufnbuflen;
2899 {
2900 char mac_pathname[MAXPATHLEN];
2901
2902 if (path_from_vol_dir_name (mac_pathname, sizeof (mac_pathname) - 1,
2903 fs->vRefNum, fs->parID, fs->name)
2904 && mac_to_posix_pathname (mac_pathname, ufn, ufnbuflen))
2905 return noErr;
2906 else
2907 return fnfErr;
2908 }
2909
2910 #ifndef MAC_OSX
2911
2912 int
2913 readlink (const char *path, char *buf, int bufsiz)
2914 {
2915 char mac_sym_link_name[MAXPATHLEN+1];
2916 OSErr err;
2917 FSSpec fsspec;
2918 Boolean target_is_folder, was_aliased;
2919 Str255 directory_name, mac_pathname;
2920 CInfoPBRec cipb;
2921
2922 if (posix_to_mac_pathname (path, mac_sym_link_name, MAXPATHLEN+1) == 0)
2923 return -1;
2924
2925 c2pstr (mac_sym_link_name);
2926 err = FSMakeFSSpec (0, 0, mac_sym_link_name, &fsspec);
2927 if (err != noErr)
2928 {
2929 errno = ENOENT;
2930 return -1;
2931 }
2932
2933 err = ResolveAliasFile (&fsspec, true, &target_is_folder, &was_aliased);
2934 if (err != noErr || !was_aliased)
2935 {
2936 errno = ENOENT;
2937 return -1;
2938 }
2939
2940 if (path_from_vol_dir_name (mac_pathname, 255, fsspec.vRefNum, fsspec.parID,
2941 fsspec.name) == 0)
2942 {
2943 errno = ENOENT;
2944 return -1;
2945 }
2946
2947 if (mac_to_posix_pathname (mac_pathname, buf, bufsiz) == 0)
2948 {
2949 errno = ENOENT;
2950 return -1;
2951 }
2952
2953 return strlen (buf);
2954 }
2955
2956
2957 /* Convert a path to one with aliases fully expanded. */
2958
2959 static int
2960 find_true_pathname (const char *path, char *buf, int bufsiz)
2961 {
2962 char *q, temp[MAXPATHLEN+1];
2963 const char *p;
2964 int len;
2965
2966 if (bufsiz <= 0 || path == 0 || path[0] == '\0')
2967 return -1;
2968
2969 buf[0] = '\0';
2970
2971 p = path;
2972 if (*p == '/')
2973 q = strchr (p + 1, '/');
2974 else
2975 q = strchr (p, '/');
2976 len = 0; /* loop may not be entered, e.g., for "/" */
2977
2978 while (q)
2979 {
2980 strcpy (temp, buf);
2981 strncat (temp, p, q - p);
2982 len = readlink (temp, buf, bufsiz);
2983 if (len <= -1)
2984 {
2985 if (strlen (temp) + 1 > bufsiz)
2986 return -1;
2987 strcpy (buf, temp);
2988 }
2989 strcat (buf, "/");
2990 len++;
2991 p = q + 1;
2992 q = strchr(p, '/');
2993 }
2994
2995 if (len + strlen (p) + 1 >= bufsiz)
2996 return -1;
2997
2998 strcat (buf, p);
2999 return len + strlen (p);
3000 }
3001
3002
3003 mode_t
3004 umask (mode_t numask)
3005 {
3006 static mode_t mask = 022;
3007 mode_t oldmask = mask;
3008 mask = numask;
3009 return oldmask;
3010 }
3011
3012
3013 int
3014 chmod (const char *path, mode_t mode)
3015 {
3016 /* say it always succeed for now */
3017 return 0;
3018 }
3019
3020
3021 int
3022 fchmod (int fd, mode_t mode)
3023 {
3024 /* say it always succeed for now */
3025 return 0;
3026 }
3027
3028
3029 int
3030 fchown (int fd, uid_t owner, gid_t group)
3031 {
3032 /* say it always succeed for now */
3033 return 0;
3034 }
3035
3036
3037 int
3038 dup (int oldd)
3039 {
3040 #ifdef __MRC__
3041 return fcntl (oldd, F_DUPFD, 0);
3042 #elif __MWERKS__
3043 /* current implementation of fcntl in fcntl.mac.c simply returns old
3044 descriptor */
3045 return fcntl (oldd, F_DUPFD);
3046 #else
3047 You lose!!!
3048 #endif
3049 }
3050
3051
3052 /* This is from the original sysdep.c. Emulate BSD dup2. First close
3053 newd if it already exists. Then, attempt to dup oldd. If not
3054 successful, call dup2 recursively until we are, then close the
3055 unsuccessful ones. */
3056
3057 int
3058 dup2 (int oldd, int newd)
3059 {
3060 int fd, ret;
3061
3062 close (newd);
3063
3064 fd = dup (oldd);
3065 if (fd == -1)
3066 return -1;
3067 if (fd == newd)
3068 return newd;
3069 ret = dup2 (oldd, newd);
3070 close (fd);
3071 return ret;
3072 }
3073
3074
3075 /* let it fail for now */
3076
3077 char *
3078 sbrk (int incr)
3079 {
3080 return (char *) -1;
3081 }
3082
3083
3084 int
3085 fsync (int fd)
3086 {
3087 return 0;
3088 }
3089
3090
3091 int
3092 ioctl (int d, int request, void *argp)
3093 {
3094 return -1;
3095 }
3096
3097
3098 #ifdef __MRC__
3099 int
3100 isatty (int fildes)
3101 {
3102 if (fildes >=0 && fildes <= 2)
3103 return 1;
3104 else
3105 return 0;
3106 }
3107
3108
3109 int
3110 getgid ()
3111 {
3112 return 100;
3113 }
3114
3115
3116 int
3117 getegid ()
3118 {
3119 return 100;
3120 }
3121
3122
3123 int
3124 getuid ()
3125 {
3126 return 200;
3127 }
3128
3129
3130 int
3131 geteuid ()
3132 {
3133 return 200;
3134 }
3135 #endif /* __MRC__ */
3136
3137
3138 #ifdef __MWERKS__
3139 #if __MSL__ < 0x6000
3140 #undef getpid
3141 int
3142 getpid ()
3143 {
3144 return 9999;
3145 }
3146 #endif
3147 #endif /* __MWERKS__ */
3148
3149 #endif /* ! MAC_OSX */
3150
3151
3152 /* Return the path to the directory in which Emacs can create
3153 temporary files. The MacOS "temporary items" directory cannot be
3154 used because it removes the file written by a process when it
3155 exits. In that sense it's more like "/dev/null" than "/tmp" (but
3156 again not exactly). And of course Emacs needs to read back the
3157 files written by its subprocesses. So here we write the files to a
3158 directory "Emacs" in the Preferences Folder. This directory is
3159 created if it does not exist. */
3160
3161 char *
3162 get_temp_dir_name ()
3163 {
3164 static char *temp_dir_name = NULL;
3165 short vol_ref_num;
3166 long dir_id;
3167 OSErr err;
3168 Str255 dir_name, full_path;
3169 CInfoPBRec cpb;
3170 char unix_dir_name[MAXPATHLEN+1];
3171 DIR *dir;
3172
3173 /* Cache directory name with pointer temp_dir_name.
3174 Look for it only the first time. */
3175 if (!temp_dir_name)
3176 {
3177 err = FindFolder (kOnSystemDisk, kPreferencesFolderType, kCreateFolder,
3178 &vol_ref_num, &dir_id);
3179 if (err != noErr)
3180 return NULL;
3181
3182 if (!path_from_vol_dir_name (full_path, 255, vol_ref_num, dir_id, "\p"))
3183 return NULL;
3184
3185 if (strlen (full_path) + 6 <= MAXPATHLEN)
3186 strcat (full_path, "Emacs:");
3187 else
3188 return NULL;
3189
3190 if (!mac_to_posix_pathname (full_path, unix_dir_name, MAXPATHLEN+1))
3191 return NULL;
3192
3193 dir = opendir (unix_dir_name); /* check whether temp directory exists */
3194 if (dir)
3195 closedir (dir);
3196 else if (mkdir (unix_dir_name, 0700) != 0) /* create it if not */
3197 return NULL;
3198
3199 temp_dir_name = (char *) malloc (strlen (unix_dir_name) + 1);
3200 strcpy (temp_dir_name, unix_dir_name);
3201 }
3202
3203 return temp_dir_name;
3204 }
3205
3206 #ifndef MAC_OSX
3207
3208 /* Allocate and construct an array of pointers to strings from a list
3209 of strings stored in a 'STR#' resource. The returned pointer array
3210 is stored in the style of argv and environ: if the 'STR#' resource
3211 contains numString strings, a pointer array with numString+1
3212 elements is returned in which the last entry contains a null
3213 pointer. The pointer to the pointer array is passed by pointer in
3214 parameter t. The resource ID of the 'STR#' resource is passed in
3215 parameter StringListID.
3216 */
3217
3218 void
3219 get_string_list (char ***t, short string_list_id)
3220 {
3221 Handle h;
3222 Ptr p;
3223 int i, num_strings;
3224
3225 h = GetResource ('STR#', string_list_id);
3226 if (h)
3227 {
3228 HLock (h);
3229 p = *h;
3230 num_strings = * (short *) p;
3231 p += sizeof(short);
3232 *t = (char **) malloc (sizeof (char *) * (num_strings + 1));
3233 for (i = 0; i < num_strings; i++)
3234 {
3235 short length = *p++;
3236 (*t)[i] = (char *) malloc (length + 1);
3237 strncpy ((*t)[i], p, length);
3238 (*t)[i][length] = '\0';
3239 p += length;
3240 }
3241 (*t)[num_strings] = 0;
3242 HUnlock (h);
3243 }
3244 else
3245 {
3246 /* Return no string in case GetResource fails. Bug fixed by
3247 Ikegami Tsutomu. Caused MPW build to crash without sym -on
3248 option (no sym -on implies -opt local). */
3249 *t = (char **) malloc (sizeof (char *));
3250 (*t)[0] = 0;
3251 }
3252 }
3253
3254
3255 static char *
3256 get_path_to_system_folder ()
3257 {
3258 short vol_ref_num;
3259 long dir_id;
3260 OSErr err;
3261 Str255 dir_name, full_path;
3262 CInfoPBRec cpb;
3263 static char system_folder_unix_name[MAXPATHLEN+1];
3264 DIR *dir;
3265
3266 err = FindFolder (kOnSystemDisk, kSystemFolderType, kDontCreateFolder,
3267 &vol_ref_num, &dir_id);
3268 if (err != noErr)
3269 return NULL;
3270
3271 if (!path_from_vol_dir_name (full_path, 255, vol_ref_num, dir_id, "\p"))
3272 return NULL;
3273
3274 if (!mac_to_posix_pathname (full_path, system_folder_unix_name,
3275 MAXPATHLEN+1))
3276 return NULL;
3277
3278 return system_folder_unix_name;
3279 }
3280
3281
3282 char **environ;
3283
3284 #define ENVIRON_STRING_LIST_ID 128
3285
3286 /* Get environment variable definitions from STR# resource. */
3287
3288 void
3289 init_environ ()
3290 {
3291 int i;
3292
3293 get_string_list (&environ, ENVIRON_STRING_LIST_ID);
3294
3295 i = 0;
3296 while (environ[i])
3297 i++;
3298
3299 /* Make HOME directory the one Emacs starts up in if not specified
3300 by resource. */
3301 if (getenv ("HOME") == NULL)
3302 {
3303 environ = (char **) realloc (environ, sizeof (char *) * (i + 2));
3304 if (environ)
3305 {
3306 environ[i] = (char *) malloc (strlen (my_passwd_dir) + 6);
3307 if (environ[i])
3308 {
3309 strcpy (environ[i], "HOME=");
3310 strcat (environ[i], my_passwd_dir);
3311 }
3312 environ[i+1] = 0;
3313 i++;
3314 }
3315 }
3316
3317 /* Make HOME directory the one Emacs starts up in if not specified
3318 by resource. */
3319 if (getenv ("MAIL") == NULL)
3320 {
3321 environ = (char **) realloc (environ, sizeof (char *) * (i + 2));
3322 if (environ)
3323 {
3324 char * path_to_system_folder = get_path_to_system_folder ();
3325 environ[i] = (char *) malloc (strlen (path_to_system_folder) + 22);
3326 if (environ[i])
3327 {
3328 strcpy (environ[i], "MAIL=");
3329 strcat (environ[i], path_to_system_folder);
3330 strcat (environ[i], "Eudora Folder/In");
3331 }
3332 environ[i+1] = 0;
3333 }
3334 }
3335 }
3336
3337
3338 /* Return the value of the environment variable NAME. */
3339
3340 char *
3341 getenv (const char *name)
3342 {
3343 int length = strlen(name);
3344 char **e;
3345
3346 for (e = environ; *e != 0; e++)
3347 if (strncmp(*e, name, length) == 0 && (*e)[length] == '=')
3348 return &(*e)[length + 1];
3349
3350 if (strcmp (name, "TMPDIR") == 0)
3351 return get_temp_dir_name ();
3352
3353 return 0;
3354 }
3355
3356
3357 #ifdef __MRC__
3358 /* see Interfaces&Libraries:Interfaces:CIncludes:signal.h */
3359 char *sys_siglist[] =
3360 {
3361 "Zero is not a signal!!!",
3362 "Abort", /* 1 */
3363 "Interactive user interrupt", /* 2 */ "?",
3364 "Floating point exception", /* 4 */ "?", "?", "?",
3365 "Illegal instruction", /* 8 */ "?", "?", "?", "?", "?", "?", "?",
3366 "Segment violation", /* 16 */ "?", "?", "?", "?", "?", "?", "?",
3367 "?", "?", "?", "?", "?", "?", "?", "?",
3368 "Terminal" /* 32 */
3369 };
3370 #elif __MWERKS__
3371 char *sys_siglist[] =
3372 {
3373 "Zero is not a signal!!!",
3374 "Abort",
3375 "Floating point exception",
3376 "Illegal instruction",
3377 "Interactive user interrupt",
3378 "Segment violation",
3379 "Terminal"
3380 };
3381 #else /* not __MRC__ and not __MWERKS__ */
3382 You lose!!!
3383 #endif /* not __MRC__ and not __MWERKS__ */
3384
3385
3386 #include <utsname.h>
3387
3388 int
3389 uname (struct utsname *name)
3390 {
3391 char **system_name;
3392 system_name = GetString (-16413); /* IM - Resource Manager Reference */
3393 if (system_name)
3394 {
3395 BlockMove (*system_name, name->nodename, (*system_name)[0]+1);
3396 p2cstr (name->nodename);
3397 return 0;
3398 }
3399 else
3400 return -1;
3401 }
3402
3403
3404 /* Event class of HLE sent to subprocess. */
3405 const OSType kEmacsSubprocessSend = 'ESND';
3406
3407 /* Event class of HLE sent back from subprocess. */
3408 const OSType kEmacsSubprocessReply = 'ERPY';
3409
3410
3411 char *
3412 mystrchr (char *s, char c)
3413 {
3414 while (*s && *s != c)
3415 {
3416 if (*s == '\\')
3417 s++;
3418 s++;
3419 }
3420
3421 if (*s)
3422 {
3423 *s = '\0';
3424 return s;
3425 }
3426 else
3427 return NULL;
3428 }
3429
3430
3431 char *
3432 mystrtok (char *s)
3433 {
3434 while (*s)
3435 s++;
3436
3437 return s + 1;
3438 }
3439
3440
3441 void
3442 mystrcpy (char *to, char *from)
3443 {
3444 while (*from)
3445 {
3446 if (*from == '\\')
3447 from++;
3448 *to++ = *from++;
3449 }
3450 *to = '\0';
3451 }
3452
3453
3454 /* Start a Mac subprocess. Arguments for it is passed in argv (null
3455 terminated). The process should run with the default directory
3456 "workdir", read input from "infn", and write output and error to
3457 "outfn" and "errfn", resp. The Process Manager call
3458 LaunchApplication is used to start the subprocess. We use high
3459 level events as the mechanism to pass arguments to the subprocess
3460 and to make Emacs wait for the subprocess to terminate and pass
3461 back a result code. The bulk of the code here packs the arguments
3462 into one message to be passed together with the high level event.
3463 Emacs also sometimes starts a subprocess using a shell to perform
3464 wildcard filename expansion. Since we don't really have a shell on
3465 the Mac, this case is detected and the starting of the shell is
3466 by-passed. We really need to add code here to do filename
3467 expansion to support such functionality.
3468
3469 We can't use this strategy in Carbon because the High Level Event
3470 APIs are not available. */
3471
3472 int
3473 run_mac_command (argv, workdir, infn, outfn, errfn)
3474 unsigned char **argv;
3475 const char *workdir;
3476 const char *infn, *outfn, *errfn;
3477 {
3478 #if TARGET_API_MAC_CARBON
3479 return -1;
3480 #else /* not TARGET_API_MAC_CARBON */
3481 char macappname[MAXPATHLEN+1], macworkdir[MAXPATHLEN+1];
3482 char macinfn[MAXPATHLEN+1], macoutfn[MAXPATHLEN+1], macerrfn[MAXPATHLEN+1];
3483 int paramlen, argc, newargc, j, retries;
3484 char **newargv, *param, *p;
3485 OSErr iErr;
3486 FSSpec spec;
3487 LaunchParamBlockRec lpbr;
3488 EventRecord send_event, reply_event;
3489 RgnHandle cursor_region_handle;
3490 TargetID targ;
3491 unsigned long ref_con, len;
3492
3493 if (posix_to_mac_pathname (workdir, macworkdir, MAXPATHLEN+1) == 0)
3494 return -1;
3495 if (posix_to_mac_pathname (infn, macinfn, MAXPATHLEN+1) == 0)
3496 return -1;
3497 if (posix_to_mac_pathname (outfn, macoutfn, MAXPATHLEN+1) == 0)
3498 return -1;
3499 if (posix_to_mac_pathname (errfn, macerrfn, MAXPATHLEN+1) == 0)
3500 return -1;
3501
3502 paramlen = strlen (macworkdir) + strlen (macinfn) + strlen (macoutfn)
3503 + strlen (macerrfn) + 4; /* count nulls at end of strings */
3504
3505 argc = 0;
3506 while (argv[argc])
3507 argc++;
3508
3509 if (argc == 0)
3510 return -1;
3511
3512 /* If a subprocess is invoked with a shell, we receive 3 arguments
3513 of the form: "<path to emacs bins>/sh" "-c" "<path to emacs
3514 bins>/<command> <command args>" */
3515 j = strlen (argv[0]);
3516 if (j >= 3 && strcmp (argv[0]+j-3, "/sh") == 0
3517 && argc == 3 && strcmp (argv[1], "-c") == 0)
3518 {
3519 char *command, *t, tempmacpathname[MAXPATHLEN+1];
3520
3521 /* The arguments for the command in argv[2] are separated by
3522 spaces. Count them and put the count in newargc. */
3523 command = (char *) alloca (strlen (argv[2])+2);
3524 strcpy (command, argv[2]);
3525 if (command[strlen (command) - 1] != ' ')
3526 strcat (command, " ");
3527
3528 t = command;
3529 newargc = 0;
3530 t = mystrchr (t, ' ');
3531 while (t)
3532 {
3533 newargc++;
3534 t = mystrchr (t+1, ' ');
3535 }
3536
3537 newargv = (char **) alloca (sizeof (char *) * newargc);
3538
3539 t = command;
3540 for (j = 0; j < newargc; j++)
3541 {
3542 newargv[j] = (char *) alloca (strlen (t) + 1);
3543 mystrcpy (newargv[j], t);
3544
3545 t = mystrtok (t);
3546 paramlen += strlen (newargv[j]) + 1;
3547 }
3548
3549 if (strncmp (newargv[0], "~emacs/", 7) == 0)
3550 {
3551 if (posix_to_mac_pathname (newargv[0], tempmacpathname, MAXPATHLEN+1)
3552 == 0)
3553 return -1;
3554 }
3555 else
3556 { /* sometimes Emacs call "sh" without a path for the command */
3557 #if 0
3558 char *t = (char *) alloca (strlen (newargv[0]) + 7 + 1);
3559 strcpy (t, "~emacs/");
3560 strcat (t, newargv[0]);
3561 #endif /* 0 */
3562 Lisp_Object path;
3563 openp (Vexec_path, build_string (newargv[0]), Vexec_suffixes, &path,
3564 make_number (X_OK));
3565
3566 if (NILP (path))
3567 return -1;
3568 if (posix_to_mac_pathname (SDATA (path), tempmacpathname,
3569 MAXPATHLEN+1) == 0)
3570 return -1;
3571 }
3572 strcpy (macappname, tempmacpathname);
3573 }
3574 else
3575 {
3576 if (posix_to_mac_pathname (argv[0], macappname, MAXPATHLEN+1) == 0)
3577 return -1;
3578
3579 newargv = (char **) alloca (sizeof (char *) * argc);
3580 newargc = argc;
3581 for (j = 1; j < argc; j++)
3582 {
3583 if (strncmp (argv[j], "~emacs/", 7) == 0)
3584 {
3585 char *t = strchr (argv[j], ' ');
3586 if (t)
3587 {
3588 char tempcmdname[MAXPATHLEN+1], tempmaccmdname[MAXPATHLEN+1];
3589 strncpy (tempcmdname, argv[j], t-argv[j]);
3590 tempcmdname[t-argv[j]] = '\0';
3591 if (posix_to_mac_pathname (tempcmdname, tempmaccmdname,
3592 MAXPATHLEN+1) == 0)
3593 return -1;
3594 newargv[j] = (char *) alloca (strlen (tempmaccmdname)
3595 + strlen (t) + 1);
3596 strcpy (newargv[j], tempmaccmdname);
3597 strcat (newargv[j], t);
3598 }
3599 else
3600 {
3601 char tempmaccmdname[MAXPATHLEN+1];
3602 if (posix_to_mac_pathname (argv[j], tempmaccmdname,
3603 MAXPATHLEN+1) == 0)
3604 return -1;
3605 newargv[j] = (char *) alloca (strlen (tempmaccmdname)+1);
3606 strcpy (newargv[j], tempmaccmdname);
3607 }
3608 }
3609 else
3610 newargv[j] = argv[j];
3611 paramlen += strlen (newargv[j]) + 1;
3612 }
3613 }
3614
3615 /* After expanding all the arguments, we now know the length of the
3616 parameter block to be sent to the subprocess as a message
3617 attached to the HLE. */
3618 param = (char *) malloc (paramlen + 1);
3619 if (!param)
3620 return -1;
3621
3622 p = param;
3623 *p++ = newargc;
3624 /* first byte of message contains number of arguments for command */
3625 strcpy (p, macworkdir);
3626 p += strlen (macworkdir);
3627 *p++ = '\0';
3628 /* null terminate strings sent so it's possible to use strcpy over there */
3629 strcpy (p, macinfn);
3630 p += strlen (macinfn);
3631 *p++ = '\0';
3632 strcpy (p, macoutfn);
3633 p += strlen (macoutfn);
3634 *p++ = '\0';
3635 strcpy (p, macerrfn);
3636 p += strlen (macerrfn);
3637 *p++ = '\0';
3638 for (j = 1; j < newargc; j++)
3639 {
3640 strcpy (p, newargv[j]);
3641 p += strlen (newargv[j]);
3642 *p++ = '\0';
3643 }
3644
3645 c2pstr (macappname);
3646
3647 iErr = FSMakeFSSpec (0, 0, macappname, &spec);
3648
3649 if (iErr != noErr)
3650 {
3651 free (param);
3652 return -1;
3653 }
3654
3655 lpbr.launchBlockID = extendedBlock;
3656 lpbr.launchEPBLength = extendedBlockLen;
3657 lpbr.launchControlFlags = launchContinue + launchNoFileFlags;
3658 lpbr.launchAppSpec = &spec;
3659 lpbr.launchAppParameters = NULL;
3660
3661 iErr = LaunchApplication (&lpbr); /* call the subprocess */
3662 if (iErr != noErr)
3663 {
3664 free (param);
3665 return -1;
3666 }
3667
3668 send_event.what = kHighLevelEvent;
3669 send_event.message = kEmacsSubprocessSend;
3670 /* Event ID stored in "where" unused */
3671
3672 retries = 3;
3673 /* OS may think current subprocess has terminated if previous one
3674 terminated recently. */
3675 do
3676 {
3677 iErr = PostHighLevelEvent (&send_event, &lpbr.launchProcessSN, 0, param,
3678 paramlen + 1, receiverIDisPSN);
3679 }
3680 while (iErr == sessClosedErr && retries-- > 0);
3681
3682 if (iErr != noErr)
3683 {
3684 free (param);
3685 return -1;
3686 }
3687
3688 cursor_region_handle = NewRgn ();
3689
3690 /* Wait for the subprocess to finish, when it will send us a ERPY
3691 high level event. */
3692 while (1)
3693 if (WaitNextEvent (highLevelEventMask, &reply_event, 180,
3694 cursor_region_handle)
3695 && reply_event.message == kEmacsSubprocessReply)
3696 break;
3697
3698 /* The return code is sent through the refCon */
3699 iErr = AcceptHighLevelEvent (&targ, &ref_con, NULL, &len);
3700 if (iErr != noErr)
3701 {
3702 DisposeHandle ((Handle) cursor_region_handle);
3703 free (param);
3704 return -1;
3705 }
3706
3707 DisposeHandle ((Handle) cursor_region_handle);
3708 free (param);
3709
3710 return ref_con;
3711 #endif /* not TARGET_API_MAC_CARBON */
3712 }
3713
3714
3715 DIR *
3716 opendir (const char *dirname)
3717 {
3718 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
3719 char mac_pathname[MAXPATHLEN+1], vol_name[MAXPATHLEN+1];
3720 DIR *dirp;
3721 CInfoPBRec cipb;
3722 HVolumeParam vpb;
3723 int len, vol_name_len;
3724
3725 if (find_true_pathname (dirname, true_pathname, MAXPATHLEN+1) == -1)
3726 return 0;
3727
3728 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
3729 if (len > -1)
3730 fully_resolved_name[len] = '\0';
3731 else
3732 strcpy (fully_resolved_name, true_pathname);
3733
3734 dirp = (DIR *) malloc (sizeof(DIR));
3735 if (!dirp)
3736 return 0;
3737
3738 /* Handle special case when dirname is "/": sets up for readir to
3739 get all mount volumes. */
3740 if (strcmp (fully_resolved_name, "/") == 0)
3741 {
3742 dirp->getting_volumes = 1; /* special all mounted volumes DIR struct */
3743 dirp->current_index = 1; /* index for first volume */
3744 return dirp;
3745 }
3746
3747 /* Handle typical cases: not accessing all mounted volumes. */
3748 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
3749 return 0;
3750
3751 /* Emacs calls opendir without the trailing '/', Mac needs trailing ':' */
3752 len = strlen (mac_pathname);
3753 if (mac_pathname[len - 1] != ':' && len < MAXPATHLEN)
3754 strcat (mac_pathname, ":");
3755
3756 /* Extract volume name */
3757 vol_name_len = strchr (mac_pathname, ':') - mac_pathname;
3758 strncpy (vol_name, mac_pathname, vol_name_len);
3759 vol_name[vol_name_len] = '\0';
3760 strcat (vol_name, ":");
3761
3762 c2pstr (mac_pathname);
3763 cipb.hFileInfo.ioNamePtr = mac_pathname;
3764 /* using full pathname so vRefNum and DirID ignored */
3765 cipb.hFileInfo.ioVRefNum = 0;
3766 cipb.hFileInfo.ioDirID = 0;
3767 cipb.hFileInfo.ioFDirIndex = 0;
3768 /* set to 0 to get information about specific dir or file */
3769
3770 errno = PBGetCatInfo (&cipb, false);
3771 if (errno != noErr)
3772 {
3773 errno = ENOENT;
3774 return 0;
3775 }
3776
3777 if (!(cipb.hFileInfo.ioFlAttrib & 0x10)) /* bit 4 = 1 for directories */
3778 return 0; /* not a directory */
3779
3780 dirp->dir_id = cipb.dirInfo.ioDrDirID; /* used later in readdir */
3781 dirp->getting_volumes = 0;
3782 dirp->current_index = 1; /* index for first file/directory */
3783
3784 c2pstr (vol_name);
3785 vpb.ioNamePtr = vol_name;
3786 /* using full pathname so vRefNum and DirID ignored */
3787 vpb.ioVRefNum = 0;
3788 vpb.ioVolIndex = -1;
3789 errno = PBHGetVInfo ((union HParamBlockRec *) &vpb, false);
3790 if (errno != noErr)
3791 {
3792 errno = ENOENT;
3793 return 0;
3794 }
3795
3796 dirp->vol_ref_num = vpb.ioVRefNum;
3797
3798 return dirp;
3799 }
3800
3801 int
3802 closedir (DIR *dp)
3803 {
3804 free (dp);
3805
3806 return 0;
3807 }
3808
3809
3810 struct dirent *
3811 readdir (DIR *dp)
3812 {
3813 HParamBlockRec hpblock;
3814 CInfoPBRec cipb;
3815 static struct dirent s_dirent;
3816 static Str255 s_name;
3817 int done;
3818 char *p;
3819
3820 /* Handle the root directory containing the mounted volumes. Call
3821 PBHGetVInfo specifying an index to obtain the info for a volume.
3822 PBHGetVInfo returns an error when it receives an index beyond the
3823 last volume, at which time we should return a nil dirent struct
3824 pointer. */
3825 if (dp->getting_volumes)
3826 {
3827 hpblock.volumeParam.ioNamePtr = s_name;
3828 hpblock.volumeParam.ioVRefNum = 0;
3829 hpblock.volumeParam.ioVolIndex = dp->current_index;
3830
3831 errno = PBHGetVInfo (&hpblock, false);
3832 if (errno != noErr)
3833 {
3834 errno = ENOENT;
3835 return 0;
3836 }
3837
3838 p2cstr (s_name);
3839 strcat (s_name, "/"); /* need "/" for stat to work correctly */
3840
3841 dp->current_index++;
3842
3843 s_dirent.d_ino = hpblock.volumeParam.ioVRefNum;
3844 s_dirent.d_name = s_name;
3845
3846 return &s_dirent;
3847 }
3848 else
3849 {
3850 cipb.hFileInfo.ioVRefNum = dp->vol_ref_num;
3851 cipb.hFileInfo.ioNamePtr = s_name;
3852 /* location to receive filename returned */
3853
3854 /* return only visible files */
3855 done = false;
3856 while (!done)
3857 {
3858 cipb.hFileInfo.ioDirID = dp->dir_id;
3859 /* directory ID found by opendir */
3860 cipb.hFileInfo.ioFDirIndex = dp->current_index;
3861
3862 errno = PBGetCatInfo (&cipb, false);
3863 if (errno != noErr)
3864 {
3865 errno = ENOENT;
3866 return 0;
3867 }
3868
3869 /* insist on a visible entry */
3870 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* directory? */
3871 done = !(cipb.dirInfo.ioDrUsrWds.frFlags & fInvisible);
3872 else
3873 done = !(cipb.hFileInfo.ioFlFndrInfo.fdFlags & fInvisible);
3874
3875 dp->current_index++;
3876 }
3877
3878 p2cstr (s_name);
3879
3880 p = s_name;
3881 while (*p)
3882 {
3883 if (*p == '/')
3884 *p = ':';
3885 p++;
3886 }
3887
3888 s_dirent.d_ino = cipb.dirInfo.ioDrDirID;
3889 /* value unimportant: non-zero for valid file */
3890 s_dirent.d_name = s_name;
3891
3892 return &s_dirent;
3893 }
3894 }
3895
3896
3897 char *
3898 getwd (char *path)
3899 {
3900 char mac_pathname[MAXPATHLEN+1];
3901 Str255 directory_name;
3902 OSErr errno;
3903 CInfoPBRec cipb;
3904
3905 if (path_from_vol_dir_name (mac_pathname, 255, 0, 0, "\p") == 0)
3906 return NULL;
3907
3908 if (mac_to_posix_pathname (mac_pathname, path, MAXPATHLEN+1) == 0)
3909 return 0;
3910 else
3911 return path;
3912 }
3913
3914 #endif /* ! MAC_OSX */
3915
3916
3917 void
3918 initialize_applescript ()
3919 {
3920 AEDesc null_desc;
3921 OSAError osaerror;
3922
3923 /* if open fails, as_scripting_component is set to NULL. Its
3924 subsequent use in OSA calls will fail with badComponentInstance
3925 error. */
3926 as_scripting_component = OpenDefaultComponent (kOSAComponentType,
3927 kAppleScriptSubtype);
3928
3929 null_desc.descriptorType = typeNull;
3930 null_desc.dataHandle = 0;
3931 osaerror = OSAMakeContext (as_scripting_component, &null_desc,
3932 kOSANullScript, &as_script_context);
3933 if (osaerror)
3934 as_script_context = kOSANullScript;
3935 /* use default context if create fails */
3936 }
3937
3938
3939 void
3940 terminate_applescript()
3941 {
3942 OSADispose (as_scripting_component, as_script_context);
3943 CloseComponent (as_scripting_component);
3944 }
3945
3946 /* Convert a lisp string to the 4 byte character code. */
3947
3948 OSType
3949 mac_get_code_from_arg(Lisp_Object arg, OSType defCode)
3950 {
3951 OSType result;
3952 if (NILP(arg))
3953 {
3954 result = defCode;
3955 }
3956 else
3957 {
3958 /* check type string */
3959 CHECK_STRING(arg);
3960 if (SBYTES (arg) != 4)
3961 {
3962 error ("Wrong argument: need string of length 4 for code");
3963 }
3964 result = EndianU32_BtoN (*((UInt32 *) SDATA (arg)));
3965 }
3966 return result;
3967 }
3968
3969 /* Convert the 4 byte character code into a 4 byte string. */
3970
3971 Lisp_Object
3972 mac_get_object_from_code(OSType defCode)
3973 {
3974 UInt32 code = EndianU32_NtoB (defCode);
3975
3976 return make_unibyte_string ((char *)&code, 4);
3977 }
3978
3979
3980 DEFUN ("mac-get-file-creator", Fmac_get_file_creator, Smac_get_file_creator, 1, 1, 0,
3981 doc: /* Get the creator code of FILENAME as a four character string. */)
3982 (filename)
3983 Lisp_Object filename;
3984 {
3985 OSErr status;
3986 #ifdef MAC_OSX
3987 FSRef fref;
3988 #else
3989 FSSpec fss;
3990 #endif
3991 OSType cCode;
3992 Lisp_Object result = Qnil;
3993 CHECK_STRING (filename);
3994
3995 if (NILP(Ffile_exists_p(filename)) || !NILP(Ffile_directory_p(filename))) {
3996 return Qnil;
3997 }
3998 filename = Fexpand_file_name (filename, Qnil);
3999
4000 BLOCK_INPUT;
4001 #ifdef MAC_OSX
4002 status = FSPathMakeRef(SDATA(ENCODE_FILE(filename)), &fref, NULL);
4003 #else
4004 status = posix_pathname_to_fsspec (SDATA (ENCODE_FILE (filename)), &fss);
4005 #endif
4006
4007 if (status == noErr)
4008 {
4009 #ifdef MAC_OSX
4010 FSCatalogInfo catalogInfo;
4011
4012 status = FSGetCatalogInfo(&fref, kFSCatInfoFinderInfo,
4013 &catalogInfo, NULL, NULL, NULL);
4014 #else
4015 FInfo finder_info;
4016
4017 status = FSpGetFInfo (&fss, &finder_info);
4018 #endif
4019 if (status == noErr)
4020 {
4021 #ifdef MAC_OSX
4022 result = mac_get_object_from_code(((FileInfo*)&catalogInfo.finderInfo)->fileCreator);
4023 #else
4024 result = mac_get_object_from_code (finder_info.fdCreator);
4025 #endif
4026 }
4027 }
4028 UNBLOCK_INPUT;
4029 if (status != noErr) {
4030 error ("Error while getting file information.");
4031 }
4032 return result;
4033 }
4034
4035 DEFUN ("mac-get-file-type", Fmac_get_file_type, Smac_get_file_type, 1, 1, 0,
4036 doc: /* Get the type code of FILENAME as a four character string. */)
4037 (filename)
4038 Lisp_Object filename;
4039 {
4040 OSErr status;
4041 #ifdef MAC_OSX
4042 FSRef fref;
4043 #else
4044 FSSpec fss;
4045 #endif
4046 OSType cCode;
4047 Lisp_Object result = Qnil;
4048 CHECK_STRING (filename);
4049
4050 if (NILP(Ffile_exists_p(filename)) || !NILP(Ffile_directory_p(filename))) {
4051 return Qnil;
4052 }
4053 filename = Fexpand_file_name (filename, Qnil);
4054
4055 BLOCK_INPUT;
4056 #ifdef MAC_OSX
4057 status = FSPathMakeRef(SDATA(ENCODE_FILE(filename)), &fref, NULL);
4058 #else
4059 status = posix_pathname_to_fsspec (SDATA (ENCODE_FILE (filename)), &fss);
4060 #endif
4061
4062 if (status == noErr)
4063 {
4064 #ifdef MAC_OSX
4065 FSCatalogInfo catalogInfo;
4066
4067 status = FSGetCatalogInfo(&fref, kFSCatInfoFinderInfo,
4068 &catalogInfo, NULL, NULL, NULL);
4069 #else
4070 FInfo finder_info;
4071
4072 status = FSpGetFInfo (&fss, &finder_info);
4073 #endif
4074 if (status == noErr)
4075 {
4076 #ifdef MAC_OSX
4077 result = mac_get_object_from_code(((FileInfo*)&catalogInfo.finderInfo)->fileType);
4078 #else
4079 result = mac_get_object_from_code (finder_info.fdType);
4080 #endif
4081 }
4082 }
4083 UNBLOCK_INPUT;
4084 if (status != noErr) {
4085 error ("Error while getting file information.");
4086 }
4087 return result;
4088 }
4089
4090 DEFUN ("mac-set-file-creator", Fmac_set_file_creator, Smac_set_file_creator, 1, 2, 0,
4091 doc: /* Set creator code of file FILENAME to CODE.
4092 If non-nil, CODE must be a 4-character string. Otherwise, 'EMAx' is
4093 assumed. Return non-nil if successful. */)
4094 (filename, code)
4095 Lisp_Object filename, code;
4096 {
4097 OSErr status;
4098 #ifdef MAC_OSX
4099 FSRef fref;
4100 #else
4101 FSSpec fss;
4102 #endif
4103 OSType cCode;
4104 CHECK_STRING (filename);
4105
4106 cCode = mac_get_code_from_arg(code, 'EMAx');
4107
4108 if (NILP(Ffile_exists_p(filename)) || !NILP(Ffile_directory_p(filename))) {
4109 return Qnil;
4110 }
4111 filename = Fexpand_file_name (filename, Qnil);
4112
4113 BLOCK_INPUT;
4114 #ifdef MAC_OSX
4115 status = FSPathMakeRef(SDATA(ENCODE_FILE(filename)), &fref, NULL);
4116 #else
4117 status = posix_pathname_to_fsspec (SDATA (ENCODE_FILE (filename)), &fss);
4118 #endif
4119
4120 if (status == noErr)
4121 {
4122 #ifdef MAC_OSX
4123 FSCatalogInfo catalogInfo;
4124 FSRef parentDir;
4125 status = FSGetCatalogInfo(&fref, kFSCatInfoFinderInfo,
4126 &catalogInfo, NULL, NULL, &parentDir);
4127 #else
4128 FInfo finder_info;
4129
4130 status = FSpGetFInfo (&fss, &finder_info);
4131 #endif
4132 if (status == noErr)
4133 {
4134 #ifdef MAC_OSX
4135 ((FileInfo*)&catalogInfo.finderInfo)->fileCreator = cCode;
4136 status = FSSetCatalogInfo(&fref, kFSCatInfoFinderInfo, &catalogInfo);
4137 /* TODO: on Mac OS 10.2, we need to touch the parent dir, FNNotify? */
4138 #else
4139 finder_info.fdCreator = cCode;
4140 status = FSpSetFInfo (&fss, &finder_info);
4141 #endif
4142 }
4143 }
4144 UNBLOCK_INPUT;
4145 if (status != noErr) {
4146 error ("Error while setting creator information.");
4147 }
4148 return Qt;
4149 }
4150
4151 DEFUN ("mac-set-file-type", Fmac_set_file_type, Smac_set_file_type, 2, 2, 0,
4152 doc: /* Set file code of file FILENAME to CODE.
4153 CODE must be a 4-character string. Return non-nil if successful. */)
4154 (filename, code)
4155 Lisp_Object filename, code;
4156 {
4157 OSErr status;
4158 #ifdef MAC_OSX
4159 FSRef fref;
4160 #else
4161 FSSpec fss;
4162 #endif
4163 OSType cCode;
4164 CHECK_STRING (filename);
4165
4166 cCode = mac_get_code_from_arg(code, 0); /* Default to empty code*/
4167
4168 if (NILP(Ffile_exists_p(filename)) || !NILP(Ffile_directory_p(filename))) {
4169 return Qnil;
4170 }
4171 filename = Fexpand_file_name (filename, Qnil);
4172
4173 BLOCK_INPUT;
4174 #ifdef MAC_OSX
4175 status = FSPathMakeRef(SDATA(ENCODE_FILE(filename)), &fref, NULL);
4176 #else
4177 status = posix_pathname_to_fsspec (SDATA (ENCODE_FILE (filename)), &fss);
4178 #endif
4179
4180 if (status == noErr)
4181 {
4182 #ifdef MAC_OSX
4183 FSCatalogInfo catalogInfo;
4184 FSRef parentDir;
4185 status = FSGetCatalogInfo(&fref, kFSCatInfoFinderInfo,
4186 &catalogInfo, NULL, NULL, &parentDir);
4187 #else
4188 FInfo finder_info;
4189
4190 status = FSpGetFInfo (&fss, &finder_info);
4191 #endif
4192 if (status == noErr)
4193 {
4194 #ifdef MAC_OSX
4195 ((FileInfo*)&catalogInfo.finderInfo)->fileType = cCode;
4196 status = FSSetCatalogInfo(&fref, kFSCatInfoFinderInfo, &catalogInfo);
4197 /* TODO: on Mac OS 10.2, we need to touch the parent dir, FNNotify? */
4198 #else
4199 finder_info.fdType = cCode;
4200 status = FSpSetFInfo (&fss, &finder_info);
4201 #endif
4202 }
4203 }
4204 UNBLOCK_INPUT;
4205 if (status != noErr) {
4206 error ("Error while setting creator information.");
4207 }
4208 return Qt;
4209 }
4210
4211
4212 /* Compile and execute the AppleScript SCRIPT and return the error
4213 status as function value. A zero is returned if compilation and
4214 execution is successful, in which case *RESULT is set to a Lisp
4215 string containing the resulting script value. Otherwise, the Mac
4216 error code is returned and *RESULT is set to an error Lisp string.
4217 For documentation on the MacOS scripting architecture, see Inside
4218 Macintosh - Interapplication Communications: Scripting
4219 Components. */
4220
4221 static long
4222 do_applescript (script, result)
4223 Lisp_Object script, *result;
4224 {
4225 AEDesc script_desc, result_desc, error_desc, *desc = NULL;
4226 OSErr error;
4227 OSAError osaerror;
4228
4229 *result = Qnil;
4230
4231 if (!as_scripting_component)
4232 initialize_applescript();
4233
4234 error = AECreateDesc (typeChar, SDATA (script), SBYTES (script),
4235 &script_desc);
4236 if (error)
4237 return error;
4238
4239 osaerror = OSADoScript (as_scripting_component, &script_desc, kOSANullScript,
4240 typeChar, kOSAModeNull, &result_desc);
4241
4242 if (osaerror == noErr)
4243 /* success: retrieve resulting script value */
4244 desc = &result_desc;
4245 else if (osaerror == errOSAScriptError)
4246 /* error executing AppleScript: retrieve error message */
4247 if (!OSAScriptError (as_scripting_component, kOSAErrorMessage, typeChar,
4248 &error_desc))
4249 desc = &error_desc;
4250
4251 if (desc)
4252 {
4253 #if TARGET_API_MAC_CARBON
4254 *result = make_uninit_string (AEGetDescDataSize (desc));
4255 AEGetDescData (desc, SDATA (*result), SBYTES (*result));
4256 #else /* not TARGET_API_MAC_CARBON */
4257 *result = make_uninit_string (GetHandleSize (desc->dataHandle));
4258 memcpy (SDATA (*result), *(desc->dataHandle), SBYTES (*result));
4259 #endif /* not TARGET_API_MAC_CARBON */
4260 AEDisposeDesc (desc);
4261 }
4262
4263 AEDisposeDesc (&script_desc);
4264
4265 return osaerror;
4266 }
4267
4268
4269 DEFUN ("do-applescript", Fdo_applescript, Sdo_applescript, 1, 1, 0,
4270 doc: /* Compile and execute AppleScript SCRIPT and return the result.
4271 If compilation and execution are successful, the resulting script
4272 value is returned as a string. Otherwise the function aborts and
4273 displays the error message returned by the AppleScript scripting
4274 component. */)
4275 (script)
4276 Lisp_Object script;
4277 {
4278 Lisp_Object result;
4279 long status;
4280
4281 CHECK_STRING (script);
4282
4283 BLOCK_INPUT;
4284 status = do_applescript (script, &result);
4285 UNBLOCK_INPUT;
4286 if (status == 0)
4287 return result;
4288 else if (!STRINGP (result))
4289 error ("AppleScript error %d", status);
4290 else
4291 error ("%s", SDATA (result));
4292 }
4293
4294
4295 DEFUN ("mac-file-name-to-posix", Fmac_file_name_to_posix,
4296 Smac_file_name_to_posix, 1, 1, 0,
4297 doc: /* Convert Macintosh FILENAME to Posix form. */)
4298 (filename)
4299 Lisp_Object filename;
4300 {
4301 char posix_filename[MAXPATHLEN+1];
4302
4303 CHECK_STRING (filename);
4304
4305 if (mac_to_posix_pathname (SDATA (filename), posix_filename, MAXPATHLEN))
4306 return build_string (posix_filename);
4307 else
4308 return Qnil;
4309 }
4310
4311
4312 DEFUN ("posix-file-name-to-mac", Fposix_file_name_to_mac,
4313 Sposix_file_name_to_mac, 1, 1, 0,
4314 doc: /* Convert Posix FILENAME to Mac form. */)
4315 (filename)
4316 Lisp_Object filename;
4317 {
4318 char mac_filename[MAXPATHLEN+1];
4319
4320 CHECK_STRING (filename);
4321
4322 if (posix_to_mac_pathname (SDATA (filename), mac_filename, MAXPATHLEN))
4323 return build_string (mac_filename);
4324 else
4325 return Qnil;
4326 }
4327
4328
4329 DEFUN ("mac-coerce-ae-data", Fmac_coerce_ae_data, Smac_coerce_ae_data, 3, 3, 0,
4330 doc: /* Coerce Apple event data SRC-DATA of type SRC-TYPE to DST-TYPE.
4331 Each type should be a string of length 4 or the symbol
4332 `undecoded-file-name'. */)
4333 (src_type, src_data, dst_type)
4334 Lisp_Object src_type, src_data, dst_type;
4335 {
4336 OSErr err;
4337 Lisp_Object result = Qnil;
4338 DescType src_desc_type, dst_desc_type;
4339 AEDesc dst_desc;
4340 #ifdef MAC_OSX
4341 FSRef fref;
4342 #else
4343 FSSpec fs;
4344 #endif
4345
4346 CHECK_STRING (src_data);
4347 if (EQ (src_type, Qundecoded_file_name))
4348 src_desc_type = TYPE_FILE_NAME;
4349 else
4350 src_desc_type = mac_get_code_from_arg (src_type, 0);
4351
4352 if (EQ (dst_type, Qundecoded_file_name))
4353 dst_desc_type = TYPE_FILE_NAME;
4354 else
4355 dst_desc_type = mac_get_code_from_arg (dst_type, 0);
4356
4357 BLOCK_INPUT;
4358 err = AECoercePtr (src_desc_type, SDATA (src_data), SBYTES (src_data),
4359 dst_desc_type, &dst_desc);
4360 if (err == noErr)
4361 {
4362 result = Fcdr (mac_aedesc_to_lisp (&dst_desc));
4363 AEDisposeDesc (&dst_desc);
4364 }
4365 UNBLOCK_INPUT;
4366
4367 return result;
4368 }
4369
4370
4371 #if TARGET_API_MAC_CARBON
4372 static Lisp_Object Qxml, Qmime_charset;
4373 static Lisp_Object QNFD, QNFKD, QNFC, QNFKC, QHFS_plus_D, QHFS_plus_C;
4374
4375 DEFUN ("mac-get-preference", Fmac_get_preference, Smac_get_preference, 1, 4, 0,
4376 doc: /* Return the application preference value for KEY.
4377 KEY is either a string specifying a preference key, or a list of key
4378 strings. If it is a list, the (i+1)-th element is used as a key for
4379 the CFDictionary value obtained by the i-th element. Return nil if
4380 lookup is failed at some stage.
4381
4382 Optional arg APPLICATION is an application ID string. If omitted or
4383 nil, that stands for the current application.
4384
4385 Optional arg FORMAT specifies the data format of the return value. If
4386 omitted or nil, each Core Foundation object is converted into a
4387 corresponding Lisp object as follows:
4388
4389 Core Foundation Lisp Tag
4390 ------------------------------------------------------------
4391 CFString Multibyte string string
4392 CFNumber Integer or float number
4393 CFBoolean Symbol (t or nil) boolean
4394 CFDate List of three integers date
4395 (cf. `current-time')
4396 CFData Unibyte string data
4397 CFArray Vector array
4398 CFDictionary Alist or hash table dictionary
4399 (depending on HASH-BOUND)
4400
4401 If it is t, a symbol that represents the type of the original Core
4402 Foundation object is prepended. If it is `xml', the value is returned
4403 as an XML representation.
4404
4405 Optional arg HASH-BOUND specifies which kinds of the list objects,
4406 alists or hash tables, are used as the targets of the conversion from
4407 CFDictionary. If HASH-BOUND is a negative integer or nil, always
4408 generate alists. If HASH-BOUND >= 0, generate an alist if the number
4409 of keys in the dictionary is smaller than HASH-BOUND, and a hash table
4410 otherwise. */)
4411 (key, application, format, hash_bound)
4412 Lisp_Object key, application, format, hash_bound;
4413 {
4414 CFStringRef app_id, key_str;
4415 CFPropertyListRef app_plist = NULL, plist;
4416 Lisp_Object result = Qnil, tmp;
4417
4418 if (STRINGP (key))
4419 key = Fcons (key, Qnil);
4420 else
4421 {
4422 CHECK_CONS (key);
4423 for (tmp = key; CONSP (tmp); tmp = XCDR (tmp))
4424 CHECK_STRING_CAR (tmp);
4425 if (!NILP (tmp))
4426 wrong_type_argument (Qlistp, key);
4427 }
4428 if (!NILP (application))
4429 CHECK_STRING (application);
4430 CHECK_SYMBOL (format);
4431 if (!NILP (hash_bound))
4432 CHECK_NUMBER (hash_bound);
4433
4434 BLOCK_INPUT;
4435
4436 app_id = kCFPreferencesCurrentApplication;
4437 if (!NILP (application))
4438 {
4439 app_id = cfstring_create_with_string (application);
4440 if (app_id == NULL)
4441 goto out;
4442 }
4443 key_str = cfstring_create_with_string (XCAR (key));
4444 if (key_str == NULL)
4445 goto out;
4446 app_plist = CFPreferencesCopyAppValue (key_str, app_id);
4447 CFRelease (key_str);
4448 if (app_plist == NULL)
4449 goto out;
4450
4451 plist = app_plist;
4452 for (key = XCDR (key); CONSP (key); key = XCDR (key))
4453 {
4454 if (CFGetTypeID (plist) != CFDictionaryGetTypeID ())
4455 break;
4456 key_str = cfstring_create_with_string (XCAR (key));
4457 if (key_str == NULL)
4458 goto out;
4459 plist = CFDictionaryGetValue (plist, key_str);
4460 CFRelease (key_str);
4461 if (plist == NULL)
4462 goto out;
4463 }
4464
4465 if (NILP (key))
4466 if (EQ (format, Qxml))
4467 {
4468 CFDataRef data = CFPropertyListCreateXMLData (NULL, plist);
4469 if (data == NULL)
4470 goto out;
4471 result = cfdata_to_lisp (data);
4472 CFRelease (data);
4473 }
4474 else
4475 result =
4476 cfproperty_list_to_lisp (plist, EQ (format, Qt),
4477 NILP (hash_bound) ? -1 : XINT (hash_bound));
4478
4479 out:
4480 if (app_plist)
4481 CFRelease (app_plist);
4482 CFRelease (app_id);
4483
4484 UNBLOCK_INPUT;
4485
4486 return result;
4487 }
4488
4489
4490 static CFStringEncoding
4491 get_cfstring_encoding_from_lisp (obj)
4492 Lisp_Object obj;
4493 {
4494 CFStringRef iana_name;
4495 CFStringEncoding encoding = kCFStringEncodingInvalidId;
4496
4497 if (NILP (obj))
4498 return kCFStringEncodingUnicode;
4499
4500 if (INTEGERP (obj))
4501 return XINT (obj);
4502
4503 if (SYMBOLP (obj) && !NILP (Fcoding_system_p (obj)))
4504 {
4505 Lisp_Object coding_spec, plist;
4506
4507 coding_spec = Fget (obj, Qcoding_system);
4508 plist = XVECTOR (coding_spec)->contents[3];
4509 obj = Fplist_get (XVECTOR (coding_spec)->contents[3], Qmime_charset);
4510 }
4511
4512 if (SYMBOLP (obj))
4513 obj = SYMBOL_NAME (obj);
4514
4515 if (STRINGP (obj))
4516 {
4517 iana_name = cfstring_create_with_string (obj);
4518 if (iana_name)
4519 {
4520 encoding = CFStringConvertIANACharSetNameToEncoding (iana_name);
4521 CFRelease (iana_name);
4522 }
4523 }
4524
4525 return encoding;
4526 }
4527
4528 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
4529 static CFStringRef
4530 cfstring_create_normalized (str, symbol)
4531 CFStringRef str;
4532 Lisp_Object symbol;
4533 {
4534 int form = -1;
4535 TextEncodingVariant variant;
4536 float initial_mag = 0.0;
4537 CFStringRef result = NULL;
4538
4539 if (EQ (symbol, QNFD))
4540 form = kCFStringNormalizationFormD;
4541 else if (EQ (symbol, QNFKD))
4542 form = kCFStringNormalizationFormKD;
4543 else if (EQ (symbol, QNFC))
4544 form = kCFStringNormalizationFormC;
4545 else if (EQ (symbol, QNFKC))
4546 form = kCFStringNormalizationFormKC;
4547 else if (EQ (symbol, QHFS_plus_D))
4548 {
4549 variant = kUnicodeHFSPlusDecompVariant;
4550 initial_mag = 1.5;
4551 }
4552 else if (EQ (symbol, QHFS_plus_C))
4553 {
4554 variant = kUnicodeHFSPlusCompVariant;
4555 initial_mag = 1.0;
4556 }
4557
4558 if (form >= 0)
4559 {
4560 CFMutableStringRef mut_str = CFStringCreateMutableCopy (NULL, 0, str);
4561
4562 if (mut_str)
4563 {
4564 CFStringNormalize (mut_str, form);
4565 result = mut_str;
4566 }
4567 }
4568 else if (initial_mag > 0.0)
4569 {
4570 UnicodeToTextInfo uni = NULL;
4571 UnicodeMapping map;
4572 CFIndex length;
4573 UniChar *in_text, *buffer = NULL, *out_buf = NULL;
4574 OSErr err = noErr;
4575 ByteCount out_read, out_size, out_len;
4576
4577 map.unicodeEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault,
4578 kUnicodeNoSubset,
4579 kTextEncodingDefaultFormat);
4580 map.otherEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault,
4581 variant,
4582 kTextEncodingDefaultFormat);
4583 map.mappingVersion = kUnicodeUseLatestMapping;
4584
4585 length = CFStringGetLength (str);
4586 out_size = (int)((float)length * initial_mag) * sizeof (UniChar);
4587 if (out_size < 32)
4588 out_size = 32;
4589
4590 in_text = (UniChar *)CFStringGetCharactersPtr (str);
4591 if (in_text == NULL)
4592 {
4593 buffer = xmalloc (sizeof (UniChar) * length);
4594 if (buffer)
4595 {
4596 CFStringGetCharacters (str, CFRangeMake (0, length), buffer);
4597 in_text = buffer;
4598 }
4599 }
4600
4601 if (in_text)
4602 err = CreateUnicodeToTextInfo(&map, &uni);
4603 while (err == noErr)
4604 {
4605 out_buf = xmalloc (out_size);
4606 if (out_buf == NULL)
4607 err = mFulErr;
4608 else
4609 err = ConvertFromUnicodeToText (uni, length * sizeof (UniChar),
4610 in_text,
4611 kUnicodeDefaultDirectionMask,
4612 0, NULL, NULL, NULL,
4613 out_size, &out_read, &out_len,
4614 out_buf);
4615 if (err == noErr && out_read < length * sizeof (UniChar))
4616 {
4617 xfree (out_buf);
4618 out_size += length;
4619 }
4620 else
4621 break;
4622 }
4623 if (err == noErr)
4624 result = CFStringCreateWithCharacters (NULL, out_buf,
4625 out_len / sizeof (UniChar));
4626 if (uni)
4627 DisposeUnicodeToTextInfo (&uni);
4628 if (out_buf)
4629 xfree (out_buf);
4630 if (buffer)
4631 xfree (buffer);
4632 }
4633 else
4634 {
4635 result = str;
4636 CFRetain (result);
4637 }
4638
4639 return result;
4640 }
4641 #endif
4642
4643 DEFUN ("mac-code-convert-string", Fmac_code_convert_string, Smac_code_convert_string, 3, 4, 0,
4644 doc: /* Convert STRING from SOURCE encoding to TARGET encoding.
4645 The conversion is performed using the converter provided by the system.
4646 Each encoding is specified by either a coding system symbol, a mime
4647 charset string, or an integer as a CFStringEncoding value. Nil for
4648 encoding means UTF-16 in native byte order, no byte order mark.
4649 On Mac OS X 10.2 and later, you can do Unicode Normalization by
4650 specifying the optional argument NORMALIZATION-FORM with a symbol NFD,
4651 NFKD, NFC, NFKC, HFS+D, or HFS+C.
4652 On successful conversion, return the result string, else return nil. */)
4653 (string, source, target, normalization_form)
4654 Lisp_Object string, source, target, normalization_form;
4655 {
4656 Lisp_Object result = Qnil;
4657 CFStringEncoding src_encoding, tgt_encoding;
4658 CFStringRef str = NULL;
4659
4660 CHECK_STRING (string);
4661 if (!INTEGERP (source) && !STRINGP (source))
4662 CHECK_SYMBOL (source);
4663 if (!INTEGERP (target) && !STRINGP (target))
4664 CHECK_SYMBOL (target);
4665 CHECK_SYMBOL (normalization_form);
4666
4667 BLOCK_INPUT;
4668
4669 src_encoding = get_cfstring_encoding_from_lisp (source);
4670 tgt_encoding = get_cfstring_encoding_from_lisp (target);
4671
4672 /* We really want string_to_unibyte, but since it doesn't exist yet, we
4673 use string_as_unibyte which works as well, except for the fact that
4674 it's too permissive (it doesn't check that the multibyte string only
4675 contain single-byte chars). */
4676 string = Fstring_as_unibyte (string);
4677 if (src_encoding != kCFStringEncodingInvalidId
4678 && tgt_encoding != kCFStringEncodingInvalidId)
4679 str = CFStringCreateWithBytes (NULL, SDATA (string), SBYTES (string),
4680 src_encoding, !NILP (source));
4681 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
4682 if (str)
4683 {
4684 CFStringRef saved_str = str;
4685
4686 str = cfstring_create_normalized (saved_str, normalization_form);
4687 CFRelease (saved_str);
4688 }
4689 #endif
4690 if (str)
4691 {
4692 CFIndex str_len, buf_len;
4693
4694 str_len = CFStringGetLength (str);
4695 if (CFStringGetBytes (str, CFRangeMake (0, str_len), tgt_encoding, 0,
4696 !NILP (target), NULL, 0, &buf_len) == str_len)
4697 {
4698 result = make_uninit_string (buf_len);
4699 CFStringGetBytes (str, CFRangeMake (0, str_len), tgt_encoding, 0,
4700 !NILP (target), SDATA (result), buf_len, NULL);
4701 }
4702 CFRelease (str);
4703 }
4704
4705 UNBLOCK_INPUT;
4706
4707 return result;
4708 }
4709 #endif /* TARGET_API_MAC_CARBON */
4710
4711
4712 DEFUN ("mac-clear-font-name-table", Fmac_clear_font_name_table, Smac_clear_font_name_table, 0, 0, 0,
4713 doc: /* Clear the font name table. */)
4714 ()
4715 {
4716 check_mac ();
4717 mac_clear_font_name_table ();
4718 return Qnil;
4719 }
4720
4721
4722 static Lisp_Object
4723 mac_get_system_locale ()
4724 {
4725 OSErr err;
4726 LangCode lang;
4727 RegionCode region;
4728 LocaleRef locale;
4729 Str255 str;
4730
4731 lang = GetScriptVariable (smSystemScript, smScriptLang);
4732 region = GetScriptManagerVariable (smRegionCode);
4733 err = LocaleRefFromLangOrRegionCode (lang, region, &locale);
4734 if (err == noErr)
4735 err = LocaleRefGetPartString (locale, kLocaleAllPartsMask,
4736 sizeof (str), str);
4737 if (err == noErr)
4738 return build_string (str);
4739 else
4740 return Qnil;
4741 }
4742
4743
4744 #ifdef MAC_OSX
4745 #undef select
4746
4747 extern int inhibit_window_system;
4748 extern int noninteractive;
4749
4750 /* Unlike in X11, window events in Carbon do not come from sockets.
4751 So we cannot simply use `select' to monitor two kinds of inputs:
4752 window events and process outputs. We emulate such functionality
4753 by regarding fd 0 as the window event channel and simultaneously
4754 monitoring both kinds of input channels. It is implemented by
4755 dividing into some cases:
4756 1. The window event channel is not involved.
4757 -> Use `select'.
4758 2. Sockets are not involved.
4759 -> Use ReceiveNextEvent.
4760 3. [If SELECT_USE_CFSOCKET is defined]
4761 Only the window event channel and socket read channels are
4762 involved, and timeout is not too short (greater than
4763 SELECT_TIMEOUT_THRESHHOLD_RUNLOOP seconds).
4764 -> Create CFSocket for each socket and add it into the current
4765 event RunLoop so that a `ready-to-read' event can be posted
4766 to the event queue that is also used for window events. Then
4767 ReceiveNextEvent can wait for both kinds of inputs.
4768 4. Otherwise.
4769 -> Periodically poll the window input channel while repeatedly
4770 executing `select' with a short timeout
4771 (SELECT_POLLING_PERIOD_USEC microseconds). */
4772
4773 #define SELECT_POLLING_PERIOD_USEC 20000
4774 #ifdef SELECT_USE_CFSOCKET
4775 #define SELECT_TIMEOUT_THRESHOLD_RUNLOOP 0.2
4776 #define EVENT_CLASS_SOCK 'Sock'
4777
4778 static void
4779 socket_callback (s, type, address, data, info)
4780 CFSocketRef s;
4781 CFSocketCallBackType type;
4782 CFDataRef address;
4783 const void *data;
4784 void *info;
4785 {
4786 EventRef event;
4787
4788 CreateEvent (NULL, EVENT_CLASS_SOCK, 0, 0, kEventAttributeNone, &event);
4789 PostEventToQueue (GetCurrentEventQueue (), event, kEventPriorityStandard);
4790 ReleaseEvent (event);
4791 }
4792 #endif /* SELECT_USE_CFSOCKET */
4793
4794 static int
4795 select_and_poll_event (n, rfds, wfds, efds, timeout)
4796 int n;
4797 SELECT_TYPE *rfds;
4798 SELECT_TYPE *wfds;
4799 SELECT_TYPE *efds;
4800 struct timeval *timeout;
4801 {
4802 int r;
4803 OSErr err;
4804
4805 r = select (n, rfds, wfds, efds, timeout);
4806 if (r != -1)
4807 {
4808 BLOCK_INPUT;
4809 err = ReceiveNextEvent (0, NULL, kEventDurationNoWait,
4810 kEventLeaveInQueue, NULL);
4811 UNBLOCK_INPUT;
4812 if (err == noErr)
4813 {
4814 FD_SET (0, rfds);
4815 r++;
4816 }
4817 }
4818 return r;
4819 }
4820
4821 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1020
4822 #undef SELECT_INVALIDATE_CFSOCKET
4823 #endif
4824
4825 int
4826 sys_select (n, rfds, wfds, efds, timeout)
4827 int n;
4828 SELECT_TYPE *rfds;
4829 SELECT_TYPE *wfds;
4830 SELECT_TYPE *efds;
4831 struct timeval *timeout;
4832 {
4833 OSErr err;
4834 int i, r;
4835 EMACS_TIME select_timeout;
4836
4837 if (inhibit_window_system || noninteractive
4838 || rfds == NULL || !FD_ISSET (0, rfds))
4839 return select (n, rfds, wfds, efds, timeout);
4840
4841 FD_CLR (0, rfds);
4842
4843 if (wfds == NULL && efds == NULL)
4844 {
4845 int nsocks = 0;
4846 SELECT_TYPE orfds = *rfds;
4847
4848 EventTimeout timeout_sec =
4849 (timeout
4850 ? (EMACS_SECS (*timeout) * kEventDurationSecond
4851 + EMACS_USECS (*timeout) * kEventDurationMicrosecond)
4852 : kEventDurationForever);
4853
4854 for (i = 1; i < n; i++)
4855 if (FD_ISSET (i, rfds))
4856 nsocks++;
4857
4858 if (nsocks == 0)
4859 {
4860 BLOCK_INPUT;
4861 err = ReceiveNextEvent (0, NULL, timeout_sec,
4862 kEventLeaveInQueue, NULL);
4863 UNBLOCK_INPUT;
4864 if (err == noErr)
4865 {
4866 FD_SET (0, rfds);
4867 return 1;
4868 }
4869 else
4870 return 0;
4871 }
4872
4873 /* Avoid initial overhead of RunLoop setup for the case that
4874 some input is already available. */
4875 EMACS_SET_SECS_USECS (select_timeout, 0, 0);
4876 r = select_and_poll_event (n, rfds, wfds, efds, &select_timeout);
4877 if (r != 0 || timeout_sec == 0.0)
4878 return r;
4879
4880 *rfds = orfds;
4881
4882 #ifdef SELECT_USE_CFSOCKET
4883 if (timeout_sec > 0 && timeout_sec <= SELECT_TIMEOUT_THRESHOLD_RUNLOOP)
4884 goto poll_periodically;
4885
4886 {
4887 CFRunLoopRef runloop =
4888 (CFRunLoopRef) GetCFRunLoopFromEventLoop (GetCurrentEventLoop ());
4889 EventTypeSpec specs[] = {{EVENT_CLASS_SOCK, 0}};
4890 #ifdef SELECT_INVALIDATE_CFSOCKET
4891 CFSocketRef *shead, *s;
4892 #else
4893 CFRunLoopSourceRef *shead, *s;
4894 #endif
4895
4896 BLOCK_INPUT;
4897
4898 #ifdef SELECT_INVALIDATE_CFSOCKET
4899 shead = xmalloc (sizeof (CFSocketRef) * nsocks);
4900 #else
4901 shead = xmalloc (sizeof (CFRunLoopSourceRef) * nsocks);
4902 #endif
4903 s = shead;
4904 for (i = 1; i < n; i++)
4905 if (FD_ISSET (i, rfds))
4906 {
4907 CFSocketRef socket =
4908 CFSocketCreateWithNative (NULL, i, kCFSocketReadCallBack,
4909 socket_callback, NULL);
4910 CFRunLoopSourceRef source =
4911 CFSocketCreateRunLoopSource (NULL, socket, 0);
4912
4913 #ifdef SELECT_INVALIDATE_CFSOCKET
4914 CFSocketSetSocketFlags (socket, 0);
4915 #endif
4916 CFRunLoopAddSource (runloop, source, kCFRunLoopDefaultMode);
4917 #ifdef SELECT_INVALIDATE_CFSOCKET
4918 CFRelease (source);
4919 *s = socket;
4920 #else
4921 CFRelease (socket);
4922 *s = source;
4923 #endif
4924 s++;
4925 }
4926
4927 err = ReceiveNextEvent (0, NULL, timeout_sec, kEventLeaveInQueue, NULL);
4928
4929 do
4930 {
4931 --s;
4932 #ifdef SELECT_INVALIDATE_CFSOCKET
4933 CFSocketInvalidate (*s);
4934 #else
4935 CFRunLoopRemoveSource (runloop, *s, kCFRunLoopDefaultMode);
4936 #endif
4937 CFRelease (*s);
4938 }
4939 while (s != shead);
4940
4941 xfree (shead);
4942
4943 if (err)
4944 {
4945 FD_ZERO (rfds);
4946 r = 0;
4947 }
4948 else
4949 {
4950 FlushEventsMatchingListFromQueue (GetCurrentEventQueue (),
4951 GetEventTypeCount (specs),
4952 specs);
4953 EMACS_SET_SECS_USECS (select_timeout, 0, 0);
4954 r = select_and_poll_event (n, rfds, wfds, efds, &select_timeout);
4955 }
4956
4957 UNBLOCK_INPUT;
4958
4959 return r;
4960 }
4961 #endif /* SELECT_USE_CFSOCKET */
4962 }
4963
4964 poll_periodically:
4965 {
4966 EMACS_TIME end_time, now, remaining_time;
4967 SELECT_TYPE orfds = *rfds, owfds, oefds;
4968
4969 if (wfds)
4970 owfds = *wfds;
4971 if (efds)
4972 oefds = *efds;
4973 if (timeout)
4974 {
4975 remaining_time = *timeout;
4976 EMACS_GET_TIME (now);
4977 EMACS_ADD_TIME (end_time, now, remaining_time);
4978 }
4979
4980 do
4981 {
4982 EMACS_SET_SECS_USECS (select_timeout, 0, SELECT_POLLING_PERIOD_USEC);
4983 if (timeout && EMACS_TIME_LT (remaining_time, select_timeout))
4984 select_timeout = remaining_time;
4985 r = select_and_poll_event (n, rfds, wfds, efds, &select_timeout);
4986 if (r != 0)
4987 return r;
4988
4989 *rfds = orfds;
4990 if (wfds)
4991 *wfds = owfds;
4992 if (efds)
4993 *efds = oefds;
4994
4995 if (timeout)
4996 {
4997 EMACS_GET_TIME (now);
4998 EMACS_SUB_TIME (remaining_time, end_time, now);
4999 }
5000 }
5001 while (!timeout || EMACS_TIME_LT (now, end_time));
5002
5003 FD_ZERO (rfds);
5004 if (wfds)
5005 FD_ZERO (wfds);
5006 if (efds)
5007 FD_ZERO (efds);
5008 return 0;
5009 }
5010 }
5011
5012 /* Set up environment variables so that Emacs can correctly find its
5013 support files when packaged as an application bundle. Directories
5014 placed in /usr/local/share/emacs/<emacs-version>/, /usr/local/bin,
5015 and /usr/local/libexec/emacs/<emacs-version>/<system-configuration>
5016 by `make install' by default can instead be placed in
5017 .../Emacs.app/Contents/Resources/ and
5018 .../Emacs.app/Contents/MacOS/. Each of these environment variables
5019 is changed only if it is not already set. Presumably if the user
5020 sets an environment variable, he will want to use files in his path
5021 instead of ones in the application bundle. */
5022 void
5023 init_mac_osx_environment ()
5024 {
5025 CFBundleRef bundle;
5026 CFURLRef bundleURL;
5027 CFStringRef cf_app_bundle_pathname;
5028 int app_bundle_pathname_len;
5029 char *app_bundle_pathname;
5030 char *p, *q;
5031 struct stat st;
5032
5033 /* Initialize locale related variables. */
5034 mac_system_script_code =
5035 (ScriptCode) GetScriptManagerVariable (smSysScript);
5036 Vmac_system_locale = mac_get_system_locale ();
5037
5038 /* Fetch the pathname of the application bundle as a C string into
5039 app_bundle_pathname. */
5040
5041 bundle = CFBundleGetMainBundle ();
5042 if (!bundle || CFBundleGetIdentifier (bundle) == NULL)
5043 {
5044 /* We could not find the bundle identifier. For now, prevent
5045 the fatal error by bringing it up in the terminal. */
5046 inhibit_window_system = 1;
5047 return;
5048 }
5049
5050 bundleURL = CFBundleCopyBundleURL (bundle);
5051 if (!bundleURL)
5052 return;
5053
5054 cf_app_bundle_pathname = CFURLCopyFileSystemPath (bundleURL,
5055 kCFURLPOSIXPathStyle);
5056 app_bundle_pathname_len = CFStringGetLength (cf_app_bundle_pathname);
5057 app_bundle_pathname = (char *) alloca (app_bundle_pathname_len + 1);
5058
5059 if (!CFStringGetCString (cf_app_bundle_pathname,
5060 app_bundle_pathname,
5061 app_bundle_pathname_len + 1,
5062 kCFStringEncodingISOLatin1))
5063 {
5064 CFRelease (cf_app_bundle_pathname);
5065 return;
5066 }
5067
5068 CFRelease (cf_app_bundle_pathname);
5069
5070 /* P should have sufficient room for the pathname of the bundle plus
5071 the subpath in it leading to the respective directories. Q
5072 should have three times that much room because EMACSLOADPATH can
5073 have the value "<path to lisp dir>:<path to leim dir>:<path to
5074 site-lisp dir>". */
5075 p = (char *) alloca (app_bundle_pathname_len + 50);
5076 q = (char *) alloca (3 * app_bundle_pathname_len + 150);
5077 if (!getenv ("EMACSLOADPATH"))
5078 {
5079 q[0] = '\0';
5080
5081 strcpy (p, app_bundle_pathname);
5082 strcat (p, "/Contents/Resources/lisp");
5083 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5084 strcat (q, p);
5085
5086 strcpy (p, app_bundle_pathname);
5087 strcat (p, "/Contents/Resources/leim");
5088 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5089 {
5090 if (q[0] != '\0')
5091 strcat (q, ":");
5092 strcat (q, p);
5093 }
5094
5095 strcpy (p, app_bundle_pathname);
5096 strcat (p, "/Contents/Resources/site-lisp");
5097 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5098 {
5099 if (q[0] != '\0')
5100 strcat (q, ":");
5101 strcat (q, p);
5102 }
5103
5104 if (q[0] != '\0')
5105 setenv ("EMACSLOADPATH", q, 1);
5106 }
5107
5108 if (!getenv ("EMACSPATH"))
5109 {
5110 q[0] = '\0';
5111
5112 strcpy (p, app_bundle_pathname);
5113 strcat (p, "/Contents/MacOS/libexec");
5114 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5115 strcat (q, p);
5116
5117 strcpy (p, app_bundle_pathname);
5118 strcat (p, "/Contents/MacOS/bin");
5119 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5120 {
5121 if (q[0] != '\0')
5122 strcat (q, ":");
5123 strcat (q, p);
5124 }
5125
5126 if (q[0] != '\0')
5127 setenv ("EMACSPATH", q, 1);
5128 }
5129
5130 if (!getenv ("EMACSDATA"))
5131 {
5132 strcpy (p, app_bundle_pathname);
5133 strcat (p, "/Contents/Resources/etc");
5134 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5135 setenv ("EMACSDATA", p, 1);
5136 }
5137
5138 if (!getenv ("EMACSDOC"))
5139 {
5140 strcpy (p, app_bundle_pathname);
5141 strcat (p, "/Contents/Resources/etc");
5142 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5143 setenv ("EMACSDOC", p, 1);
5144 }
5145
5146 if (!getenv ("INFOPATH"))
5147 {
5148 strcpy (p, app_bundle_pathname);
5149 strcat (p, "/Contents/Resources/info");
5150 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
5151 setenv ("INFOPATH", p, 1);
5152 }
5153 }
5154 #endif /* MAC_OSX */
5155
5156
5157 void
5158 syms_of_mac ()
5159 {
5160 Qundecoded_file_name = intern ("undecoded-file-name");
5161 staticpro (&Qundecoded_file_name);
5162
5163 #if TARGET_API_MAC_CARBON
5164 Qstring = intern ("string"); staticpro (&Qstring);
5165 Qnumber = intern ("number"); staticpro (&Qnumber);
5166 Qboolean = intern ("boolean"); staticpro (&Qboolean);
5167 Qdate = intern ("date"); staticpro (&Qdate);
5168 Qdata = intern ("data"); staticpro (&Qdata);
5169 Qarray = intern ("array"); staticpro (&Qarray);
5170 Qdictionary = intern ("dictionary"); staticpro (&Qdictionary);
5171
5172 Qxml = intern ("xml");
5173 staticpro (&Qxml);
5174
5175 Qmime_charset = intern ("mime-charset");
5176 staticpro (&Qmime_charset);
5177
5178 QNFD = intern ("NFD"); staticpro (&QNFD);
5179 QNFKD = intern ("NFKD"); staticpro (&QNFKD);
5180 QNFC = intern ("NFC"); staticpro (&QNFC);
5181 QNFKC = intern ("NFKC"); staticpro (&QNFKC);
5182 QHFS_plus_D = intern ("HFS+D"); staticpro (&QHFS_plus_D);
5183 QHFS_plus_C = intern ("HFS+C"); staticpro (&QHFS_plus_C);
5184 #endif
5185
5186 defsubr (&Smac_coerce_ae_data);
5187 #if TARGET_API_MAC_CARBON
5188 defsubr (&Smac_get_preference);
5189 defsubr (&Smac_code_convert_string);
5190 #endif
5191 defsubr (&Smac_clear_font_name_table);
5192
5193 defsubr (&Smac_set_file_creator);
5194 defsubr (&Smac_set_file_type);
5195 defsubr (&Smac_get_file_creator);
5196 defsubr (&Smac_get_file_type);
5197 defsubr (&Sdo_applescript);
5198 defsubr (&Smac_file_name_to_posix);
5199 defsubr (&Sposix_file_name_to_mac);
5200
5201 DEFVAR_INT ("mac-system-script-code", &mac_system_script_code,
5202 doc: /* The system script code. */);
5203 mac_system_script_code = (ScriptCode) GetScriptManagerVariable (smSysScript);
5204
5205 DEFVAR_LISP ("mac-system-locale", &Vmac_system_locale,
5206 doc: /* The system locale identifier string.
5207 This is not a POSIX locale ID, but an ICU locale ID. So encoding
5208 information is not included. */);
5209 Vmac_system_locale = mac_get_system_locale ();
5210 }
5211
5212 /* arch-tag: 29d30c1f-0c6b-4f88-8a6d-0558d7f9dbff
5213 (do not change this comment) */