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