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