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