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