(Excursions): Replace arg `forms' with `body' in `save-excursion'.
[emacs.git] / src / mac.c
blob4c8e78ade25c154a5a530dca064a95c209516c1e
1 /* Unix emulation routines for GNU Emacs on the Mac OS.
2 Copyright (C) 2000, 2001, 2002, 2003, 2004,
3 2005 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA. */
22 /* Contributed by Andrew Choi (akochoi@mac.com). */
24 #include <config.h>
26 #include <stdio.h>
27 #include <errno.h>
29 #include "lisp.h"
30 #include "process.h"
31 #undef init_process
32 #include "systime.h"
33 #include "sysselect.h"
34 #include "blockinput.h"
36 #include "macterm.h"
38 #include "charset.h"
39 #include "coding.h"
40 #if !TARGET_API_MAC_CARBON
41 #include <Files.h>
42 #include <MacTypes.h>
43 #include <TextUtils.h>
44 #include <Folders.h>
45 #include <Resources.h>
46 #include <Aliases.h>
47 #include <FixMath.h>
48 #include <Timer.h>
49 #include <OSA.h>
50 #include <AppleScript.h>
51 #include <Scrap.h>
52 #include <Events.h>
53 #include <Processes.h>
54 #include <EPPC.h>
55 #include <MacLocales.h>
56 #include <Endian.h>
57 #endif /* not TARGET_API_MAC_CARBON */
59 #include <utime.h>
60 #include <dirent.h>
61 #include <sys/types.h>
62 #include <sys/stat.h>
63 #include <pwd.h>
64 #include <grp.h>
65 #include <sys/param.h>
66 #include <fcntl.h>
67 #if __MWERKS__
68 #include <unistd.h>
69 #endif
71 /* The system script code. */
72 static int mac_system_script_code;
74 /* The system locale identifier string. */
75 static Lisp_Object Vmac_system_locale;
77 /* An instance of the AppleScript component. */
78 static ComponentInstance as_scripting_component;
79 /* The single script context used for all script executions. */
80 static OSAID as_script_context;
83 /* When converting from Mac to Unix pathnames, /'s in folder names are
84 converted to :'s. This function, used in copying folder names,
85 performs a strncat and converts all character a to b in the copy of
86 the string s2 appended to the end of s1. */
88 void
89 string_cat_and_replace (char *s1, const char *s2, int n, char a, char b)
91 int l1 = strlen (s1);
92 int l2 = strlen (s2);
93 char *p = s1 + l1;
94 int i;
96 strncat (s1, s2, n);
97 for (i = 0; i < l2; i++)
99 if (*p == a)
100 *p = b;
101 p++;
106 /* Convert a Mac pathname to Posix form. A Mac full pathname is one
107 that does not begin with a ':' and contains at least one ':'. A Mac
108 full pathname causes a '/' to be prepended to the Posix pathname.
109 The algorithm for the rest of the pathname is as follows:
110 For each segment between two ':',
111 if it is non-null, copy as is and then add a '/' at the end,
112 otherwise, insert a "../" into the Posix pathname.
113 Returns 1 if successful; 0 if fails. */
116 mac_to_posix_pathname (const char *mfn, char *ufn, int ufnbuflen)
118 const char *p, *q, *pe;
120 strcpy (ufn, "");
122 if (*mfn == '\0')
123 return 1;
125 p = strchr (mfn, ':');
126 if (p != 0 && p != mfn) /* full pathname */
127 strcat (ufn, "/");
129 p = mfn;
130 if (*p == ':')
131 p++;
133 pe = mfn + strlen (mfn);
134 while (p < pe)
136 q = strchr (p, ':');
137 if (q)
139 if (q == p)
140 { /* two consecutive ':' */
141 if (strlen (ufn) + 3 >= ufnbuflen)
142 return 0;
143 strcat (ufn, "../");
145 else
147 if (strlen (ufn) + (q - p) + 1 >= ufnbuflen)
148 return 0;
149 string_cat_and_replace (ufn, p, q - p, '/', ':');
150 strcat (ufn, "/");
152 p = q + 1;
154 else
156 if (strlen (ufn) + (pe - p) >= ufnbuflen)
157 return 0;
158 string_cat_and_replace (ufn, p, pe - p, '/', ':');
159 /* no separator for last one */
160 p = pe;
164 return 1;
168 extern char *get_temp_dir_name ();
171 /* Convert a Posix pathname to Mac form. Approximately reverse of the
172 above in algorithm. */
175 posix_to_mac_pathname (const char *ufn, char *mfn, int mfnbuflen)
177 const char *p, *q, *pe;
178 char expanded_pathname[MAXPATHLEN+1];
180 strcpy (mfn, "");
182 if (*ufn == '\0')
183 return 1;
185 p = ufn;
187 /* Check for and handle volume names. Last comparison: strangely
188 somewhere "/.emacs" is passed. A temporary fix for now. */
189 if (*p == '/' && strchr (p+1, '/') == NULL && strcmp (p, "/.emacs") != 0)
191 if (strlen (p) + 1 > mfnbuflen)
192 return 0;
193 strcpy (mfn, p+1);
194 strcat (mfn, ":");
195 return 1;
198 /* expand to emacs dir found by init_emacs_passwd_dir */
199 if (strncmp (p, "~emacs/", 7) == 0)
201 struct passwd *pw = getpwnam ("emacs");
202 p += 7;
203 if (strlen (pw->pw_dir) + strlen (p) > MAXPATHLEN)
204 return 0;
205 strcpy (expanded_pathname, pw->pw_dir);
206 strcat (expanded_pathname, p);
207 p = expanded_pathname;
208 /* now p points to the pathname with emacs dir prefix */
210 else if (strncmp (p, "/tmp/", 5) == 0)
212 char *t = get_temp_dir_name ();
213 p += 5;
214 if (strlen (t) + strlen (p) > MAXPATHLEN)
215 return 0;
216 strcpy (expanded_pathname, t);
217 strcat (expanded_pathname, p);
218 p = expanded_pathname;
219 /* now p points to the pathname with emacs dir prefix */
221 else if (*p != '/') /* relative pathname */
222 strcat (mfn, ":");
224 if (*p == '/')
225 p++;
227 pe = p + strlen (p);
228 while (p < pe)
230 q = strchr (p, '/');
231 if (q)
233 if (q - p == 2 && *p == '.' && *(p+1) == '.')
235 if (strlen (mfn) + 1 >= mfnbuflen)
236 return 0;
237 strcat (mfn, ":");
239 else
241 if (strlen (mfn) + (q - p) + 1 >= mfnbuflen)
242 return 0;
243 string_cat_and_replace (mfn, p, q - p, ':', '/');
244 strcat (mfn, ":");
246 p = q + 1;
248 else
250 if (strlen (mfn) + (pe - p) >= mfnbuflen)
251 return 0;
252 string_cat_and_replace (mfn, p, pe - p, ':', '/');
253 p = pe;
257 return 1;
261 /***********************************************************************
262 Conversion between Lisp and Core Foundation objects
263 ***********************************************************************/
265 #if TARGET_API_MAC_CARBON
266 static Lisp_Object Qstring, Qnumber, Qboolean, Qdate, Qdata;
267 static Lisp_Object Qarray, Qdictionary;
268 #define DECODE_UTF_8(str) code_convert_string_norecord (str, Qutf_8, 0)
270 struct cfdict_context
272 Lisp_Object *result;
273 int with_tag, hash_bound;
276 /* C string to CFString. */
278 CFStringRef
279 cfstring_create_with_utf8_cstring (c_str)
280 const char *c_str;
282 CFStringRef str;
284 str = CFStringCreateWithCString (NULL, c_str, kCFStringEncodingUTF8);
285 if (str == NULL)
286 /* Failed to interpret as UTF 8. Fall back on Mac Roman. */
287 str = CFStringCreateWithCString (NULL, c_str, kCFStringEncodingMacRoman);
289 return str;
293 /* Lisp string to CFString. */
295 CFStringRef
296 cfstring_create_with_string (s)
297 Lisp_Object s;
299 CFStringRef string = NULL;
301 if (STRING_MULTIBYTE (s))
303 char *p, *end = SDATA (s) + SBYTES (s);
305 for (p = SDATA (s); p < end; p++)
306 if (!isascii (*p))
308 s = ENCODE_UTF_8 (s);
309 break;
311 string = CFStringCreateWithBytes (NULL, SDATA (s), SBYTES (s),
312 kCFStringEncodingUTF8, false);
315 if (string == NULL)
316 /* Failed to interpret as UTF 8. Fall back on Mac Roman. */
317 string = CFStringCreateWithBytes (NULL, SDATA (s), SBYTES (s),
318 kCFStringEncodingMacRoman, false);
320 return string;
324 /* From CFData to a lisp string. Always returns a unibyte string. */
326 Lisp_Object
327 cfdata_to_lisp (data)
328 CFDataRef data;
330 CFIndex len = CFDataGetLength (data);
331 Lisp_Object result = make_uninit_string (len);
333 CFDataGetBytes (data, CFRangeMake (0, len), SDATA (result));
335 return result;
339 /* From CFString to a lisp string. Never returns a unibyte string
340 (even if it only contains ASCII characters).
341 This may cause GC during code conversion. */
343 Lisp_Object
344 cfstring_to_lisp (string)
345 CFStringRef string;
347 Lisp_Object result = Qnil;
348 const char *s = CFStringGetCStringPtr (string, kCFStringEncodingUTF8);
350 if (s)
351 result = make_unibyte_string (s, strlen (s));
352 else
354 CFDataRef data =
355 CFStringCreateExternalRepresentation (NULL, string,
356 kCFStringEncodingUTF8, '?');
358 if (data)
360 result = cfdata_to_lisp (data);
361 CFRelease (data);
365 if (!NILP (result))
367 result = DECODE_UTF_8 (result);
368 /* This may be superfluous. Just to make sure that the result
369 is a multibyte string. */
370 result = string_to_multibyte (result);
373 return result;
377 /* CFNumber to a lisp integer or a lisp float. */
379 Lisp_Object
380 cfnumber_to_lisp (number)
381 CFNumberRef number;
383 Lisp_Object result = Qnil;
384 #if BITS_PER_EMACS_INT > 32
385 SInt64 int_val;
386 CFNumberType emacs_int_type = kCFNumberSInt64Type;
387 #else
388 SInt32 int_val;
389 CFNumberType emacs_int_type = kCFNumberSInt32Type;
390 #endif
391 double float_val;
393 if (CFNumberGetValue (number, emacs_int_type, &int_val)
394 && !FIXNUM_OVERFLOW_P (int_val))
395 result = make_number (int_val);
396 else
397 if (CFNumberGetValue (number, kCFNumberDoubleType, &float_val))
398 result = make_float (float_val);
399 return result;
403 /* CFDate to a list of three integers as in a return value of
404 `current-time'. */
406 Lisp_Object
407 cfdate_to_lisp (date)
408 CFDateRef date;
410 static const CFGregorianDate epoch_gdate = {1970, 1, 1, 0, 0, 0.0};
411 static CFAbsoluteTime epoch = 0.0, sec;
412 int high, low;
414 if (epoch == 0.0)
415 epoch = CFGregorianDateGetAbsoluteTime (epoch_gdate, NULL);
417 sec = CFDateGetAbsoluteTime (date) - epoch;
418 high = sec / 65536.0;
419 low = sec - high * 65536.0;
421 return list3 (make_number (high), make_number (low), make_number (0));
425 /* CFBoolean to a lisp symbol, `t' or `nil'. */
427 Lisp_Object
428 cfboolean_to_lisp (boolean)
429 CFBooleanRef boolean;
431 return CFBooleanGetValue (boolean) ? Qt : Qnil;
435 /* Any Core Foundation object to a (lengthy) lisp string. */
437 Lisp_Object
438 cfobject_desc_to_lisp (object)
439 CFTypeRef object;
441 Lisp_Object result = Qnil;
442 CFStringRef desc = CFCopyDescription (object);
444 if (desc)
446 result = cfstring_to_lisp (desc);
447 CFRelease (desc);
450 return result;
454 /* Callback functions for cfproperty_list_to_lisp. */
456 static void
457 cfdictionary_add_to_list (key, value, context)
458 const void *key;
459 const void *value;
460 void *context;
462 struct cfdict_context *cxt = (struct cfdict_context *)context;
464 *cxt->result =
465 Fcons (Fcons (cfstring_to_lisp (key),
466 cfproperty_list_to_lisp (value, cxt->with_tag,
467 cxt->hash_bound)),
468 *cxt->result);
471 static void
472 cfdictionary_puthash (key, value, context)
473 const void *key;
474 const void *value;
475 void *context;
477 Lisp_Object lisp_key = cfstring_to_lisp (key);
478 struct cfdict_context *cxt = (struct cfdict_context *)context;
479 struct Lisp_Hash_Table *h = XHASH_TABLE (*(cxt->result));
480 unsigned hash_code;
482 hash_lookup (h, lisp_key, &hash_code);
483 hash_put (h, lisp_key,
484 cfproperty_list_to_lisp (value, cxt->with_tag, cxt->hash_bound),
485 hash_code);
489 /* Convert CFPropertyList PLIST to a lisp object. If WITH_TAG is
490 non-zero, a symbol that represents the type of the original Core
491 Foundation object is prepended. HASH_BOUND specifies which kinds
492 of the lisp objects, alists or hash tables, are used as the targets
493 of the conversion from CFDictionary. If HASH_BOUND is negative,
494 always generate alists. If HASH_BOUND >= 0, generate an alist if
495 the number of keys in the dictionary is smaller than HASH_BOUND,
496 and a hash table otherwise. */
498 Lisp_Object
499 cfproperty_list_to_lisp (plist, with_tag, hash_bound)
500 CFPropertyListRef plist;
501 int with_tag, hash_bound;
503 CFTypeID type_id = CFGetTypeID (plist);
504 Lisp_Object tag = Qnil, result = Qnil;
505 struct gcpro gcpro1, gcpro2;
507 GCPRO2 (tag, result);
509 if (type_id == CFStringGetTypeID ())
511 tag = Qstring;
512 result = cfstring_to_lisp (plist);
514 else if (type_id == CFNumberGetTypeID ())
516 tag = Qnumber;
517 result = cfnumber_to_lisp (plist);
519 else if (type_id == CFBooleanGetTypeID ())
521 tag = Qboolean;
522 result = cfboolean_to_lisp (plist);
524 else if (type_id == CFDateGetTypeID ())
526 tag = Qdate;
527 result = cfdate_to_lisp (plist);
529 else if (type_id == CFDataGetTypeID ())
531 tag = Qdata;
532 result = cfdata_to_lisp (plist);
534 else if (type_id == CFArrayGetTypeID ())
536 CFIndex index, count = CFArrayGetCount (plist);
538 tag = Qarray;
539 result = Fmake_vector (make_number (count), Qnil);
540 for (index = 0; index < count; index++)
541 XVECTOR (result)->contents[index] =
542 cfproperty_list_to_lisp (CFArrayGetValueAtIndex (plist, index),
543 with_tag, hash_bound);
545 else if (type_id == CFDictionaryGetTypeID ())
547 struct cfdict_context context;
548 CFIndex count = CFDictionaryGetCount (plist);
550 tag = Qdictionary;
551 context.result = &result;
552 context.with_tag = with_tag;
553 context.hash_bound = hash_bound;
554 if (hash_bound < 0 || count < hash_bound)
556 result = Qnil;
557 CFDictionaryApplyFunction (plist, cfdictionary_add_to_list,
558 &context);
560 else
562 result = make_hash_table (Qequal,
563 make_number (count),
564 make_float (DEFAULT_REHASH_SIZE),
565 make_float (DEFAULT_REHASH_THRESHOLD),
566 Qnil, Qnil, Qnil);
567 CFDictionaryApplyFunction (plist, cfdictionary_puthash,
568 &context);
571 else
572 abort ();
574 UNGCPRO;
576 if (with_tag)
577 result = Fcons (tag, result);
579 return result;
581 #endif
584 /***********************************************************************
585 Emulation of the X Resource Manager
586 ***********************************************************************/
588 /* Parser functions for resource lines. Each function takes an
589 address of a variable whose value points to the head of a string.
590 The value will be advanced so that it points to the next character
591 of the parsed part when the function returns.
593 A resource name such as "Emacs*font" is parsed into a non-empty
594 list called `quarks'. Each element is either a Lisp string that
595 represents a concrete component, a Lisp symbol LOOSE_BINDING
596 (actually Qlambda) that represents any number (>=0) of intervening
597 components, or a Lisp symbol SINGLE_COMPONENT (actually Qquote)
598 that represents as any single component. */
600 #define P (*p)
602 #define LOOSE_BINDING Qlambda /* '*' ("L"oose) */
603 #define SINGLE_COMPONENT Qquote /* '?' ("Q"uestion) */
605 static void
606 skip_white_space (p)
607 char **p;
609 /* WhiteSpace = {<space> | <horizontal tab>} */
610 while (*P == ' ' || *P == '\t')
611 P++;
614 static int
615 parse_comment (p)
616 char **p;
618 /* Comment = "!" {<any character except null or newline>} */
619 if (*P == '!')
621 P++;
622 while (*P)
623 if (*P++ == '\n')
624 break;
625 return 1;
627 else
628 return 0;
631 /* Don't interpret filename. Just skip until the newline. */
632 static int
633 parse_include_file (p)
634 char **p;
636 /* IncludeFile = "#" WhiteSpace "include" WhiteSpace FileName WhiteSpace */
637 if (*P == '#')
639 P++;
640 while (*P)
641 if (*P++ == '\n')
642 break;
643 return 1;
645 else
646 return 0;
649 static char
650 parse_binding (p)
651 char **p;
653 /* Binding = "." | "*" */
654 if (*P == '.' || *P == '*')
656 char binding = *P++;
658 while (*P == '.' || *P == '*')
659 if (*P++ == '*')
660 binding = '*';
661 return binding;
663 else
664 return '\0';
667 static Lisp_Object
668 parse_component (p)
669 char **p;
671 /* Component = "?" | ComponentName
672 ComponentName = NameChar {NameChar}
673 NameChar = "a"-"z" | "A"-"Z" | "0"-"9" | "_" | "-" */
674 if (*P == '?')
676 P++;
677 return SINGLE_COMPONENT;
679 else if (isalnum (*P) || *P == '_' || *P == '-')
681 char *start = P++;
683 while (isalnum (*P) || *P == '_' || *P == '-')
684 P++;
686 return make_unibyte_string (start, P - start);
688 else
689 return Qnil;
692 static Lisp_Object
693 parse_resource_name (p)
694 char **p;
696 Lisp_Object result = Qnil, component;
697 char binding;
699 /* ResourceName = [Binding] {Component Binding} ComponentName */
700 if (parse_binding (p) == '*')
701 result = Fcons (LOOSE_BINDING, result);
703 component = parse_component (p);
704 if (NILP (component))
705 return Qnil;
707 result = Fcons (component, result);
708 while ((binding = parse_binding (p)) != '\0')
710 if (binding == '*')
711 result = Fcons (LOOSE_BINDING, result);
712 component = parse_component (p);
713 if (NILP (component))
714 return Qnil;
715 else
716 result = Fcons (component, result);
719 /* The final component should not be '?'. */
720 if (EQ (component, SINGLE_COMPONENT))
721 return Qnil;
723 return Fnreverse (result);
726 static Lisp_Object
727 parse_value (p)
728 char **p;
730 char *q, *buf;
731 Lisp_Object seq = Qnil, result;
732 int buf_len, total_len = 0, len, continue_p;
734 q = strchr (P, '\n');
735 buf_len = q ? q - P : strlen (P);
736 buf = xmalloc (buf_len);
738 while (1)
740 q = buf;
741 continue_p = 0;
742 while (*P)
744 if (*P == '\n')
746 P++;
747 break;
749 else if (*P == '\\')
751 P++;
752 if (*P == '\0')
753 break;
754 else if (*P == '\n')
756 P++;
757 continue_p = 1;
758 break;
760 else if (*P == 'n')
762 *q++ = '\n';
763 P++;
765 else if ('0' <= P[0] && P[0] <= '7'
766 && '0' <= P[1] && P[1] <= '7'
767 && '0' <= P[2] && P[2] <= '7')
769 *q++ = (P[0] - '0' << 6) + (P[1] - '0' << 3) + (P[2] - '0');
770 P += 3;
772 else
773 *q++ = *P++;
775 else
776 *q++ = *P++;
778 len = q - buf;
779 seq = Fcons (make_unibyte_string (buf, len), seq);
780 total_len += len;
782 if (continue_p)
784 q = strchr (P, '\n');
785 len = q ? q - P : strlen (P);
786 if (len > buf_len)
788 xfree (buf);
789 buf_len = len;
790 buf = xmalloc (buf_len);
793 else
794 break;
796 xfree (buf);
798 if (SBYTES (XCAR (seq)) == total_len)
799 return make_string (SDATA (XCAR (seq)), total_len);
800 else
802 buf = xmalloc (total_len);
803 q = buf + total_len;
804 for (; CONSP (seq); seq = XCDR (seq))
806 len = SBYTES (XCAR (seq));
807 q -= len;
808 memcpy (q, SDATA (XCAR (seq)), len);
810 result = make_string (buf, total_len);
811 xfree (buf);
812 return result;
816 static Lisp_Object
817 parse_resource_line (p)
818 char **p;
820 Lisp_Object quarks, value;
822 /* ResourceLine = Comment | IncludeFile | ResourceSpec | <empty line> */
823 if (parse_comment (p) || parse_include_file (p))
824 return Qnil;
826 /* ResourceSpec = WhiteSpace ResourceName WhiteSpace ":" WhiteSpace Value */
827 skip_white_space (p);
828 quarks = parse_resource_name (p);
829 if (NILP (quarks))
830 goto cleanup;
831 skip_white_space (p);
832 if (*P != ':')
833 goto cleanup;
834 P++;
835 skip_white_space (p);
836 value = parse_value (p);
837 return Fcons (quarks, value);
839 cleanup:
840 /* Skip the remaining data as a dummy value. */
841 parse_value (p);
842 return Qnil;
845 #undef P
847 /* Equivalents of X Resource Manager functions.
849 An X Resource Database acts as a collection of resource names and
850 associated values. It is implemented as a trie on quarks. Namely,
851 each edge is labeled by either a string, LOOSE_BINDING, or
852 SINGLE_COMPONENT. Each node has a node id, which is a unique
853 nonnegative integer, and the root node id is 0. A database is
854 implemented as a hash table that maps a pair (SRC-NODE-ID .
855 EDGE-LABEL) to DEST-NODE-ID. It also holds a maximum node id used
856 in the table as a value for HASHKEY_MAX_NID. A value associated to
857 a node is recorded as a value for the node id. */
859 #define HASHKEY_MAX_NID (make_number (0))
861 static XrmDatabase
862 xrm_create_database ()
864 XrmDatabase database;
866 database = make_hash_table (Qequal, make_number (DEFAULT_HASH_SIZE),
867 make_float (DEFAULT_REHASH_SIZE),
868 make_float (DEFAULT_REHASH_THRESHOLD),
869 Qnil, Qnil, Qnil);
870 Fputhash (HASHKEY_MAX_NID, make_number (0), database);
872 return database;
875 static void
876 xrm_q_put_resource (database, quarks, value)
877 XrmDatabase database;
878 Lisp_Object quarks, value;
880 struct Lisp_Hash_Table *h = XHASH_TABLE (database);
881 unsigned hash_code;
882 int max_nid, i;
883 Lisp_Object node_id, key;
885 max_nid = XINT (Fgethash (HASHKEY_MAX_NID, database, Qnil));
887 XSETINT (node_id, 0);
888 for (; CONSP (quarks); quarks = XCDR (quarks))
890 key = Fcons (node_id, XCAR (quarks));
891 i = hash_lookup (h, key, &hash_code);
892 if (i < 0)
894 max_nid++;
895 XSETINT (node_id, max_nid);
896 hash_put (h, key, node_id, hash_code);
898 else
899 node_id = HASH_VALUE (h, i);
901 Fputhash (node_id, value, database);
903 Fputhash (HASHKEY_MAX_NID, make_number (max_nid), database);
906 /* Merge multiple resource entries specified by DATA into a resource
907 database DATABASE. DATA points to the head of a null-terminated
908 string consisting of multiple resource lines. It's like a
909 combination of XrmGetStringDatabase and XrmMergeDatabases. */
911 void
912 xrm_merge_string_database (database, data)
913 XrmDatabase database;
914 char *data;
916 Lisp_Object quarks_value;
918 while (*data)
920 quarks_value = parse_resource_line (&data);
921 if (!NILP (quarks_value))
922 xrm_q_put_resource (database,
923 XCAR (quarks_value), XCDR (quarks_value));
927 static Lisp_Object
928 xrm_q_get_resource_1 (database, node_id, quark_name, quark_class)
929 XrmDatabase database;
930 Lisp_Object node_id, quark_name, quark_class;
932 struct Lisp_Hash_Table *h = XHASH_TABLE (database);
933 Lisp_Object key, labels[3], value;
934 int i, k;
936 if (!CONSP (quark_name))
937 return Fgethash (node_id, database, Qnil);
939 /* First, try tight bindings */
940 labels[0] = XCAR (quark_name);
941 labels[1] = XCAR (quark_class);
942 labels[2] = SINGLE_COMPONENT;
944 key = Fcons (node_id, Qnil);
945 for (k = 0; k < sizeof (labels) / sizeof (*labels); k++)
947 XSETCDR (key, labels[k]);
948 i = hash_lookup (h, key, NULL);
949 if (i >= 0)
951 value = xrm_q_get_resource_1 (database, HASH_VALUE (h, i),
952 XCDR (quark_name), XCDR (quark_class));
953 if (!NILP (value))
954 return value;
958 /* Then, try loose bindings */
959 XSETCDR (key, LOOSE_BINDING);
960 i = hash_lookup (h, key, NULL);
961 if (i >= 0)
963 value = xrm_q_get_resource_1 (database, HASH_VALUE (h, i),
964 quark_name, quark_class);
965 if (!NILP (value))
966 return value;
967 else
968 return xrm_q_get_resource_1 (database, node_id,
969 XCDR (quark_name), XCDR (quark_class));
971 else
972 return Qnil;
975 static Lisp_Object
976 xrm_q_get_resource (database, quark_name, quark_class)
977 XrmDatabase database;
978 Lisp_Object quark_name, quark_class;
980 return xrm_q_get_resource_1 (database, make_number (0),
981 quark_name, quark_class);
984 /* Retrieve a resource value for the specified NAME and CLASS from the
985 resource database DATABASE. It corresponds to XrmGetResource. */
987 Lisp_Object
988 xrm_get_resource (database, name, class)
989 XrmDatabase database;
990 char *name, *class;
992 Lisp_Object quark_name, quark_class, tmp;
993 int nn, nc;
995 quark_name = parse_resource_name (&name);
996 if (*name != '\0')
997 return Qnil;
998 for (tmp = quark_name, nn = 0; CONSP (tmp); tmp = XCDR (tmp), nn++)
999 if (!STRINGP (XCAR (tmp)))
1000 return Qnil;
1002 quark_class = parse_resource_name (&class);
1003 if (*class != '\0')
1004 return Qnil;
1005 for (tmp = quark_class, nc = 0; CONSP (tmp); tmp = XCDR (tmp), nc++)
1006 if (!STRINGP (XCAR (tmp)))
1007 return Qnil;
1009 if (nn != nc)
1010 return Qnil;
1011 else
1012 return xrm_q_get_resource (database, quark_name, quark_class);
1015 #if TARGET_API_MAC_CARBON
1016 static Lisp_Object
1017 xrm_cfproperty_list_to_value (plist)
1018 CFPropertyListRef plist;
1020 CFTypeID type_id = CFGetTypeID (plist);
1022 if (type_id == CFStringGetTypeID ())
1023 return cfstring_to_lisp (plist);
1024 else if (type_id == CFNumberGetTypeID ())
1026 CFStringRef string;
1027 Lisp_Object result = Qnil;
1029 string = CFStringCreateWithFormat (NULL, NULL, CFSTR ("%@"), plist);
1030 if (string)
1032 result = cfstring_to_lisp (string);
1033 CFRelease (string);
1035 return result;
1037 else if (type_id == CFBooleanGetTypeID ())
1038 return build_string (CFBooleanGetValue (plist) ? "true" : "false");
1039 else if (type_id == CFDataGetTypeID ())
1040 return cfdata_to_lisp (plist);
1041 else
1042 return Qnil;
1044 #endif
1046 /* Create a new resource database from the preferences for the
1047 application APPLICATION. APPLICATION is either a string that
1048 specifies an application ID, or NULL that represents the current
1049 application. */
1051 XrmDatabase
1052 xrm_get_preference_database (application)
1053 char *application;
1055 #if TARGET_API_MAC_CARBON
1056 CFStringRef app_id, *keys, user_doms[2], host_doms[2];
1057 CFMutableSetRef key_set = NULL;
1058 CFArrayRef key_array;
1059 CFIndex index, count;
1060 char *res_name;
1061 XrmDatabase database;
1062 Lisp_Object quarks = Qnil, value = Qnil;
1063 CFPropertyListRef plist;
1064 int iu, ih;
1065 struct gcpro gcpro1, gcpro2, gcpro3;
1067 user_doms[0] = kCFPreferencesCurrentUser;
1068 user_doms[1] = kCFPreferencesAnyUser;
1069 host_doms[0] = kCFPreferencesCurrentHost;
1070 host_doms[1] = kCFPreferencesAnyHost;
1072 database = xrm_create_database ();
1074 GCPRO3 (database, quarks, value);
1076 BLOCK_INPUT;
1078 app_id = kCFPreferencesCurrentApplication;
1079 if (application)
1081 app_id = cfstring_create_with_utf8_cstring (application);
1082 if (app_id == NULL)
1083 goto out;
1086 key_set = CFSetCreateMutable (NULL, 0, &kCFCopyStringSetCallBacks);
1087 if (key_set == NULL)
1088 goto out;
1089 for (iu = 0; iu < sizeof (user_doms) / sizeof (*user_doms) ; iu++)
1090 for (ih = 0; ih < sizeof (host_doms) / sizeof (*host_doms); ih++)
1092 key_array = CFPreferencesCopyKeyList (app_id, user_doms[iu],
1093 host_doms[ih]);
1094 if (key_array)
1096 count = CFArrayGetCount (key_array);
1097 for (index = 0; index < count; index++)
1098 CFSetAddValue (key_set,
1099 CFArrayGetValueAtIndex (key_array, index));
1100 CFRelease (key_array);
1104 count = CFSetGetCount (key_set);
1105 keys = xmalloc (sizeof (CFStringRef) * count);
1106 if (keys == NULL)
1107 goto out;
1108 CFSetGetValues (key_set, (const void **)keys);
1109 for (index = 0; index < count; index++)
1111 res_name = SDATA (cfstring_to_lisp (keys[index]));
1112 quarks = parse_resource_name (&res_name);
1113 if (!(NILP (quarks) || *res_name))
1115 plist = CFPreferencesCopyAppValue (keys[index], app_id);
1116 value = xrm_cfproperty_list_to_value (plist);
1117 CFRelease (plist);
1118 if (!NILP (value))
1119 xrm_q_put_resource (database, quarks, value);
1123 xfree (keys);
1124 out:
1125 if (key_set)
1126 CFRelease (key_set);
1127 CFRelease (app_id);
1129 UNBLOCK_INPUT;
1131 UNGCPRO;
1133 return database;
1134 #else
1135 return xrm_create_database ();
1136 #endif
1140 #ifndef MAC_OSX
1142 /* The following functions with "sys_" prefix are stubs to Unix
1143 functions that have already been implemented by CW or MPW. The
1144 calls to them in Emacs source course are #define'd to call the sys_
1145 versions by the header files s-mac.h. In these stubs pathnames are
1146 converted between their Unix and Mac forms. */
1149 /* Unix epoch is Jan 1, 1970 while Mac epoch is Jan 1, 1904: 66 years
1150 + 17 leap days. These are for adjusting time values returned by
1151 MacOS Toolbox functions. */
1153 #define MAC_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
1155 #ifdef __MWERKS__
1156 #if __MSL__ < 0x6000
1157 /* CW Pro 5 epoch is Jan 1, 1900 (aaarghhhhh!); remember, 1900 is not
1158 a leap year! This is for adjusting time_t values returned by MSL
1159 functions. */
1160 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 70 + 17) * 24 * 60 * 60)
1161 #else /* __MSL__ >= 0x6000 */
1162 /* CW changes Pro 6 to follow Unix! */
1163 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
1164 #endif /* __MSL__ >= 0x6000 */
1165 #elif __MRC__
1166 /* MPW library functions follow Unix (confused?). */
1167 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
1168 #else /* not __MRC__ */
1169 You lose!!!
1170 #endif /* not __MRC__ */
1173 /* Define our own stat function for both MrC and CW. The reason for
1174 doing this: "stat" is both the name of a struct and function name:
1175 can't use the same trick like that for sys_open, sys_close, etc. to
1176 redirect Emacs's calls to our own version that converts Unix style
1177 filenames to Mac style filename because all sorts of compilation
1178 errors will be generated if stat is #define'd to be sys_stat. */
1181 stat_noalias (const char *path, struct stat *buf)
1183 char mac_pathname[MAXPATHLEN+1];
1184 CInfoPBRec cipb;
1186 if (posix_to_mac_pathname (path, mac_pathname, MAXPATHLEN+1) == 0)
1187 return -1;
1189 c2pstr (mac_pathname);
1190 cipb.hFileInfo.ioNamePtr = mac_pathname;
1191 cipb.hFileInfo.ioVRefNum = 0;
1192 cipb.hFileInfo.ioDirID = 0;
1193 cipb.hFileInfo.ioFDirIndex = 0;
1194 /* set to 0 to get information about specific dir or file */
1196 errno = PBGetCatInfo (&cipb, false);
1197 if (errno == -43) /* -43: fnfErr defined in Errors.h */
1198 errno = ENOENT;
1199 if (errno != noErr)
1200 return -1;
1202 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* bit 4 = 1 for directories */
1204 buf->st_mode = S_IFDIR | S_IREAD | S_IEXEC;
1206 if (!(cipb.hFileInfo.ioFlAttrib & 0x1))
1207 buf->st_mode |= S_IWRITE; /* bit 1 = 1 for locked files/directories */
1208 buf->st_ino = cipb.dirInfo.ioDrDirID;
1209 buf->st_dev = cipb.dirInfo.ioVRefNum;
1210 buf->st_size = cipb.dirInfo.ioDrNmFls;
1211 /* size of dir = number of files and dirs */
1212 buf->st_atime
1213 = buf->st_mtime
1214 = cipb.dirInfo.ioDrMdDat - MAC_UNIX_EPOCH_DIFF;
1215 buf->st_ctime = cipb.dirInfo.ioDrCrDat - MAC_UNIX_EPOCH_DIFF;
1217 else
1219 buf->st_mode = S_IFREG | S_IREAD;
1220 if (!(cipb.hFileInfo.ioFlAttrib & 0x1))
1221 buf->st_mode |= S_IWRITE; /* bit 1 = 1 for locked files/directories */
1222 if (cipb.hFileInfo.ioFlFndrInfo.fdType == 'APPL')
1223 buf->st_mode |= S_IEXEC;
1224 buf->st_ino = cipb.hFileInfo.ioDirID;
1225 buf->st_dev = cipb.hFileInfo.ioVRefNum;
1226 buf->st_size = cipb.hFileInfo.ioFlLgLen;
1227 buf->st_atime
1228 = buf->st_mtime
1229 = cipb.hFileInfo.ioFlMdDat - MAC_UNIX_EPOCH_DIFF;
1230 buf->st_ctime = cipb.hFileInfo.ioFlCrDat - MAC_UNIX_EPOCH_DIFF;
1233 if (cipb.hFileInfo.ioFlFndrInfo.fdFlags & 0x8000)
1235 /* identify alias files as symlinks */
1236 buf->st_mode &= ~S_IFREG;
1237 buf->st_mode |= S_IFLNK;
1240 buf->st_nlink = 1;
1241 buf->st_uid = getuid ();
1242 buf->st_gid = getgid ();
1243 buf->st_rdev = 0;
1245 return 0;
1250 lstat (const char *path, struct stat *buf)
1252 int result;
1253 char true_pathname[MAXPATHLEN+1];
1255 /* Try looking for the file without resolving aliases first. */
1256 if ((result = stat_noalias (path, buf)) >= 0)
1257 return result;
1259 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1260 return -1;
1262 return stat_noalias (true_pathname, buf);
1267 stat (const char *path, struct stat *sb)
1269 int result;
1270 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
1271 int len;
1273 if ((result = stat_noalias (path, sb)) >= 0 &&
1274 ! (sb->st_mode & S_IFLNK))
1275 return result;
1277 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1278 return -1;
1280 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
1281 if (len > -1)
1283 fully_resolved_name[len] = '\0';
1284 /* in fact our readlink terminates strings */
1285 return lstat (fully_resolved_name, sb);
1287 else
1288 return lstat (true_pathname, sb);
1292 #if __MRC__
1293 /* CW defines fstat in stat.mac.c while MPW does not provide this
1294 function. Without the information of how to get from a file
1295 descriptor in MPW StdCLib to a Mac OS file spec, it should be hard
1296 to implement this function. Fortunately, there is only one place
1297 where this function is called in our configuration: in fileio.c,
1298 where only the st_dev and st_ino fields are used to determine
1299 whether two fildes point to different i-nodes to prevent copying
1300 a file onto itself equal. What we have here probably needs
1301 improvement. */
1304 fstat (int fildes, struct stat *buf)
1306 buf->st_dev = 0;
1307 buf->st_ino = fildes;
1308 buf->st_mode = S_IFREG; /* added by T.I. for the copy-file */
1309 return 0; /* success */
1311 #endif /* __MRC__ */
1315 mkdir (const char *dirname, int mode)
1317 #pragma unused(mode)
1319 HFileParam hfpb;
1320 char true_pathname[MAXPATHLEN+1], mac_pathname[MAXPATHLEN+1];
1322 if (find_true_pathname (dirname, true_pathname, MAXPATHLEN+1) == -1)
1323 return -1;
1325 if (posix_to_mac_pathname (true_pathname, mac_pathname, MAXPATHLEN+1) == 0)
1326 return -1;
1328 c2pstr (mac_pathname);
1329 hfpb.ioNamePtr = mac_pathname;
1330 hfpb.ioVRefNum = 0; /* ignored unless name is invalid */
1331 hfpb.ioDirID = 0; /* parent is the root */
1333 errno = PBDirCreate ((HParmBlkPtr) &hfpb, false);
1334 /* just return the Mac OSErr code for now */
1335 return errno == noErr ? 0 : -1;
1339 #undef rmdir
1340 sys_rmdir (const char *dirname)
1342 HFileParam hfpb;
1343 char mac_pathname[MAXPATHLEN+1];
1345 if (posix_to_mac_pathname (dirname, mac_pathname, MAXPATHLEN+1) == 0)
1346 return -1;
1348 c2pstr (mac_pathname);
1349 hfpb.ioNamePtr = mac_pathname;
1350 hfpb.ioVRefNum = 0; /* ignored unless name is invalid */
1351 hfpb.ioDirID = 0; /* parent is the root */
1353 errno = PBHDelete ((HParmBlkPtr) &hfpb, false);
1354 return errno == noErr ? 0 : -1;
1358 #ifdef __MRC__
1359 /* No implementation yet. */
1361 execvp (const char *path, ...)
1363 return -1;
1365 #endif /* __MRC__ */
1369 utime (const char *path, const struct utimbuf *times)
1371 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
1372 int len;
1373 char mac_pathname[MAXPATHLEN+1];
1374 CInfoPBRec cipb;
1376 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1377 return -1;
1379 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
1380 if (len > -1)
1381 fully_resolved_name[len] = '\0';
1382 else
1383 strcpy (fully_resolved_name, true_pathname);
1385 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
1386 return -1;
1388 c2pstr (mac_pathname);
1389 cipb.hFileInfo.ioNamePtr = mac_pathname;
1390 cipb.hFileInfo.ioVRefNum = 0;
1391 cipb.hFileInfo.ioDirID = 0;
1392 cipb.hFileInfo.ioFDirIndex = 0;
1393 /* set to 0 to get information about specific dir or file */
1395 errno = PBGetCatInfo (&cipb, false);
1396 if (errno != noErr)
1397 return -1;
1399 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* bit 4 = 1 for directories */
1401 if (times)
1402 cipb.dirInfo.ioDrMdDat = times->modtime + MAC_UNIX_EPOCH_DIFF;
1403 else
1404 GetDateTime (&cipb.dirInfo.ioDrMdDat);
1406 else
1408 if (times)
1409 cipb.hFileInfo.ioFlMdDat = times->modtime + MAC_UNIX_EPOCH_DIFF;
1410 else
1411 GetDateTime (&cipb.hFileInfo.ioFlMdDat);
1414 errno = PBSetCatInfo (&cipb, false);
1415 return errno == noErr ? 0 : -1;
1419 #ifndef F_OK
1420 #define F_OK 0
1421 #endif
1422 #ifndef X_OK
1423 #define X_OK 1
1424 #endif
1425 #ifndef W_OK
1426 #define W_OK 2
1427 #endif
1429 /* Like stat, but test for access mode in hfpb.ioFlAttrib */
1431 access (const char *path, int mode)
1433 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
1434 int len;
1435 char mac_pathname[MAXPATHLEN+1];
1436 CInfoPBRec cipb;
1438 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1439 return -1;
1441 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
1442 if (len > -1)
1443 fully_resolved_name[len] = '\0';
1444 else
1445 strcpy (fully_resolved_name, true_pathname);
1447 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
1448 return -1;
1450 c2pstr (mac_pathname);
1451 cipb.hFileInfo.ioNamePtr = mac_pathname;
1452 cipb.hFileInfo.ioVRefNum = 0;
1453 cipb.hFileInfo.ioDirID = 0;
1454 cipb.hFileInfo.ioFDirIndex = 0;
1455 /* set to 0 to get information about specific dir or file */
1457 errno = PBGetCatInfo (&cipb, false);
1458 if (errno != noErr)
1459 return -1;
1461 if (mode == F_OK) /* got this far, file exists */
1462 return 0;
1464 if (mode & X_OK)
1465 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* path refers to a directory */
1466 return 0;
1467 else
1469 if (cipb.hFileInfo.ioFlFndrInfo.fdType == 'APPL')
1470 return 0;
1471 else
1472 return -1;
1475 if (mode & W_OK)
1476 return (cipb.hFileInfo.ioFlAttrib & 0x1) ? -1 : 0;
1477 /* don't allow if lock bit is on */
1479 return -1;
1483 #define DEV_NULL_FD 0x10000
1485 #undef open
1487 sys_open (const char *path, int oflag)
1489 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
1490 int len;
1491 char mac_pathname[MAXPATHLEN+1];
1493 if (strcmp (path, "/dev/null") == 0)
1494 return DEV_NULL_FD; /* some bogus fd to be ignored in write */
1496 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1497 return -1;
1499 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
1500 if (len > -1)
1501 fully_resolved_name[len] = '\0';
1502 else
1503 strcpy (fully_resolved_name, true_pathname);
1505 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
1506 return -1;
1507 else
1509 #ifdef __MRC__
1510 int res = open (mac_pathname, oflag);
1511 /* if (oflag == O_WRONLY || oflag == O_RDWR) */
1512 if (oflag & O_CREAT)
1513 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
1514 return res;
1515 #else /* not __MRC__ */
1516 return open (mac_pathname, oflag);
1517 #endif /* not __MRC__ */
1522 #undef creat
1524 sys_creat (const char *path, mode_t mode)
1526 char true_pathname[MAXPATHLEN+1];
1527 int len;
1528 char mac_pathname[MAXPATHLEN+1];
1530 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1531 return -1;
1533 if (!posix_to_mac_pathname (true_pathname, mac_pathname, MAXPATHLEN+1))
1534 return -1;
1535 else
1537 #ifdef __MRC__
1538 int result = creat (mac_pathname);
1539 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
1540 return result;
1541 #else /* not __MRC__ */
1542 return creat (mac_pathname, mode);
1543 #endif /* not __MRC__ */
1548 #undef unlink
1550 sys_unlink (const char *path)
1552 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
1553 int len;
1554 char mac_pathname[MAXPATHLEN+1];
1556 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1557 return -1;
1559 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
1560 if (len > -1)
1561 fully_resolved_name[len] = '\0';
1562 else
1563 strcpy (fully_resolved_name, true_pathname);
1565 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
1566 return -1;
1567 else
1568 return unlink (mac_pathname);
1572 #undef read
1574 sys_read (int fildes, char *buf, int count)
1576 if (fildes == 0) /* this should not be used for console input */
1577 return -1;
1578 else
1579 #if __MSL__ >= 0x6000
1580 return _read (fildes, buf, count);
1581 #else
1582 return read (fildes, buf, count);
1583 #endif
1587 #undef write
1589 sys_write (int fildes, const char *buf, int count)
1591 if (fildes == DEV_NULL_FD)
1592 return count;
1593 else
1594 #if __MSL__ >= 0x6000
1595 return _write (fildes, buf, count);
1596 #else
1597 return write (fildes, buf, count);
1598 #endif
1602 #undef rename
1604 sys_rename (const char * old_name, const char * new_name)
1606 char true_old_pathname[MAXPATHLEN+1], true_new_pathname[MAXPATHLEN+1];
1607 char fully_resolved_old_name[MAXPATHLEN+1];
1608 int len;
1609 char mac_old_name[MAXPATHLEN+1], mac_new_name[MAXPATHLEN+1];
1611 if (find_true_pathname (old_name, true_old_pathname, MAXPATHLEN+1) == -1)
1612 return -1;
1614 len = readlink (true_old_pathname, fully_resolved_old_name, MAXPATHLEN);
1615 if (len > -1)
1616 fully_resolved_old_name[len] = '\0';
1617 else
1618 strcpy (fully_resolved_old_name, true_old_pathname);
1620 if (find_true_pathname (new_name, true_new_pathname, MAXPATHLEN+1) == -1)
1621 return -1;
1623 if (strcmp (fully_resolved_old_name, true_new_pathname) == 0)
1624 return 0;
1626 if (!posix_to_mac_pathname (fully_resolved_old_name,
1627 mac_old_name,
1628 MAXPATHLEN+1))
1629 return -1;
1631 if (!posix_to_mac_pathname(true_new_pathname, mac_new_name, MAXPATHLEN+1))
1632 return -1;
1634 /* If a file with new_name already exists, rename deletes the old
1635 file in Unix. CW version fails in these situation. So we add a
1636 call to unlink here. */
1637 (void) unlink (mac_new_name);
1639 return rename (mac_old_name, mac_new_name);
1643 #undef fopen
1644 extern FILE *fopen (const char *name, const char *mode);
1645 FILE *
1646 sys_fopen (const char *name, const char *mode)
1648 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
1649 int len;
1650 char mac_pathname[MAXPATHLEN+1];
1652 if (find_true_pathname (name, true_pathname, MAXPATHLEN+1) == -1)
1653 return 0;
1655 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
1656 if (len > -1)
1657 fully_resolved_name[len] = '\0';
1658 else
1659 strcpy (fully_resolved_name, true_pathname);
1661 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
1662 return 0;
1663 else
1665 #ifdef __MRC__
1666 if (mode[0] == 'w' || mode[0] == 'a')
1667 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
1668 #endif /* not __MRC__ */
1669 return fopen (mac_pathname, mode);
1674 #include "keyboard.h"
1675 extern Boolean mac_wait_next_event (EventRecord *, UInt32, Boolean);
1678 select (n, rfds, wfds, efds, timeout)
1679 int n;
1680 SELECT_TYPE *rfds;
1681 SELECT_TYPE *wfds;
1682 SELECT_TYPE *efds;
1683 struct timeval *timeout;
1685 OSErr err;
1686 #if TARGET_API_MAC_CARBON
1687 EventTimeout timeout_sec =
1688 (timeout
1689 ? (EMACS_SECS (*timeout) * kEventDurationSecond
1690 + EMACS_USECS (*timeout) * kEventDurationMicrosecond)
1691 : kEventDurationForever);
1693 BLOCK_INPUT;
1694 err = ReceiveNextEvent (0, NULL, timeout_sec, kEventLeaveInQueue, NULL);
1695 UNBLOCK_INPUT;
1696 #else /* not TARGET_API_MAC_CARBON */
1697 EventRecord e;
1698 UInt32 sleep_time = EMACS_SECS (*timeout) * 60 +
1699 ((EMACS_USECS (*timeout) * 60) / 1000000);
1701 /* Can only handle wait for keyboard input. */
1702 if (n > 1 || wfds || efds)
1703 return -1;
1705 /* Also return true if an event other than a keyDown has occurred.
1706 This causes kbd_buffer_get_event in keyboard.c to call
1707 read_avail_input which in turn calls XTread_socket to poll for
1708 these events. Otherwise these never get processed except but a
1709 very slow poll timer. */
1710 if (mac_wait_next_event (&e, sleep_time, false))
1711 err = noErr;
1712 else
1713 err = -9875; /* eventLoopTimedOutErr */
1714 #endif /* not TARGET_API_MAC_CARBON */
1716 if (FD_ISSET (0, rfds))
1717 if (err == noErr)
1718 return 1;
1719 else
1721 FD_ZERO (rfds);
1722 return 0;
1724 else
1725 if (err == noErr)
1727 if (input_polling_used ())
1729 /* It could be confusing if a real alarm arrives while
1730 processing the fake one. Turn it off and let the
1731 handler reset it. */
1732 extern void poll_for_input_1 P_ ((void));
1733 int old_poll_suppress_count = poll_suppress_count;
1734 poll_suppress_count = 1;
1735 poll_for_input_1 ();
1736 poll_suppress_count = old_poll_suppress_count;
1738 errno = EINTR;
1739 return -1;
1741 else
1742 return 0;
1746 /* Simulation of SIGALRM. The stub for function signal stores the
1747 signal handler function in alarm_signal_func if a SIGALRM is
1748 encountered. */
1750 #include <signal.h>
1751 #include "syssignal.h"
1753 static TMTask mac_atimer_task;
1755 static QElemPtr mac_atimer_qlink = (QElemPtr) &mac_atimer_task;
1757 static int signal_mask = 0;
1759 #ifdef __MRC__
1760 __sigfun alarm_signal_func = (__sigfun) 0;
1761 #elif __MWERKS__
1762 __signal_func_ptr alarm_signal_func = (__signal_func_ptr) 0;
1763 #else /* not __MRC__ and not __MWERKS__ */
1764 You lose!!!
1765 #endif /* not __MRC__ and not __MWERKS__ */
1767 #undef signal
1768 #ifdef __MRC__
1769 extern __sigfun signal (int signal, __sigfun signal_func);
1770 __sigfun
1771 sys_signal (int signal_num, __sigfun signal_func)
1772 #elif __MWERKS__
1773 extern __signal_func_ptr signal (int signal, __signal_func_ptr signal_func);
1774 __signal_func_ptr
1775 sys_signal (int signal_num, __signal_func_ptr signal_func)
1776 #else /* not __MRC__ and not __MWERKS__ */
1777 You lose!!!
1778 #endif /* not __MRC__ and not __MWERKS__ */
1780 if (signal_num != SIGALRM)
1781 return signal (signal_num, signal_func);
1782 else
1784 #ifdef __MRC__
1785 __sigfun old_signal_func;
1786 #elif __MWERKS__
1787 __signal_func_ptr old_signal_func;
1788 #else
1789 You lose!!!
1790 #endif
1791 old_signal_func = alarm_signal_func;
1792 alarm_signal_func = signal_func;
1793 return old_signal_func;
1798 static pascal void
1799 mac_atimer_handler (qlink)
1800 TMTaskPtr qlink;
1802 if (alarm_signal_func)
1803 (alarm_signal_func) (SIGALRM);
1807 static void
1808 set_mac_atimer (count)
1809 long count;
1811 static TimerUPP mac_atimer_handlerUPP = NULL;
1813 if (mac_atimer_handlerUPP == NULL)
1814 mac_atimer_handlerUPP = NewTimerUPP (mac_atimer_handler);
1815 mac_atimer_task.tmCount = 0;
1816 mac_atimer_task.tmAddr = mac_atimer_handlerUPP;
1817 mac_atimer_qlink = (QElemPtr) &mac_atimer_task;
1818 InsTime (mac_atimer_qlink);
1819 if (count)
1820 PrimeTime (mac_atimer_qlink, count);
1825 remove_mac_atimer (remaining_count)
1826 long *remaining_count;
1828 if (mac_atimer_qlink)
1830 RmvTime (mac_atimer_qlink);
1831 if (remaining_count)
1832 *remaining_count = mac_atimer_task.tmCount;
1833 mac_atimer_qlink = NULL;
1835 return 0;
1837 else
1838 return -1;
1843 sigblock (int mask)
1845 int old_mask = signal_mask;
1847 signal_mask |= mask;
1849 if ((old_mask ^ signal_mask) & sigmask (SIGALRM))
1850 remove_mac_atimer (NULL);
1852 return old_mask;
1857 sigsetmask (int mask)
1859 int old_mask = signal_mask;
1861 signal_mask = mask;
1863 if ((old_mask ^ signal_mask) & sigmask (SIGALRM))
1864 if (signal_mask & sigmask (SIGALRM))
1865 remove_mac_atimer (NULL);
1866 else
1867 set_mac_atimer (mac_atimer_task.tmCount);
1869 return old_mask;
1874 alarm (int seconds)
1876 long remaining_count;
1878 if (remove_mac_atimer (&remaining_count) == 0)
1880 set_mac_atimer (seconds * 1000);
1882 return remaining_count / 1000;
1884 else
1886 mac_atimer_task.tmCount = seconds * 1000;
1888 return 0;
1894 setitimer (which, value, ovalue)
1895 int which;
1896 const struct itimerval *value;
1897 struct itimerval *ovalue;
1899 long remaining_count;
1900 long count = (EMACS_SECS (value->it_value) * 1000
1901 + (EMACS_USECS (value->it_value) + 999) / 1000);
1903 if (remove_mac_atimer (&remaining_count) == 0)
1905 if (ovalue)
1907 bzero (ovalue, sizeof (*ovalue));
1908 EMACS_SET_SECS_USECS (ovalue->it_value, remaining_count / 1000,
1909 (remaining_count % 1000) * 1000);
1911 set_mac_atimer (count);
1913 else
1914 mac_atimer_task.tmCount = count;
1916 return 0;
1920 /* gettimeofday should return the amount of time (in a timeval
1921 structure) since midnight today. The toolbox function Microseconds
1922 returns the number of microseconds (in a UnsignedWide value) since
1923 the machine was booted. Also making this complicated is WideAdd,
1924 WideSubtract, etc. take wide values. */
1927 gettimeofday (tp)
1928 struct timeval *tp;
1930 static inited = 0;
1931 static wide wall_clock_at_epoch, clicks_at_epoch;
1932 UnsignedWide uw_microseconds;
1933 wide w_microseconds;
1934 time_t sys_time (time_t *);
1936 /* If this function is called for the first time, record the number
1937 of seconds since midnight and the number of microseconds since
1938 boot at the time of this first call. */
1939 if (!inited)
1941 time_t systime;
1942 inited = 1;
1943 systime = sys_time (NULL);
1944 /* Store microseconds since midnight in wall_clock_at_epoch. */
1945 WideMultiply (systime, 1000000L, &wall_clock_at_epoch);
1946 Microseconds (&uw_microseconds);
1947 /* Store microseconds since boot in clicks_at_epoch. */
1948 clicks_at_epoch.hi = uw_microseconds.hi;
1949 clicks_at_epoch.lo = uw_microseconds.lo;
1952 /* Get time since boot */
1953 Microseconds (&uw_microseconds);
1955 /* Convert to time since midnight*/
1956 w_microseconds.hi = uw_microseconds.hi;
1957 w_microseconds.lo = uw_microseconds.lo;
1958 WideSubtract (&w_microseconds, &clicks_at_epoch);
1959 WideAdd (&w_microseconds, &wall_clock_at_epoch);
1960 tp->tv_sec = WideDivide (&w_microseconds, 1000000L, &tp->tv_usec);
1962 return 0;
1966 #ifdef __MRC__
1967 unsigned int
1968 sleep (unsigned int seconds)
1970 unsigned long time_up;
1971 EventRecord e;
1973 time_up = TickCount () + seconds * 60;
1974 while (TickCount () < time_up)
1976 /* Accept no event; just wait. by T.I. */
1977 WaitNextEvent (0, &e, 30, NULL);
1980 return (0);
1982 #endif /* __MRC__ */
1985 /* The time functions adjust time values according to the difference
1986 between the Unix and CW epoches. */
1988 #undef gmtime
1989 extern struct tm *gmtime (const time_t *);
1990 struct tm *
1991 sys_gmtime (const time_t *timer)
1993 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
1995 return gmtime (&unix_time);
1999 #undef localtime
2000 extern struct tm *localtime (const time_t *);
2001 struct tm *
2002 sys_localtime (const time_t *timer)
2004 #if __MSL__ >= 0x6000
2005 time_t unix_time = *timer;
2006 #else
2007 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
2008 #endif
2010 return localtime (&unix_time);
2014 #undef ctime
2015 extern char *ctime (const time_t *);
2016 char *
2017 sys_ctime (const time_t *timer)
2019 #if __MSL__ >= 0x6000
2020 time_t unix_time = *timer;
2021 #else
2022 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
2023 #endif
2025 return ctime (&unix_time);
2029 #undef time
2030 extern time_t time (time_t *);
2031 time_t
2032 sys_time (time_t *timer)
2034 #if __MSL__ >= 0x6000
2035 time_t mac_time = time (NULL);
2036 #else
2037 time_t mac_time = time (NULL) - CW_OR_MPW_UNIX_EPOCH_DIFF;
2038 #endif
2040 if (timer)
2041 *timer = mac_time;
2043 return mac_time;
2047 /* no subprocesses, empty wait */
2050 wait (int pid)
2052 return 0;
2056 void
2057 croak (char *badfunc)
2059 printf ("%s not yet implemented\r\n", badfunc);
2060 exit (1);
2064 char *
2065 mktemp (char *template)
2067 int len, k;
2068 static seqnum = 0;
2070 len = strlen (template);
2071 k = len - 1;
2072 while (k >= 0 && template[k] == 'X')
2073 k--;
2075 k++; /* make k index of first 'X' */
2077 if (k < len)
2079 /* Zero filled, number of digits equal to the number of X's. */
2080 sprintf (&template[k], "%0*d", len-k, seqnum++);
2082 return template;
2084 else
2085 return 0;
2089 /* Emulate getpwuid, getpwnam and others. */
2091 #define PASSWD_FIELD_SIZE 256
2093 static char my_passwd_name[PASSWD_FIELD_SIZE];
2094 static char my_passwd_dir[MAXPATHLEN+1];
2096 static struct passwd my_passwd =
2098 my_passwd_name,
2099 my_passwd_dir,
2102 static struct group my_group =
2104 /* There are no groups on the mac, so we just return "root" as the
2105 group name. */
2106 "root",
2110 /* Initialized by main () in macterm.c to pathname of emacs directory. */
2112 char emacs_passwd_dir[MAXPATHLEN+1];
2114 char *
2115 getwd (char *);
2117 void
2118 init_emacs_passwd_dir ()
2120 int found = false;
2122 if (getwd (emacs_passwd_dir) && getwd (my_passwd_dir))
2124 /* Need pathname of first ancestor that begins with "emacs"
2125 since Mac emacs application is somewhere in the emacs-*
2126 tree. */
2127 int len = strlen (emacs_passwd_dir);
2128 int j = len - 1;
2129 /* j points to the "/" following the directory name being
2130 compared. */
2131 int i = j - 1;
2132 while (i >= 0 && !found)
2134 while (i >= 0 && emacs_passwd_dir[i] != '/')
2135 i--;
2136 if (emacs_passwd_dir[i] == '/' && i+5 < len)
2137 found = (strncmp (&(emacs_passwd_dir[i+1]), "emacs", 5) == 0);
2138 if (found)
2139 emacs_passwd_dir[j+1] = '\0';
2140 else
2142 j = i;
2143 i = j - 1;
2148 if (!found)
2150 /* Setting to "/" probably won't work but set it to something
2151 anyway. */
2152 strcpy (emacs_passwd_dir, "/");
2153 strcpy (my_passwd_dir, "/");
2158 static struct passwd emacs_passwd =
2160 "emacs",
2161 emacs_passwd_dir,
2164 static int my_passwd_inited = 0;
2167 static void
2168 init_my_passwd ()
2170 char **owner_name;
2172 /* Note: my_passwd_dir initialized in int_emacs_passwd_dir to
2173 directory where Emacs was started. */
2175 owner_name = (char **) GetResource ('STR ',-16096);
2176 if (owner_name)
2178 HLock (owner_name);
2179 BlockMove ((unsigned char *) *owner_name,
2180 (unsigned char *) my_passwd_name,
2181 *owner_name[0]+1);
2182 HUnlock (owner_name);
2183 p2cstr ((unsigned char *) my_passwd_name);
2185 else
2186 my_passwd_name[0] = 0;
2190 struct passwd *
2191 getpwuid (uid_t uid)
2193 if (!my_passwd_inited)
2195 init_my_passwd ();
2196 my_passwd_inited = 1;
2199 return &my_passwd;
2203 struct group *
2204 getgrgid (gid_t gid)
2206 return &my_group;
2210 struct passwd *
2211 getpwnam (const char *name)
2213 if (strcmp (name, "emacs") == 0)
2214 return &emacs_passwd;
2216 if (!my_passwd_inited)
2218 init_my_passwd ();
2219 my_passwd_inited = 1;
2222 return &my_passwd;
2226 /* The functions fork, kill, sigsetmask, sigblock, request_sigio,
2227 setpgrp, setpriority, and unrequest_sigio are defined to be empty
2228 as in msdos.c. */
2232 fork ()
2234 return -1;
2239 kill (int x, int y)
2241 return -1;
2245 void
2246 sys_subshell ()
2248 error ("Can't spawn subshell");
2252 void
2253 request_sigio (void)
2258 void
2259 unrequest_sigio (void)
2265 setpgrp ()
2267 return 0;
2271 /* No pipes yet. */
2274 pipe (int _fildes[2])
2276 errno = EACCES;
2277 return -1;
2281 /* Hard and symbolic links. */
2284 symlink (const char *name1, const char *name2)
2286 errno = ENOENT;
2287 return -1;
2292 link (const char *name1, const char *name2)
2294 errno = ENOENT;
2295 return -1;
2298 #endif /* ! MAC_OSX */
2300 /* Determine the path name of the file specified by VREFNUM, DIRID,
2301 and NAME and place that in the buffer PATH of length
2302 MAXPATHLEN. */
2304 path_from_vol_dir_name (char *path, int man_path_len, short vol_ref_num,
2305 long dir_id, ConstStr255Param name)
2307 Str255 dir_name;
2308 CInfoPBRec cipb;
2309 OSErr err;
2311 if (strlen (name) > man_path_len)
2312 return 0;
2314 memcpy (dir_name, name, name[0]+1);
2315 memcpy (path, name, name[0]+1);
2316 p2cstr (path);
2318 cipb.dirInfo.ioDrParID = dir_id;
2319 cipb.dirInfo.ioNamePtr = dir_name;
2323 cipb.dirInfo.ioVRefNum = vol_ref_num;
2324 cipb.dirInfo.ioFDirIndex = -1;
2325 cipb.dirInfo.ioDrDirID = cipb.dirInfo.ioDrParID;
2326 /* go up to parent each time */
2328 err = PBGetCatInfo (&cipb, false);
2329 if (err != noErr)
2330 return 0;
2332 p2cstr (dir_name);
2333 if (strlen (dir_name) + strlen (path) + 1 >= man_path_len)
2334 return 0;
2336 strcat (dir_name, ":");
2337 strcat (dir_name, path);
2338 /* attach to front since we're going up directory tree */
2339 strcpy (path, dir_name);
2341 while (cipb.dirInfo.ioDrDirID != fsRtDirID);
2342 /* stop when we see the volume's root directory */
2344 return 1; /* success */
2348 OSErr
2349 posix_pathname_to_fsspec (ufn, fs)
2350 const char *ufn;
2351 FSSpec *fs;
2353 Str255 mac_pathname;
2355 if (posix_to_mac_pathname (ufn, mac_pathname, sizeof (mac_pathname)) == 0)
2356 return fnfErr;
2357 else
2359 c2pstr (mac_pathname);
2360 return FSMakeFSSpec (0, 0, mac_pathname, fs);
2364 OSErr
2365 fsspec_to_posix_pathname (fs, ufn, ufnbuflen)
2366 const FSSpec *fs;
2367 char *ufn;
2368 int ufnbuflen;
2370 char mac_pathname[MAXPATHLEN];
2372 if (path_from_vol_dir_name (mac_pathname, sizeof (mac_pathname) - 1,
2373 fs->vRefNum, fs->parID, fs->name)
2374 && mac_to_posix_pathname (mac_pathname, ufn, ufnbuflen))
2375 return noErr;
2376 else
2377 return fnfErr;
2380 #ifndef MAC_OSX
2383 readlink (const char *path, char *buf, int bufsiz)
2385 char mac_sym_link_name[MAXPATHLEN+1];
2386 OSErr err;
2387 FSSpec fsspec;
2388 Boolean target_is_folder, was_aliased;
2389 Str255 directory_name, mac_pathname;
2390 CInfoPBRec cipb;
2392 if (posix_to_mac_pathname (path, mac_sym_link_name, MAXPATHLEN+1) == 0)
2393 return -1;
2395 c2pstr (mac_sym_link_name);
2396 err = FSMakeFSSpec (0, 0, mac_sym_link_name, &fsspec);
2397 if (err != noErr)
2399 errno = ENOENT;
2400 return -1;
2403 err = ResolveAliasFile (&fsspec, true, &target_is_folder, &was_aliased);
2404 if (err != noErr || !was_aliased)
2406 errno = ENOENT;
2407 return -1;
2410 if (path_from_vol_dir_name (mac_pathname, 255, fsspec.vRefNum, fsspec.parID,
2411 fsspec.name) == 0)
2413 errno = ENOENT;
2414 return -1;
2417 if (mac_to_posix_pathname (mac_pathname, buf, bufsiz) == 0)
2419 errno = ENOENT;
2420 return -1;
2423 return strlen (buf);
2427 /* Convert a path to one with aliases fully expanded. */
2429 static int
2430 find_true_pathname (const char *path, char *buf, int bufsiz)
2432 char *q, temp[MAXPATHLEN+1];
2433 const char *p;
2434 int len;
2436 if (bufsiz <= 0 || path == 0 || path[0] == '\0')
2437 return -1;
2439 buf[0] = '\0';
2441 p = path;
2442 if (*p == '/')
2443 q = strchr (p + 1, '/');
2444 else
2445 q = strchr (p, '/');
2446 len = 0; /* loop may not be entered, e.g., for "/" */
2448 while (q)
2450 strcpy (temp, buf);
2451 strncat (temp, p, q - p);
2452 len = readlink (temp, buf, bufsiz);
2453 if (len <= -1)
2455 if (strlen (temp) + 1 > bufsiz)
2456 return -1;
2457 strcpy (buf, temp);
2459 strcat (buf, "/");
2460 len++;
2461 p = q + 1;
2462 q = strchr(p, '/');
2465 if (len + strlen (p) + 1 >= bufsiz)
2466 return -1;
2468 strcat (buf, p);
2469 return len + strlen (p);
2473 mode_t
2474 umask (mode_t numask)
2476 static mode_t mask = 022;
2477 mode_t oldmask = mask;
2478 mask = numask;
2479 return oldmask;
2484 chmod (const char *path, mode_t mode)
2486 /* say it always succeed for now */
2487 return 0;
2492 fchmod (int fd, mode_t mode)
2494 /* say it always succeed for now */
2495 return 0;
2500 fchown (int fd, uid_t owner, gid_t group)
2502 /* say it always succeed for now */
2503 return 0;
2508 dup (int oldd)
2510 #ifdef __MRC__
2511 return fcntl (oldd, F_DUPFD, 0);
2512 #elif __MWERKS__
2513 /* current implementation of fcntl in fcntl.mac.c simply returns old
2514 descriptor */
2515 return fcntl (oldd, F_DUPFD);
2516 #else
2517 You lose!!!
2518 #endif
2522 /* This is from the original sysdep.c. Emulate BSD dup2. First close
2523 newd if it already exists. Then, attempt to dup oldd. If not
2524 successful, call dup2 recursively until we are, then close the
2525 unsuccessful ones. */
2528 dup2 (int oldd, int newd)
2530 int fd, ret;
2532 close (newd);
2534 fd = dup (oldd);
2535 if (fd == -1)
2536 return -1;
2537 if (fd == newd)
2538 return newd;
2539 ret = dup2 (oldd, newd);
2540 close (fd);
2541 return ret;
2545 /* let it fail for now */
2547 char *
2548 sbrk (int incr)
2550 return (char *) -1;
2555 fsync (int fd)
2557 return 0;
2562 ioctl (int d, int request, void *argp)
2564 return -1;
2568 #ifdef __MRC__
2570 isatty (int fildes)
2572 if (fildes >=0 && fildes <= 2)
2573 return 1;
2574 else
2575 return 0;
2580 getgid ()
2582 return 100;
2587 getegid ()
2589 return 100;
2594 getuid ()
2596 return 200;
2601 geteuid ()
2603 return 200;
2605 #endif /* __MRC__ */
2608 #ifdef __MWERKS__
2609 #if __MSL__ < 0x6000
2610 #undef getpid
2612 getpid ()
2614 return 9999;
2616 #endif
2617 #endif /* __MWERKS__ */
2619 #endif /* ! MAC_OSX */
2622 /* Return the path to the directory in which Emacs can create
2623 temporary files. The MacOS "temporary items" directory cannot be
2624 used because it removes the file written by a process when it
2625 exits. In that sense it's more like "/dev/null" than "/tmp" (but
2626 again not exactly). And of course Emacs needs to read back the
2627 files written by its subprocesses. So here we write the files to a
2628 directory "Emacs" in the Preferences Folder. This directory is
2629 created if it does not exist. */
2631 char *
2632 get_temp_dir_name ()
2634 static char *temp_dir_name = NULL;
2635 short vol_ref_num;
2636 long dir_id;
2637 OSErr err;
2638 Str255 dir_name, full_path;
2639 CInfoPBRec cpb;
2640 char unix_dir_name[MAXPATHLEN+1];
2641 DIR *dir;
2643 /* Cache directory name with pointer temp_dir_name.
2644 Look for it only the first time. */
2645 if (!temp_dir_name)
2647 err = FindFolder (kOnSystemDisk, kPreferencesFolderType, kCreateFolder,
2648 &vol_ref_num, &dir_id);
2649 if (err != noErr)
2650 return NULL;
2652 if (!path_from_vol_dir_name (full_path, 255, vol_ref_num, dir_id, "\p"))
2653 return NULL;
2655 if (strlen (full_path) + 6 <= MAXPATHLEN)
2656 strcat (full_path, "Emacs:");
2657 else
2658 return NULL;
2660 if (!mac_to_posix_pathname (full_path, unix_dir_name, MAXPATHLEN+1))
2661 return NULL;
2663 dir = opendir (unix_dir_name); /* check whether temp directory exists */
2664 if (dir)
2665 closedir (dir);
2666 else if (mkdir (unix_dir_name, 0700) != 0) /* create it if not */
2667 return NULL;
2669 temp_dir_name = (char *) malloc (strlen (unix_dir_name) + 1);
2670 strcpy (temp_dir_name, unix_dir_name);
2673 return temp_dir_name;
2676 #ifndef MAC_OSX
2678 /* Allocate and construct an array of pointers to strings from a list
2679 of strings stored in a 'STR#' resource. The returned pointer array
2680 is stored in the style of argv and environ: if the 'STR#' resource
2681 contains numString strings, a pointer array with numString+1
2682 elements is returned in which the last entry contains a null
2683 pointer. The pointer to the pointer array is passed by pointer in
2684 parameter t. The resource ID of the 'STR#' resource is passed in
2685 parameter StringListID.
2688 void
2689 get_string_list (char ***t, short string_list_id)
2691 Handle h;
2692 Ptr p;
2693 int i, num_strings;
2695 h = GetResource ('STR#', string_list_id);
2696 if (h)
2698 HLock (h);
2699 p = *h;
2700 num_strings = * (short *) p;
2701 p += sizeof(short);
2702 *t = (char **) malloc (sizeof (char *) * (num_strings + 1));
2703 for (i = 0; i < num_strings; i++)
2705 short length = *p++;
2706 (*t)[i] = (char *) malloc (length + 1);
2707 strncpy ((*t)[i], p, length);
2708 (*t)[i][length] = '\0';
2709 p += length;
2711 (*t)[num_strings] = 0;
2712 HUnlock (h);
2714 else
2716 /* Return no string in case GetResource fails. Bug fixed by
2717 Ikegami Tsutomu. Caused MPW build to crash without sym -on
2718 option (no sym -on implies -opt local). */
2719 *t = (char **) malloc (sizeof (char *));
2720 (*t)[0] = 0;
2725 static char *
2726 get_path_to_system_folder ()
2728 short vol_ref_num;
2729 long dir_id;
2730 OSErr err;
2731 Str255 dir_name, full_path;
2732 CInfoPBRec cpb;
2733 static char system_folder_unix_name[MAXPATHLEN+1];
2734 DIR *dir;
2736 err = FindFolder (kOnSystemDisk, kSystemFolderType, kDontCreateFolder,
2737 &vol_ref_num, &dir_id);
2738 if (err != noErr)
2739 return NULL;
2741 if (!path_from_vol_dir_name (full_path, 255, vol_ref_num, dir_id, "\p"))
2742 return NULL;
2744 if (!mac_to_posix_pathname (full_path, system_folder_unix_name,
2745 MAXPATHLEN+1))
2746 return NULL;
2748 return system_folder_unix_name;
2752 char **environ;
2754 #define ENVIRON_STRING_LIST_ID 128
2756 /* Get environment variable definitions from STR# resource. */
2758 void
2759 init_environ ()
2761 int i;
2763 get_string_list (&environ, ENVIRON_STRING_LIST_ID);
2765 i = 0;
2766 while (environ[i])
2767 i++;
2769 /* Make HOME directory the one Emacs starts up in if not specified
2770 by resource. */
2771 if (getenv ("HOME") == NULL)
2773 environ = (char **) realloc (environ, sizeof (char *) * (i + 2));
2774 if (environ)
2776 environ[i] = (char *) malloc (strlen (my_passwd_dir) + 6);
2777 if (environ[i])
2779 strcpy (environ[i], "HOME=");
2780 strcat (environ[i], my_passwd_dir);
2782 environ[i+1] = 0;
2783 i++;
2787 /* Make HOME directory the one Emacs starts up in if not specified
2788 by resource. */
2789 if (getenv ("MAIL") == NULL)
2791 environ = (char **) realloc (environ, sizeof (char *) * (i + 2));
2792 if (environ)
2794 char * path_to_system_folder = get_path_to_system_folder ();
2795 environ[i] = (char *) malloc (strlen (path_to_system_folder) + 22);
2796 if (environ[i])
2798 strcpy (environ[i], "MAIL=");
2799 strcat (environ[i], path_to_system_folder);
2800 strcat (environ[i], "Eudora Folder/In");
2802 environ[i+1] = 0;
2808 /* Return the value of the environment variable NAME. */
2810 char *
2811 getenv (const char *name)
2813 int length = strlen(name);
2814 char **e;
2816 for (e = environ; *e != 0; e++)
2817 if (strncmp(*e, name, length) == 0 && (*e)[length] == '=')
2818 return &(*e)[length + 1];
2820 if (strcmp (name, "TMPDIR") == 0)
2821 return get_temp_dir_name ();
2823 return 0;
2827 #ifdef __MRC__
2828 /* see Interfaces&Libraries:Interfaces:CIncludes:signal.h */
2829 char *sys_siglist[] =
2831 "Zero is not a signal!!!",
2832 "Abort", /* 1 */
2833 "Interactive user interrupt", /* 2 */ "?",
2834 "Floating point exception", /* 4 */ "?", "?", "?",
2835 "Illegal instruction", /* 8 */ "?", "?", "?", "?", "?", "?", "?",
2836 "Segment violation", /* 16 */ "?", "?", "?", "?", "?", "?", "?",
2837 "?", "?", "?", "?", "?", "?", "?", "?",
2838 "Terminal" /* 32 */
2840 #elif __MWERKS__
2841 char *sys_siglist[] =
2843 "Zero is not a signal!!!",
2844 "Abort",
2845 "Floating point exception",
2846 "Illegal instruction",
2847 "Interactive user interrupt",
2848 "Segment violation",
2849 "Terminal"
2851 #else /* not __MRC__ and not __MWERKS__ */
2852 You lose!!!
2853 #endif /* not __MRC__ and not __MWERKS__ */
2856 #include <utsname.h>
2859 uname (struct utsname *name)
2861 char **system_name;
2862 system_name = GetString (-16413); /* IM - Resource Manager Reference */
2863 if (system_name)
2865 BlockMove (*system_name, name->nodename, (*system_name)[0]+1);
2866 p2cstr (name->nodename);
2867 return 0;
2869 else
2870 return -1;
2874 /* Event class of HLE sent to subprocess. */
2875 const OSType kEmacsSubprocessSend = 'ESND';
2877 /* Event class of HLE sent back from subprocess. */
2878 const OSType kEmacsSubprocessReply = 'ERPY';
2881 char *
2882 mystrchr (char *s, char c)
2884 while (*s && *s != c)
2886 if (*s == '\\')
2887 s++;
2888 s++;
2891 if (*s)
2893 *s = '\0';
2894 return s;
2896 else
2897 return NULL;
2901 char *
2902 mystrtok (char *s)
2904 while (*s)
2905 s++;
2907 return s + 1;
2911 void
2912 mystrcpy (char *to, char *from)
2914 while (*from)
2916 if (*from == '\\')
2917 from++;
2918 *to++ = *from++;
2920 *to = '\0';
2924 /* Start a Mac subprocess. Arguments for it is passed in argv (null
2925 terminated). The process should run with the default directory
2926 "workdir", read input from "infn", and write output and error to
2927 "outfn" and "errfn", resp. The Process Manager call
2928 LaunchApplication is used to start the subprocess. We use high
2929 level events as the mechanism to pass arguments to the subprocess
2930 and to make Emacs wait for the subprocess to terminate and pass
2931 back a result code. The bulk of the code here packs the arguments
2932 into one message to be passed together with the high level event.
2933 Emacs also sometimes starts a subprocess using a shell to perform
2934 wildcard filename expansion. Since we don't really have a shell on
2935 the Mac, this case is detected and the starting of the shell is
2936 by-passed. We really need to add code here to do filename
2937 expansion to support such functionality. */
2940 run_mac_command (argv, workdir, infn, outfn, errfn)
2941 unsigned char **argv;
2942 const char *workdir;
2943 const char *infn, *outfn, *errfn;
2945 #if TARGET_API_MAC_CARBON
2946 return -1;
2947 #else /* not TARGET_API_MAC_CARBON */
2948 char macappname[MAXPATHLEN+1], macworkdir[MAXPATHLEN+1];
2949 char macinfn[MAXPATHLEN+1], macoutfn[MAXPATHLEN+1], macerrfn[MAXPATHLEN+1];
2950 int paramlen, argc, newargc, j, retries;
2951 char **newargv, *param, *p;
2952 OSErr iErr;
2953 FSSpec spec;
2954 LaunchParamBlockRec lpbr;
2955 EventRecord send_event, reply_event;
2956 RgnHandle cursor_region_handle;
2957 TargetID targ;
2958 unsigned long ref_con, len;
2960 if (posix_to_mac_pathname (workdir, macworkdir, MAXPATHLEN+1) == 0)
2961 return -1;
2962 if (posix_to_mac_pathname (infn, macinfn, MAXPATHLEN+1) == 0)
2963 return -1;
2964 if (posix_to_mac_pathname (outfn, macoutfn, MAXPATHLEN+1) == 0)
2965 return -1;
2966 if (posix_to_mac_pathname (errfn, macerrfn, MAXPATHLEN+1) == 0)
2967 return -1;
2969 paramlen = strlen (macworkdir) + strlen (macinfn) + strlen (macoutfn)
2970 + strlen (macerrfn) + 4; /* count nulls at end of strings */
2972 argc = 0;
2973 while (argv[argc])
2974 argc++;
2976 if (argc == 0)
2977 return -1;
2979 /* If a subprocess is invoked with a shell, we receive 3 arguments
2980 of the form: "<path to emacs bins>/sh" "-c" "<path to emacs
2981 bins>/<command> <command args>" */
2982 j = strlen (argv[0]);
2983 if (j >= 3 && strcmp (argv[0]+j-3, "/sh") == 0
2984 && argc == 3 && strcmp (argv[1], "-c") == 0)
2986 char *command, *t, tempmacpathname[MAXPATHLEN+1];
2988 /* The arguments for the command in argv[2] are separated by
2989 spaces. Count them and put the count in newargc. */
2990 command = (char *) alloca (strlen (argv[2])+2);
2991 strcpy (command, argv[2]);
2992 if (command[strlen (command) - 1] != ' ')
2993 strcat (command, " ");
2995 t = command;
2996 newargc = 0;
2997 t = mystrchr (t, ' ');
2998 while (t)
3000 newargc++;
3001 t = mystrchr (t+1, ' ');
3004 newargv = (char **) alloca (sizeof (char *) * newargc);
3006 t = command;
3007 for (j = 0; j < newargc; j++)
3009 newargv[j] = (char *) alloca (strlen (t) + 1);
3010 mystrcpy (newargv[j], t);
3012 t = mystrtok (t);
3013 paramlen += strlen (newargv[j]) + 1;
3016 if (strncmp (newargv[0], "~emacs/", 7) == 0)
3018 if (posix_to_mac_pathname (newargv[0], tempmacpathname, MAXPATHLEN+1)
3019 == 0)
3020 return -1;
3022 else
3023 { /* sometimes Emacs call "sh" without a path for the command */
3024 #if 0
3025 char *t = (char *) alloca (strlen (newargv[0]) + 7 + 1);
3026 strcpy (t, "~emacs/");
3027 strcat (t, newargv[0]);
3028 #endif /* 0 */
3029 Lisp_Object path;
3030 openp (Vexec_path, build_string (newargv[0]), Vexec_suffixes, &path,
3031 make_number (X_OK));
3033 if (NILP (path))
3034 return -1;
3035 if (posix_to_mac_pathname (SDATA (path), tempmacpathname,
3036 MAXPATHLEN+1) == 0)
3037 return -1;
3039 strcpy (macappname, tempmacpathname);
3041 else
3043 if (posix_to_mac_pathname (argv[0], macappname, MAXPATHLEN+1) == 0)
3044 return -1;
3046 newargv = (char **) alloca (sizeof (char *) * argc);
3047 newargc = argc;
3048 for (j = 1; j < argc; j++)
3050 if (strncmp (argv[j], "~emacs/", 7) == 0)
3052 char *t = strchr (argv[j], ' ');
3053 if (t)
3055 char tempcmdname[MAXPATHLEN+1], tempmaccmdname[MAXPATHLEN+1];
3056 strncpy (tempcmdname, argv[j], t-argv[j]);
3057 tempcmdname[t-argv[j]] = '\0';
3058 if (posix_to_mac_pathname (tempcmdname, tempmaccmdname,
3059 MAXPATHLEN+1) == 0)
3060 return -1;
3061 newargv[j] = (char *) alloca (strlen (tempmaccmdname)
3062 + strlen (t) + 1);
3063 strcpy (newargv[j], tempmaccmdname);
3064 strcat (newargv[j], t);
3066 else
3068 char tempmaccmdname[MAXPATHLEN+1];
3069 if (posix_to_mac_pathname (argv[j], tempmaccmdname,
3070 MAXPATHLEN+1) == 0)
3071 return -1;
3072 newargv[j] = (char *) alloca (strlen (tempmaccmdname)+1);
3073 strcpy (newargv[j], tempmaccmdname);
3076 else
3077 newargv[j] = argv[j];
3078 paramlen += strlen (newargv[j]) + 1;
3082 /* After expanding all the arguments, we now know the length of the
3083 parameter block to be sent to the subprocess as a message
3084 attached to the HLE. */
3085 param = (char *) malloc (paramlen + 1);
3086 if (!param)
3087 return -1;
3089 p = param;
3090 *p++ = newargc;
3091 /* first byte of message contains number of arguments for command */
3092 strcpy (p, macworkdir);
3093 p += strlen (macworkdir);
3094 *p++ = '\0';
3095 /* null terminate strings sent so it's possible to use strcpy over there */
3096 strcpy (p, macinfn);
3097 p += strlen (macinfn);
3098 *p++ = '\0';
3099 strcpy (p, macoutfn);
3100 p += strlen (macoutfn);
3101 *p++ = '\0';
3102 strcpy (p, macerrfn);
3103 p += strlen (macerrfn);
3104 *p++ = '\0';
3105 for (j = 1; j < newargc; j++)
3107 strcpy (p, newargv[j]);
3108 p += strlen (newargv[j]);
3109 *p++ = '\0';
3112 c2pstr (macappname);
3114 iErr = FSMakeFSSpec (0, 0, macappname, &spec);
3116 if (iErr != noErr)
3118 free (param);
3119 return -1;
3122 lpbr.launchBlockID = extendedBlock;
3123 lpbr.launchEPBLength = extendedBlockLen;
3124 lpbr.launchControlFlags = launchContinue + launchNoFileFlags;
3125 lpbr.launchAppSpec = &spec;
3126 lpbr.launchAppParameters = NULL;
3128 iErr = LaunchApplication (&lpbr); /* call the subprocess */
3129 if (iErr != noErr)
3131 free (param);
3132 return -1;
3135 send_event.what = kHighLevelEvent;
3136 send_event.message = kEmacsSubprocessSend;
3137 /* Event ID stored in "where" unused */
3139 retries = 3;
3140 /* OS may think current subprocess has terminated if previous one
3141 terminated recently. */
3144 iErr = PostHighLevelEvent (&send_event, &lpbr.launchProcessSN, 0, param,
3145 paramlen + 1, receiverIDisPSN);
3147 while (iErr == sessClosedErr && retries-- > 0);
3149 if (iErr != noErr)
3151 free (param);
3152 return -1;
3155 cursor_region_handle = NewRgn ();
3157 /* Wait for the subprocess to finish, when it will send us a ERPY
3158 high level event. */
3159 while (1)
3160 if (WaitNextEvent (highLevelEventMask, &reply_event, 180,
3161 cursor_region_handle)
3162 && reply_event.message == kEmacsSubprocessReply)
3163 break;
3165 /* The return code is sent through the refCon */
3166 iErr = AcceptHighLevelEvent (&targ, &ref_con, NULL, &len);
3167 if (iErr != noErr)
3169 DisposeHandle ((Handle) cursor_region_handle);
3170 free (param);
3171 return -1;
3174 DisposeHandle ((Handle) cursor_region_handle);
3175 free (param);
3177 return ref_con;
3178 #endif /* not TARGET_API_MAC_CARBON */
3182 DIR *
3183 opendir (const char *dirname)
3185 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
3186 char mac_pathname[MAXPATHLEN+1], vol_name[MAXPATHLEN+1];
3187 DIR *dirp;
3188 CInfoPBRec cipb;
3189 HVolumeParam vpb;
3190 int len, vol_name_len;
3192 if (find_true_pathname (dirname, true_pathname, MAXPATHLEN+1) == -1)
3193 return 0;
3195 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
3196 if (len > -1)
3197 fully_resolved_name[len] = '\0';
3198 else
3199 strcpy (fully_resolved_name, true_pathname);
3201 dirp = (DIR *) malloc (sizeof(DIR));
3202 if (!dirp)
3203 return 0;
3205 /* Handle special case when dirname is "/": sets up for readir to
3206 get all mount volumes. */
3207 if (strcmp (fully_resolved_name, "/") == 0)
3209 dirp->getting_volumes = 1; /* special all mounted volumes DIR struct */
3210 dirp->current_index = 1; /* index for first volume */
3211 return dirp;
3214 /* Handle typical cases: not accessing all mounted volumes. */
3215 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
3216 return 0;
3218 /* Emacs calls opendir without the trailing '/', Mac needs trailing ':' */
3219 len = strlen (mac_pathname);
3220 if (mac_pathname[len - 1] != ':' && len < MAXPATHLEN)
3221 strcat (mac_pathname, ":");
3223 /* Extract volume name */
3224 vol_name_len = strchr (mac_pathname, ':') - mac_pathname;
3225 strncpy (vol_name, mac_pathname, vol_name_len);
3226 vol_name[vol_name_len] = '\0';
3227 strcat (vol_name, ":");
3229 c2pstr (mac_pathname);
3230 cipb.hFileInfo.ioNamePtr = mac_pathname;
3231 /* using full pathname so vRefNum and DirID ignored */
3232 cipb.hFileInfo.ioVRefNum = 0;
3233 cipb.hFileInfo.ioDirID = 0;
3234 cipb.hFileInfo.ioFDirIndex = 0;
3235 /* set to 0 to get information about specific dir or file */
3237 errno = PBGetCatInfo (&cipb, false);
3238 if (errno != noErr)
3240 errno = ENOENT;
3241 return 0;
3244 if (!(cipb.hFileInfo.ioFlAttrib & 0x10)) /* bit 4 = 1 for directories */
3245 return 0; /* not a directory */
3247 dirp->dir_id = cipb.dirInfo.ioDrDirID; /* used later in readdir */
3248 dirp->getting_volumes = 0;
3249 dirp->current_index = 1; /* index for first file/directory */
3251 c2pstr (vol_name);
3252 vpb.ioNamePtr = vol_name;
3253 /* using full pathname so vRefNum and DirID ignored */
3254 vpb.ioVRefNum = 0;
3255 vpb.ioVolIndex = -1;
3256 errno = PBHGetVInfo ((union HParamBlockRec *) &vpb, false);
3257 if (errno != noErr)
3259 errno = ENOENT;
3260 return 0;
3263 dirp->vol_ref_num = vpb.ioVRefNum;
3265 return dirp;
3269 closedir (DIR *dp)
3271 free (dp);
3273 return 0;
3277 struct dirent *
3278 readdir (DIR *dp)
3280 HParamBlockRec hpblock;
3281 CInfoPBRec cipb;
3282 static struct dirent s_dirent;
3283 static Str255 s_name;
3284 int done;
3285 char *p;
3287 /* Handle the root directory containing the mounted volumes. Call
3288 PBHGetVInfo specifying an index to obtain the info for a volume.
3289 PBHGetVInfo returns an error when it receives an index beyond the
3290 last volume, at which time we should return a nil dirent struct
3291 pointer. */
3292 if (dp->getting_volumes)
3294 hpblock.volumeParam.ioNamePtr = s_name;
3295 hpblock.volumeParam.ioVRefNum = 0;
3296 hpblock.volumeParam.ioVolIndex = dp->current_index;
3298 errno = PBHGetVInfo (&hpblock, false);
3299 if (errno != noErr)
3301 errno = ENOENT;
3302 return 0;
3305 p2cstr (s_name);
3306 strcat (s_name, "/"); /* need "/" for stat to work correctly */
3308 dp->current_index++;
3310 s_dirent.d_ino = hpblock.volumeParam.ioVRefNum;
3311 s_dirent.d_name = s_name;
3313 return &s_dirent;
3315 else
3317 cipb.hFileInfo.ioVRefNum = dp->vol_ref_num;
3318 cipb.hFileInfo.ioNamePtr = s_name;
3319 /* location to receive filename returned */
3321 /* return only visible files */
3322 done = false;
3323 while (!done)
3325 cipb.hFileInfo.ioDirID = dp->dir_id;
3326 /* directory ID found by opendir */
3327 cipb.hFileInfo.ioFDirIndex = dp->current_index;
3329 errno = PBGetCatInfo (&cipb, false);
3330 if (errno != noErr)
3332 errno = ENOENT;
3333 return 0;
3336 /* insist on a visible entry */
3337 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* directory? */
3338 done = !(cipb.dirInfo.ioDrUsrWds.frFlags & fInvisible);
3339 else
3340 done = !(cipb.hFileInfo.ioFlFndrInfo.fdFlags & fInvisible);
3342 dp->current_index++;
3345 p2cstr (s_name);
3347 p = s_name;
3348 while (*p)
3350 if (*p == '/')
3351 *p = ':';
3352 p++;
3355 s_dirent.d_ino = cipb.dirInfo.ioDrDirID;
3356 /* value unimportant: non-zero for valid file */
3357 s_dirent.d_name = s_name;
3359 return &s_dirent;
3364 char *
3365 getwd (char *path)
3367 char mac_pathname[MAXPATHLEN+1];
3368 Str255 directory_name;
3369 OSErr errno;
3370 CInfoPBRec cipb;
3372 if (path_from_vol_dir_name (mac_pathname, 255, 0, 0, "\p") == 0)
3373 return NULL;
3375 if (mac_to_posix_pathname (mac_pathname, path, MAXPATHLEN+1) == 0)
3376 return 0;
3377 else
3378 return path;
3381 #endif /* ! MAC_OSX */
3384 void
3385 initialize_applescript ()
3387 AEDesc null_desc;
3388 OSAError osaerror;
3390 /* if open fails, as_scripting_component is set to NULL. Its
3391 subsequent use in OSA calls will fail with badComponentInstance
3392 error. */
3393 as_scripting_component = OpenDefaultComponent (kOSAComponentType,
3394 kAppleScriptSubtype);
3396 null_desc.descriptorType = typeNull;
3397 null_desc.dataHandle = 0;
3398 osaerror = OSAMakeContext (as_scripting_component, &null_desc,
3399 kOSANullScript, &as_script_context);
3400 if (osaerror)
3401 as_script_context = kOSANullScript;
3402 /* use default context if create fails */
3406 void
3407 terminate_applescript()
3409 OSADispose (as_scripting_component, as_script_context);
3410 CloseComponent (as_scripting_component);
3413 /* Convert a lisp string to the 4 byte character code. */
3415 OSType
3416 mac_get_code_from_arg(Lisp_Object arg, OSType defCode)
3418 OSType result;
3419 if (NILP(arg))
3421 result = defCode;
3423 else
3425 /* check type string */
3426 CHECK_STRING(arg);
3427 if (SBYTES (arg) != 4)
3429 error ("Wrong argument: need string of length 4 for code");
3431 result = EndianU32_BtoN (*((UInt32 *) SDATA (arg)));
3433 return result;
3436 /* Convert the 4 byte character code into a 4 byte string. */
3438 Lisp_Object
3439 mac_get_object_from_code(OSType defCode)
3441 UInt32 code = EndianU32_NtoB (defCode);
3443 return make_unibyte_string ((char *)&code, 4);
3447 DEFUN ("mac-get-file-creator", Fmac_get_file_creator, Smac_get_file_creator, 1, 1, 0,
3448 doc: /* Get the creator code of FILENAME as a four character string. */)
3449 (filename)
3450 Lisp_Object filename;
3452 OSErr status;
3453 #ifdef MAC_OSX
3454 FSRef fref;
3455 #else
3456 FSSpec fss;
3457 #endif
3458 OSType cCode;
3459 Lisp_Object result = Qnil;
3460 CHECK_STRING (filename);
3462 if (NILP(Ffile_exists_p(filename)) || !NILP(Ffile_directory_p(filename))) {
3463 return Qnil;
3465 filename = Fexpand_file_name (filename, Qnil);
3467 BLOCK_INPUT;
3468 #ifdef MAC_OSX
3469 status = FSPathMakeRef(SDATA(ENCODE_FILE(filename)), &fref, NULL);
3470 #else
3471 status = posix_pathname_to_fsspec (SDATA (ENCODE_FILE (filename)), &fss);
3472 #endif
3474 if (status == noErr)
3476 #ifdef MAC_OSX
3477 FSCatalogInfo catalogInfo;
3479 status = FSGetCatalogInfo(&fref, kFSCatInfoFinderInfo,
3480 &catalogInfo, NULL, NULL, NULL);
3481 #else
3482 FInfo finder_info;
3484 status = FSpGetFInfo (&fss, &finder_info);
3485 #endif
3486 if (status == noErr)
3488 #ifdef MAC_OSX
3489 result = mac_get_object_from_code(((FileInfo*)&catalogInfo.finderInfo)->fileCreator);
3490 #else
3491 result = mac_get_object_from_code (finder_info.fdCreator);
3492 #endif
3495 UNBLOCK_INPUT;
3496 if (status != noErr) {
3497 error ("Error while getting file information.");
3499 return result;
3502 DEFUN ("mac-get-file-type", Fmac_get_file_type, Smac_get_file_type, 1, 1, 0,
3503 doc: /* Get the type code of FILENAME as a four character string. */)
3504 (filename)
3505 Lisp_Object filename;
3507 OSErr status;
3508 #ifdef MAC_OSX
3509 FSRef fref;
3510 #else
3511 FSSpec fss;
3512 #endif
3513 OSType cCode;
3514 Lisp_Object result = Qnil;
3515 CHECK_STRING (filename);
3517 if (NILP(Ffile_exists_p(filename)) || !NILP(Ffile_directory_p(filename))) {
3518 return Qnil;
3520 filename = Fexpand_file_name (filename, Qnil);
3522 BLOCK_INPUT;
3523 #ifdef MAC_OSX
3524 status = FSPathMakeRef(SDATA(ENCODE_FILE(filename)), &fref, NULL);
3525 #else
3526 status = posix_pathname_to_fsspec (SDATA (ENCODE_FILE (filename)), &fss);
3527 #endif
3529 if (status == noErr)
3531 #ifdef MAC_OSX
3532 FSCatalogInfo catalogInfo;
3534 status = FSGetCatalogInfo(&fref, kFSCatInfoFinderInfo,
3535 &catalogInfo, NULL, NULL, NULL);
3536 #else
3537 FInfo finder_info;
3539 status = FSpGetFInfo (&fss, &finder_info);
3540 #endif
3541 if (status == noErr)
3543 #ifdef MAC_OSX
3544 result = mac_get_object_from_code(((FileInfo*)&catalogInfo.finderInfo)->fileType);
3545 #else
3546 result = mac_get_object_from_code (finder_info.fdType);
3547 #endif
3550 UNBLOCK_INPUT;
3551 if (status != noErr) {
3552 error ("Error while getting file information.");
3554 return result;
3557 DEFUN ("mac-set-file-creator", Fmac_set_file_creator, Smac_set_file_creator, 1, 2, 0,
3558 doc: /* Set creator code of file FILENAME to CODE.
3559 If non-nil, CODE must be a 4-character string. Otherwise, 'EMAx' is
3560 assumed. Return non-nil if successful. */)
3561 (filename, code)
3562 Lisp_Object filename, code;
3564 OSErr status;
3565 #ifdef MAC_OSX
3566 FSRef fref;
3567 #else
3568 FSSpec fss;
3569 #endif
3570 OSType cCode;
3571 CHECK_STRING (filename);
3573 cCode = mac_get_code_from_arg(code, 'EMAx');
3575 if (NILP(Ffile_exists_p(filename)) || !NILP(Ffile_directory_p(filename))) {
3576 return Qnil;
3578 filename = Fexpand_file_name (filename, Qnil);
3580 BLOCK_INPUT;
3581 #ifdef MAC_OSX
3582 status = FSPathMakeRef(SDATA(ENCODE_FILE(filename)), &fref, NULL);
3583 #else
3584 status = posix_pathname_to_fsspec (SDATA (ENCODE_FILE (filename)), &fss);
3585 #endif
3587 if (status == noErr)
3589 #ifdef MAC_OSX
3590 FSCatalogInfo catalogInfo;
3591 FSRef parentDir;
3592 status = FSGetCatalogInfo(&fref, kFSCatInfoFinderInfo,
3593 &catalogInfo, NULL, NULL, &parentDir);
3594 #else
3595 FInfo finder_info;
3597 status = FSpGetFInfo (&fss, &finder_info);
3598 #endif
3599 if (status == noErr)
3601 #ifdef MAC_OSX
3602 ((FileInfo*)&catalogInfo.finderInfo)->fileCreator = cCode;
3603 status = FSSetCatalogInfo(&fref, kFSCatInfoFinderInfo, &catalogInfo);
3604 /* TODO: on Mac OS 10.2, we need to touch the parent dir, FNNotify? */
3605 #else
3606 finder_info.fdCreator = cCode;
3607 status = FSpSetFInfo (&fss, &finder_info);
3608 #endif
3611 UNBLOCK_INPUT;
3612 if (status != noErr) {
3613 error ("Error while setting creator information.");
3615 return Qt;
3618 DEFUN ("mac-set-file-type", Fmac_set_file_type, Smac_set_file_type, 2, 2, 0,
3619 doc: /* Set file code of file FILENAME to CODE.
3620 CODE must be a 4-character string. Return non-nil if successful. */)
3621 (filename, code)
3622 Lisp_Object filename, code;
3624 OSErr status;
3625 #ifdef MAC_OSX
3626 FSRef fref;
3627 #else
3628 FSSpec fss;
3629 #endif
3630 OSType cCode;
3631 CHECK_STRING (filename);
3633 cCode = mac_get_code_from_arg(code, 0); /* Default to empty code*/
3635 if (NILP(Ffile_exists_p(filename)) || !NILP(Ffile_directory_p(filename))) {
3636 return Qnil;
3638 filename = Fexpand_file_name (filename, Qnil);
3640 BLOCK_INPUT;
3641 #ifdef MAC_OSX
3642 status = FSPathMakeRef(SDATA(ENCODE_FILE(filename)), &fref, NULL);
3643 #else
3644 status = posix_pathname_to_fsspec (SDATA (ENCODE_FILE (filename)), &fss);
3645 #endif
3647 if (status == noErr)
3649 #ifdef MAC_OSX
3650 FSCatalogInfo catalogInfo;
3651 FSRef parentDir;
3652 status = FSGetCatalogInfo(&fref, kFSCatInfoFinderInfo,
3653 &catalogInfo, NULL, NULL, &parentDir);
3654 #else
3655 FInfo finder_info;
3657 status = FSpGetFInfo (&fss, &finder_info);
3658 #endif
3659 if (status == noErr)
3661 #ifdef MAC_OSX
3662 ((FileInfo*)&catalogInfo.finderInfo)->fileType = cCode;
3663 status = FSSetCatalogInfo(&fref, kFSCatInfoFinderInfo, &catalogInfo);
3664 /* TODO: on Mac OS 10.2, we need to touch the parent dir, FNNotify? */
3665 #else
3666 finder_info.fdType = cCode;
3667 status = FSpSetFInfo (&fss, &finder_info);
3668 #endif
3671 UNBLOCK_INPUT;
3672 if (status != noErr) {
3673 error ("Error while setting creator information.");
3675 return Qt;
3679 /* Compile and execute the AppleScript SCRIPT and return the error
3680 status as function value. A zero is returned if compilation and
3681 execution is successful, in which case RESULT returns a pointer to
3682 a string containing the resulting script value. Otherwise, the Mac
3683 error code is returned and RESULT returns a pointer to an error
3684 string. In both cases the caller should deallocate the storage
3685 used by the string pointed to by RESULT if it is non-NULL. For
3686 documentation on the MacOS scripting architecture, see Inside
3687 Macintosh - Interapplication Communications: Scripting Components. */
3689 static long
3690 do_applescript (char *script, char **result)
3692 AEDesc script_desc, result_desc, error_desc;
3693 OSErr error;
3694 OSAError osaerror;
3695 long length;
3697 *result = 0;
3699 if (!as_scripting_component)
3700 initialize_applescript();
3702 error = AECreateDesc (typeChar, script, strlen(script), &script_desc);
3703 if (error)
3704 return error;
3706 osaerror = OSADoScript (as_scripting_component, &script_desc, kOSANullScript,
3707 typeChar, kOSAModeNull, &result_desc);
3709 if (osaerror == errOSAScriptError)
3711 /* error executing AppleScript: retrieve error message */
3712 if (!OSAScriptError (as_scripting_component, kOSAErrorMessage, typeChar,
3713 &error_desc))
3715 #if TARGET_API_MAC_CARBON
3716 length = AEGetDescDataSize (&error_desc);
3717 *result = (char *) xmalloc (length + 1);
3718 if (*result)
3720 AEGetDescData (&error_desc, *result, length);
3721 *(*result + length) = '\0';
3723 #else /* not TARGET_API_MAC_CARBON */
3724 HLock (error_desc.dataHandle);
3725 length = GetHandleSize(error_desc.dataHandle);
3726 *result = (char *) xmalloc (length + 1);
3727 if (*result)
3729 memcpy (*result, *(error_desc.dataHandle), length);
3730 *(*result + length) = '\0';
3732 HUnlock (error_desc.dataHandle);
3733 #endif /* not TARGET_API_MAC_CARBON */
3734 AEDisposeDesc (&error_desc);
3737 else if (osaerror == noErr) /* success: retrieve resulting script value */
3739 #if TARGET_API_MAC_CARBON
3740 length = AEGetDescDataSize (&result_desc);
3741 *result = (char *) xmalloc (length + 1);
3742 if (*result)
3744 AEGetDescData (&result_desc, *result, length);
3745 *(*result + length) = '\0';
3747 #else /* not TARGET_API_MAC_CARBON */
3748 HLock (result_desc.dataHandle);
3749 length = GetHandleSize(result_desc.dataHandle);
3750 *result = (char *) xmalloc (length + 1);
3751 if (*result)
3753 memcpy (*result, *(result_desc.dataHandle), length);
3754 *(*result + length) = '\0';
3756 HUnlock (result_desc.dataHandle);
3757 #endif /* not TARGET_API_MAC_CARBON */
3758 AEDisposeDesc (&result_desc);
3761 AEDisposeDesc (&script_desc);
3763 return osaerror;
3767 DEFUN ("do-applescript", Fdo_applescript, Sdo_applescript, 1, 1, 0,
3768 doc: /* Compile and execute AppleScript SCRIPT and return the result.
3769 If compilation and execution are successful, the resulting script
3770 value is returned as a string. Otherwise the function aborts and
3771 displays the error message returned by the AppleScript scripting
3772 component. */)
3773 (script)
3774 Lisp_Object script;
3776 char *result, *temp;
3777 Lisp_Object lisp_result;
3778 long status;
3780 CHECK_STRING (script);
3782 BLOCK_INPUT;
3783 status = do_applescript (SDATA (script), &result);
3784 UNBLOCK_INPUT;
3785 if (status)
3787 if (!result)
3788 error ("AppleScript error %d", status);
3789 else
3791 /* Unfortunately only OSADoScript in do_applescript knows how
3792 how large the resulting script value or error message is
3793 going to be and therefore as caller memory must be
3794 deallocated here. It is necessary to free the error
3795 message before calling error to avoid a memory leak. */
3796 temp = (char *) alloca (strlen (result) + 1);
3797 strcpy (temp, result);
3798 xfree (result);
3799 error (temp);
3802 else
3804 lisp_result = build_string (result);
3805 xfree (result);
3806 return lisp_result;
3811 DEFUN ("mac-file-name-to-posix", Fmac_file_name_to_posix,
3812 Smac_file_name_to_posix, 1, 1, 0,
3813 doc: /* Convert Macintosh FILENAME to Posix form. */)
3814 (filename)
3815 Lisp_Object filename;
3817 char posix_filename[MAXPATHLEN+1];
3819 CHECK_STRING (filename);
3821 if (mac_to_posix_pathname (SDATA (filename), posix_filename, MAXPATHLEN))
3822 return build_string (posix_filename);
3823 else
3824 return Qnil;
3828 DEFUN ("posix-file-name-to-mac", Fposix_file_name_to_mac,
3829 Sposix_file_name_to_mac, 1, 1, 0,
3830 doc: /* Convert Posix FILENAME to Mac form. */)
3831 (filename)
3832 Lisp_Object filename;
3834 char mac_filename[MAXPATHLEN+1];
3836 CHECK_STRING (filename);
3838 if (posix_to_mac_pathname (SDATA (filename), mac_filename, MAXPATHLEN))
3839 return build_string (mac_filename);
3840 else
3841 return Qnil;
3845 #if TARGET_API_MAC_CARBON
3846 static Lisp_Object Qxml, Qmime_charset;
3847 static Lisp_Object QNFD, QNFKD, QNFC, QNFKC, QHFS_plus_D, QHFS_plus_C;
3849 DEFUN ("mac-get-preference", Fmac_get_preference, Smac_get_preference, 1, 4, 0,
3850 doc: /* Return the application preference value for KEY.
3851 KEY is either a string specifying a preference key, or a list of key
3852 strings. If it is a list, the (i+1)-th element is used as a key for
3853 the CFDictionary value obtained by the i-th element. Return nil if
3854 lookup is failed at some stage.
3856 Optional arg APPLICATION is an application ID string. If omitted or
3857 nil, that stands for the current application.
3859 Optional arg FORMAT specifies the data format of the return value. If
3860 omitted or nil, each Core Foundation object is converted into a
3861 corresponding Lisp object as follows:
3863 Core Foundation Lisp Tag
3864 ------------------------------------------------------------
3865 CFString Multibyte string string
3866 CFNumber Integer or float number
3867 CFBoolean Symbol (t or nil) boolean
3868 CFDate List of three integers date
3869 (cf. `current-time')
3870 CFData Unibyte string data
3871 CFArray Vector array
3872 CFDictionary Alist or hash table dictionary
3873 (depending on HASH-BOUND)
3875 If it is t, a symbol that represents the type of the original Core
3876 Foundation object is prepended. If it is `xml', the value is returned
3877 as an XML representation.
3879 Optional arg HASH-BOUND specifies which kinds of the list objects,
3880 alists or hash tables, are used as the targets of the conversion from
3881 CFDictionary. If HASH-BOUND is a negative integer or nil, always
3882 generate alists. If HASH-BOUND >= 0, generate an alist if the number
3883 of keys in the dictionary is smaller than HASH-BOUND, and a hash table
3884 otherwise. */)
3885 (key, application, format, hash_bound)
3886 Lisp_Object key, application, format, hash_bound;
3888 CFStringRef app_id, key_str;
3889 CFPropertyListRef app_plist = NULL, plist;
3890 Lisp_Object result = Qnil, tmp;
3892 if (STRINGP (key))
3893 key = Fcons (key, Qnil);
3894 else
3896 CHECK_CONS (key);
3897 for (tmp = key; CONSP (tmp); tmp = XCDR (tmp))
3898 CHECK_STRING_CAR (tmp);
3899 if (!NILP (tmp))
3900 wrong_type_argument (Qlistp, key);
3902 if (!NILP (application))
3903 CHECK_STRING (application);
3904 CHECK_SYMBOL (format);
3905 if (!NILP (hash_bound))
3906 CHECK_NUMBER (hash_bound);
3908 BLOCK_INPUT;
3910 app_id = kCFPreferencesCurrentApplication;
3911 if (!NILP (application))
3913 app_id = cfstring_create_with_string (application);
3914 if (app_id == NULL)
3915 goto out;
3917 key_str = cfstring_create_with_string (XCAR (key));
3918 if (key_str == NULL)
3919 goto out;
3920 app_plist = CFPreferencesCopyAppValue (key_str, app_id);
3921 CFRelease (key_str);
3922 if (app_plist == NULL)
3923 goto out;
3925 plist = app_plist;
3926 for (key = XCDR (key); CONSP (key); key = XCDR (key))
3928 if (CFGetTypeID (plist) != CFDictionaryGetTypeID ())
3929 break;
3930 key_str = cfstring_create_with_string (XCAR (key));
3931 if (key_str == NULL)
3932 goto out;
3933 plist = CFDictionaryGetValue (plist, key_str);
3934 CFRelease (key_str);
3935 if (plist == NULL)
3936 goto out;
3939 if (NILP (key))
3940 if (EQ (format, Qxml))
3942 CFDataRef data = CFPropertyListCreateXMLData (NULL, plist);
3943 if (data == NULL)
3944 goto out;
3945 result = cfdata_to_lisp (data);
3946 CFRelease (data);
3948 else
3949 result =
3950 cfproperty_list_to_lisp (plist, EQ (format, Qt),
3951 NILP (hash_bound) ? -1 : XINT (hash_bound));
3953 out:
3954 if (app_plist)
3955 CFRelease (app_plist);
3956 CFRelease (app_id);
3958 UNBLOCK_INPUT;
3960 return result;
3964 static CFStringEncoding
3965 get_cfstring_encoding_from_lisp (obj)
3966 Lisp_Object obj;
3968 CFStringRef iana_name;
3969 CFStringEncoding encoding = kCFStringEncodingInvalidId;
3971 if (NILP (obj))
3972 return kCFStringEncodingUnicode;
3974 if (INTEGERP (obj))
3975 return XINT (obj);
3977 if (SYMBOLP (obj) && !NILP (Fcoding_system_p (obj)))
3979 Lisp_Object coding_spec, plist;
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);
3986 if (SYMBOLP (obj))
3987 obj = SYMBOL_NAME (obj);
3989 if (STRINGP (obj))
3991 iana_name = cfstring_create_with_string (obj);
3992 if (iana_name)
3994 encoding = CFStringConvertIANACharSetNameToEncoding (iana_name);
3995 CFRelease (iana_name);
3999 return encoding;
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;
4008 int form = -1;
4009 TextEncodingVariant variant;
4010 float initial_mag = 0.0;
4011 CFStringRef result = NULL;
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))
4023 variant = kUnicodeHFSPlusDecompVariant;
4024 initial_mag = 1.5;
4026 else if (EQ (symbol, QHFS_plus_C))
4028 variant = kUnicodeHFSPlusCompVariant;
4029 initial_mag = 1.0;
4032 if (form >= 0)
4034 CFMutableStringRef mut_str = CFStringCreateMutableCopy (NULL, 0, str);
4036 if (mut_str)
4038 CFStringNormalize (mut_str, form);
4039 result = mut_str;
4042 else if (initial_mag > 0.0)
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;
4051 map.unicodeEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault,
4052 kUnicodeNoSubset,
4053 kTextEncodingDefaultFormat);
4054 map.otherEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault,
4055 variant,
4056 kTextEncodingDefaultFormat);
4057 map.mappingVersion = kUnicodeUseLatestMapping;
4059 length = CFStringGetLength (str);
4060 out_size = (int)((float)length * initial_mag) * sizeof (UniChar);
4061 if (out_size < 32)
4062 out_size = 32;
4064 in_text = (UniChar *)CFStringGetCharactersPtr (str);
4065 if (in_text == NULL)
4067 buffer = xmalloc (sizeof (UniChar) * length);
4068 if (buffer)
4070 CFStringGetCharacters (str, CFRangeMake (0, length), buffer);
4071 in_text = buffer;
4075 if (in_text)
4076 err = CreateUnicodeToTextInfo(&map, &uni);
4077 while (err == noErr)
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))
4091 xfree (out_buf);
4092 out_size += length;
4094 else
4095 break;
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);
4107 else
4109 result = str;
4110 CFRetain (result);
4113 return result;
4115 #endif
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. Nil for
4122 encoding means UTF-16 in native byte order, no byte order marker.
4123 On Mac OS X 10.2 and later, you can do Unicode Normalization by
4124 specifying the optional argument NORMALIZATION-FORM with a symbol NFD,
4125 NFKD, NFC, NFKC, HFS+D, or HFS+C.
4126 On successful conversion, return the result string, else return nil. */)
4127 (string, source, target, normalization_form)
4128 Lisp_Object string, source, target, normalization_form;
4130 Lisp_Object result = Qnil;
4131 CFStringEncoding src_encoding, tgt_encoding;
4132 CFStringRef str = NULL;
4134 CHECK_STRING (string);
4135 if (!INTEGERP (source) && !STRINGP (source))
4136 CHECK_SYMBOL (source);
4137 if (!INTEGERP (target) && !STRINGP (target))
4138 CHECK_SYMBOL (target);
4139 CHECK_SYMBOL (normalization_form);
4141 BLOCK_INPUT;
4143 src_encoding = get_cfstring_encoding_from_lisp (source);
4144 tgt_encoding = get_cfstring_encoding_from_lisp (target);
4146 /* We really want string_to_unibyte, but since it doesn't exist yet, we
4147 use string_as_unibyte which works as well, except for the fact that
4148 it's too permissive (it doesn't check that the multibyte string only
4149 contain single-byte chars). */
4150 string = Fstring_as_unibyte (string);
4151 if (src_encoding != kCFStringEncodingInvalidId
4152 && tgt_encoding != kCFStringEncodingInvalidId)
4153 str = CFStringCreateWithBytes (NULL, SDATA (string), SBYTES (string),
4154 src_encoding, !NILP (source));
4155 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
4156 if (str)
4158 CFStringRef saved_str = str;
4160 str = cfstring_create_normalized (saved_str, normalization_form);
4161 CFRelease (saved_str);
4163 #endif
4164 if (str)
4166 CFIndex str_len, buf_len;
4168 str_len = CFStringGetLength (str);
4169 if (CFStringGetBytes (str, CFRangeMake (0, str_len), tgt_encoding, 0,
4170 !NILP (target), NULL, 0, &buf_len) == str_len)
4172 result = make_uninit_string (buf_len);
4173 CFStringGetBytes (str, CFRangeMake (0, str_len), tgt_encoding, 0,
4174 !NILP (target), SDATA (result), buf_len, NULL);
4176 CFRelease (str);
4179 UNBLOCK_INPUT;
4181 return result;
4183 #endif /* TARGET_API_MAC_CARBON */
4186 DEFUN ("mac-clear-font-name-table", Fmac_clear_font_name_table, Smac_clear_font_name_table, 0, 0, 0,
4187 doc: /* Clear the font name table. */)
4190 check_mac ();
4191 mac_clear_font_name_table ();
4192 return Qnil;
4195 #ifdef MAC_OSX
4196 #undef select
4198 extern int inhibit_window_system;
4199 extern int noninteractive;
4201 /* Unlike in X11, window events in Carbon do not come from sockets.
4202 So we cannot simply use `select' to monitor two kinds of inputs:
4203 window events and process outputs. We emulate such functionality
4204 by regarding fd 0 as the window event channel and simultaneously
4205 monitoring both kinds of input channels. It is implemented by
4206 dividing into some cases:
4207 1. The window event channel is not involved.
4208 -> Use `select'.
4209 2. Sockets are not involved.
4210 -> Use ReceiveNextEvent.
4211 3. [If SELECT_USE_CFSOCKET is defined]
4212 Only the window event channel and socket read channels are
4213 involved, and timeout is not too short (greater than
4214 SELECT_TIMEOUT_THRESHHOLD_RUNLOOP seconds).
4215 -> Create CFSocket for each socket and add it into the current
4216 event RunLoop so that an `ready-to-read' event can be posted
4217 to the event queue that is also used for window events. Then
4218 ReceiveNextEvent can wait for both kinds of inputs.
4219 4. Otherwise.
4220 -> Periodically poll the window input channel while repeatedly
4221 executing `select' with a short timeout
4222 (SELECT_POLLING_PERIOD_USEC microseconds). */
4224 #define SELECT_POLLING_PERIOD_USEC 20000
4225 #ifdef SELECT_USE_CFSOCKET
4226 #define SELECT_TIMEOUT_THRESHOLD_RUNLOOP 0.2
4227 #define EVENT_CLASS_SOCK 'Sock'
4229 static void
4230 socket_callback (s, type, address, data, info)
4231 CFSocketRef s;
4232 CFSocketCallBackType type;
4233 CFDataRef address;
4234 const void *data;
4235 void *info;
4237 EventRef event;
4239 CreateEvent (NULL, EVENT_CLASS_SOCK, 0, 0, kEventAttributeNone, &event);
4240 PostEventToQueue (GetCurrentEventQueue (), event, kEventPriorityStandard);
4241 ReleaseEvent (event);
4243 #endif /* SELECT_USE_CFSOCKET */
4245 static int
4246 select_and_poll_event (n, rfds, wfds, efds, timeout)
4247 int n;
4248 SELECT_TYPE *rfds;
4249 SELECT_TYPE *wfds;
4250 SELECT_TYPE *efds;
4251 struct timeval *timeout;
4253 int r;
4254 OSErr err;
4256 r = select (n, rfds, wfds, efds, timeout);
4257 if (r != -1)
4259 BLOCK_INPUT;
4260 err = ReceiveNextEvent (0, NULL, kEventDurationNoWait,
4261 kEventLeaveInQueue, NULL);
4262 UNBLOCK_INPUT;
4263 if (err == noErr)
4265 FD_SET (0, rfds);
4266 r++;
4269 return r;
4272 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1020
4273 #undef SELECT_INVALIDATE_CFSOCKET
4274 #endif
4277 sys_select (n, rfds, wfds, efds, timeout)
4278 int n;
4279 SELECT_TYPE *rfds;
4280 SELECT_TYPE *wfds;
4281 SELECT_TYPE *efds;
4282 struct timeval *timeout;
4284 OSErr err;
4285 int i, r;
4286 EMACS_TIME select_timeout;
4288 if (inhibit_window_system || noninteractive
4289 || rfds == NULL || !FD_ISSET (0, rfds))
4290 return select (n, rfds, wfds, efds, timeout);
4292 FD_CLR (0, rfds);
4294 if (wfds == NULL && efds == NULL)
4296 int nsocks = 0;
4297 SELECT_TYPE orfds = *rfds;
4299 EventTimeout timeout_sec =
4300 (timeout
4301 ? (EMACS_SECS (*timeout) * kEventDurationSecond
4302 + EMACS_USECS (*timeout) * kEventDurationMicrosecond)
4303 : kEventDurationForever);
4305 for (i = 1; i < n; i++)
4306 if (FD_ISSET (i, rfds))
4307 nsocks++;
4309 if (nsocks == 0)
4311 BLOCK_INPUT;
4312 err = ReceiveNextEvent (0, NULL, timeout_sec,
4313 kEventLeaveInQueue, NULL);
4314 UNBLOCK_INPUT;
4315 if (err == noErr)
4317 FD_SET (0, rfds);
4318 return 1;
4320 else
4321 return 0;
4324 /* Avoid initial overhead of RunLoop setup for the case that
4325 some input is already available. */
4326 EMACS_SET_SECS_USECS (select_timeout, 0, 0);
4327 r = select_and_poll_event (n, rfds, wfds, efds, &select_timeout);
4328 if (r != 0 || timeout_sec == 0.0)
4329 return r;
4331 *rfds = orfds;
4333 #ifdef SELECT_USE_CFSOCKET
4334 if (timeout_sec > 0 && timeout_sec <= SELECT_TIMEOUT_THRESHOLD_RUNLOOP)
4335 goto poll_periodically;
4338 CFRunLoopRef runloop =
4339 (CFRunLoopRef) GetCFRunLoopFromEventLoop (GetCurrentEventLoop ());
4340 EventTypeSpec specs[] = {{EVENT_CLASS_SOCK, 0}};
4341 #ifdef SELECT_INVALIDATE_CFSOCKET
4342 CFSocketRef *shead, *s;
4343 #else
4344 CFRunLoopSourceRef *shead, *s;
4345 #endif
4347 BLOCK_INPUT;
4349 #ifdef SELECT_INVALIDATE_CFSOCKET
4350 shead = xmalloc (sizeof (CFSocketRef) * nsocks);
4351 #else
4352 shead = xmalloc (sizeof (CFRunLoopSourceRef) * nsocks);
4353 #endif
4354 s = shead;
4355 for (i = 1; i < n; i++)
4356 if (FD_ISSET (i, rfds))
4358 CFSocketRef socket =
4359 CFSocketCreateWithNative (NULL, i, kCFSocketReadCallBack,
4360 socket_callback, NULL);
4361 CFRunLoopSourceRef source =
4362 CFSocketCreateRunLoopSource (NULL, socket, 0);
4364 #ifdef SELECT_INVALIDATE_CFSOCKET
4365 CFSocketSetSocketFlags (socket, 0);
4366 #endif
4367 CFRunLoopAddSource (runloop, source, kCFRunLoopDefaultMode);
4368 #ifdef SELECT_INVALIDATE_CFSOCKET
4369 CFRelease (source);
4370 *s = socket;
4371 #else
4372 CFRelease (socket);
4373 *s = source;
4374 #endif
4375 s++;
4378 err = ReceiveNextEvent (0, NULL, timeout_sec, kEventLeaveInQueue, NULL);
4382 --s;
4383 #ifdef SELECT_INVALIDATE_CFSOCKET
4384 CFSocketInvalidate (*s);
4385 #else
4386 CFRunLoopRemoveSource (runloop, *s, kCFRunLoopDefaultMode);
4387 #endif
4388 CFRelease (*s);
4390 while (s != shead);
4392 xfree (shead);
4394 if (err)
4396 FD_ZERO (rfds);
4397 r = 0;
4399 else
4401 FlushEventsMatchingListFromQueue (GetCurrentEventQueue (),
4402 GetEventTypeCount (specs),
4403 specs);
4404 EMACS_SET_SECS_USECS (select_timeout, 0, 0);
4405 r = select_and_poll_event (n, rfds, wfds, efds, &select_timeout);
4408 UNBLOCK_INPUT;
4410 return r;
4412 #endif /* SELECT_USE_CFSOCKET */
4415 poll_periodically:
4417 EMACS_TIME end_time, now, remaining_time;
4418 SELECT_TYPE orfds = *rfds, owfds, oefds;
4420 if (wfds)
4421 owfds = *wfds;
4422 if (efds)
4423 oefds = *efds;
4424 if (timeout)
4426 remaining_time = *timeout;
4427 EMACS_GET_TIME (now);
4428 EMACS_ADD_TIME (end_time, now, remaining_time);
4433 EMACS_SET_SECS_USECS (select_timeout, 0, SELECT_POLLING_PERIOD_USEC);
4434 if (timeout && EMACS_TIME_LT (remaining_time, select_timeout))
4435 select_timeout = remaining_time;
4436 r = select_and_poll_event (n, rfds, wfds, efds, &select_timeout);
4437 if (r != 0)
4438 return r;
4440 *rfds = orfds;
4441 if (wfds)
4442 *wfds = owfds;
4443 if (efds)
4444 *efds = oefds;
4446 if (timeout)
4448 EMACS_GET_TIME (now);
4449 EMACS_SUB_TIME (remaining_time, end_time, now);
4452 while (!timeout || EMACS_TIME_LT (now, end_time));
4454 FD_ZERO (rfds);
4455 if (wfds)
4456 FD_ZERO (wfds);
4457 if (efds)
4458 FD_ZERO (efds);
4459 return 0;
4463 /* Set up environment variables so that Emacs can correctly find its
4464 support files when packaged as an application bundle. Directories
4465 placed in /usr/local/share/emacs/<emacs-version>/, /usr/local/bin,
4466 and /usr/local/libexec/emacs/<emacs-version>/<system-configuration>
4467 by `make install' by default can instead be placed in
4468 .../Emacs.app/Contents/Resources/ and
4469 .../Emacs.app/Contents/MacOS/. Each of these environment variables
4470 is changed only if it is not already set. Presumably if the user
4471 sets an environment variable, he will want to use files in his path
4472 instead of ones in the application bundle. */
4473 void
4474 init_mac_osx_environment ()
4476 CFBundleRef bundle;
4477 CFURLRef bundleURL;
4478 CFStringRef cf_app_bundle_pathname;
4479 int app_bundle_pathname_len;
4480 char *app_bundle_pathname;
4481 char *p, *q;
4482 struct stat st;
4484 /* Fetch the pathname of the application bundle as a C string into
4485 app_bundle_pathname. */
4487 bundle = CFBundleGetMainBundle ();
4488 if (!bundle || CFBundleGetIdentifier (bundle) == NULL)
4490 /* We could not find the bundle identifier. For now, prevent
4491 the fatal error by bringing it up in the terminal. */
4492 inhibit_window_system = 1;
4493 return;
4496 bundleURL = CFBundleCopyBundleURL (bundle);
4497 if (!bundleURL)
4498 return;
4500 cf_app_bundle_pathname = CFURLCopyFileSystemPath (bundleURL,
4501 kCFURLPOSIXPathStyle);
4502 app_bundle_pathname_len = CFStringGetLength (cf_app_bundle_pathname);
4503 app_bundle_pathname = (char *) alloca (app_bundle_pathname_len + 1);
4505 if (!CFStringGetCString (cf_app_bundle_pathname,
4506 app_bundle_pathname,
4507 app_bundle_pathname_len + 1,
4508 kCFStringEncodingISOLatin1))
4510 CFRelease (cf_app_bundle_pathname);
4511 return;
4514 CFRelease (cf_app_bundle_pathname);
4516 /* P should have sufficient room for the pathname of the bundle plus
4517 the subpath in it leading to the respective directories. Q
4518 should have three times that much room because EMACSLOADPATH can
4519 have the value "<path to lisp dir>:<path to leim dir>:<path to
4520 site-lisp dir>". */
4521 p = (char *) alloca (app_bundle_pathname_len + 50);
4522 q = (char *) alloca (3 * app_bundle_pathname_len + 150);
4523 if (!getenv ("EMACSLOADPATH"))
4525 q[0] = '\0';
4527 strcpy (p, app_bundle_pathname);
4528 strcat (p, "/Contents/Resources/lisp");
4529 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
4530 strcat (q, p);
4532 strcpy (p, app_bundle_pathname);
4533 strcat (p, "/Contents/Resources/leim");
4534 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
4536 if (q[0] != '\0')
4537 strcat (q, ":");
4538 strcat (q, p);
4541 strcpy (p, app_bundle_pathname);
4542 strcat (p, "/Contents/Resources/site-lisp");
4543 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
4545 if (q[0] != '\0')
4546 strcat (q, ":");
4547 strcat (q, p);
4550 if (q[0] != '\0')
4551 setenv ("EMACSLOADPATH", q, 1);
4554 if (!getenv ("EMACSPATH"))
4556 q[0] = '\0';
4558 strcpy (p, app_bundle_pathname);
4559 strcat (p, "/Contents/MacOS/libexec");
4560 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
4561 strcat (q, p);
4563 strcpy (p, app_bundle_pathname);
4564 strcat (p, "/Contents/MacOS/bin");
4565 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
4567 if (q[0] != '\0')
4568 strcat (q, ":");
4569 strcat (q, p);
4572 if (q[0] != '\0')
4573 setenv ("EMACSPATH", q, 1);
4576 if (!getenv ("EMACSDATA"))
4578 strcpy (p, app_bundle_pathname);
4579 strcat (p, "/Contents/Resources/etc");
4580 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
4581 setenv ("EMACSDATA", p, 1);
4584 if (!getenv ("EMACSDOC"))
4586 strcpy (p, app_bundle_pathname);
4587 strcat (p, "/Contents/Resources/etc");
4588 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
4589 setenv ("EMACSDOC", p, 1);
4592 if (!getenv ("INFOPATH"))
4594 strcpy (p, app_bundle_pathname);
4595 strcat (p, "/Contents/Resources/info");
4596 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
4597 setenv ("INFOPATH", p, 1);
4600 #endif /* MAC_OSX */
4603 static Lisp_Object
4604 mac_get_system_locale ()
4606 OSErr err;
4607 LangCode lang;
4608 RegionCode region;
4609 LocaleRef locale;
4610 Str255 str;
4612 lang = GetScriptVariable (smSystemScript, smScriptLang);
4613 region = GetScriptManagerVariable (smRegionCode);
4614 err = LocaleRefFromLangOrRegionCode (lang, region, &locale);
4615 if (err == noErr)
4616 err = LocaleRefGetPartString (locale, kLocaleAllPartsMask,
4617 sizeof (str), str);
4618 if (err == noErr)
4619 return build_string (str);
4620 else
4621 return Qnil;
4625 void
4626 syms_of_mac ()
4628 #if TARGET_API_MAC_CARBON
4629 Qstring = intern ("string"); staticpro (&Qstring);
4630 Qnumber = intern ("number"); staticpro (&Qnumber);
4631 Qboolean = intern ("boolean"); staticpro (&Qboolean);
4632 Qdate = intern ("date"); staticpro (&Qdate);
4633 Qdata = intern ("data"); staticpro (&Qdata);
4634 Qarray = intern ("array"); staticpro (&Qarray);
4635 Qdictionary = intern ("dictionary"); staticpro (&Qdictionary);
4637 Qxml = intern ("xml");
4638 staticpro (&Qxml);
4640 Qmime_charset = intern ("mime-charset");
4641 staticpro (&Qmime_charset);
4643 QNFD = intern ("NFD"); staticpro (&QNFD);
4644 QNFKD = intern ("NFKD"); staticpro (&QNFKD);
4645 QNFC = intern ("NFC"); staticpro (&QNFC);
4646 QNFKC = intern ("NFKC"); staticpro (&QNFKC);
4647 QHFS_plus_D = intern ("HFS+D"); staticpro (&QHFS_plus_D);
4648 QHFS_plus_C = intern ("HFS+C"); staticpro (&QHFS_plus_C);
4649 #endif
4651 #if TARGET_API_MAC_CARBON
4652 defsubr (&Smac_get_preference);
4653 defsubr (&Smac_code_convert_string);
4654 #endif
4655 defsubr (&Smac_clear_font_name_table);
4657 defsubr (&Smac_set_file_creator);
4658 defsubr (&Smac_set_file_type);
4659 defsubr (&Smac_get_file_creator);
4660 defsubr (&Smac_get_file_type);
4661 defsubr (&Sdo_applescript);
4662 defsubr (&Smac_file_name_to_posix);
4663 defsubr (&Sposix_file_name_to_mac);
4665 DEFVAR_INT ("mac-system-script-code", &mac_system_script_code,
4666 doc: /* The system script code. */);
4667 mac_system_script_code = (ScriptCode) GetScriptManagerVariable (smSysScript);
4669 DEFVAR_LISP ("mac-system-locale", &Vmac_system_locale,
4670 doc: /* The system locale identifier string.
4671 This is not a POSIX locale ID, but an ICU locale ID. So encoding
4672 information is not included. */);
4673 Vmac_system_locale = mac_get_system_locale ();
4676 /* arch-tag: 29d30c1f-0c6b-4f88-8a6d-0558d7f9dbff
4677 (do not change this comment) */