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