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