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