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