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