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