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