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