(custom-add-parent-links): Filter out custom-group-link,
[emacs.git] / src / mac.c
blob76ba3d4179809898a655fa40bfd519fe3366f175
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;
269 struct cfdict_context
271 Lisp_Object *result;
272 int with_tag, hash_bound;
275 /* C string to CFString. */
277 CFStringRef
278 cfstring_create_with_utf8_cstring (c_str)
279 const char *c_str;
281 CFStringRef str;
283 str = CFStringCreateWithCString (NULL, c_str, kCFStringEncodingUTF8);
284 if (str == NULL)
285 /* Failed to interpret as UTF 8. Fall back on Mac Roman. */
286 str = CFStringCreateWithCString (NULL, c_str, kCFStringEncodingMacRoman);
288 return str;
292 /* Lisp string to CFString. */
294 CFStringRef
295 cfstring_create_with_string (s)
296 Lisp_Object s;
298 CFStringRef string = NULL;
300 if (STRING_MULTIBYTE (s))
302 char *p, *end = SDATA (s) + SBYTES (s);
304 for (p = SDATA (s); p < end; p++)
305 if (!isascii (*p))
307 s = ENCODE_UTF_8 (s);
308 break;
310 string = CFStringCreateWithBytes (NULL, SDATA (s), SBYTES (s),
311 kCFStringEncodingUTF8, false);
314 if (string == NULL)
315 /* Failed to interpret as UTF 8. Fall back on Mac Roman. */
316 string = CFStringCreateWithBytes (NULL, SDATA (s), SBYTES (s),
317 kCFStringEncodingMacRoman, false);
319 return string;
323 /* From CFData to a lisp string. Always returns a unibyte string. */
325 Lisp_Object
326 cfdata_to_lisp (data)
327 CFDataRef data;
329 CFIndex len = CFDataGetLength (data);
330 Lisp_Object result = make_uninit_string (len);
332 CFDataGetBytes (data, CFRangeMake (0, len), SDATA (result));
334 return result;
338 /* From CFString to a lisp string. Returns a unibyte string
339 containing a UTF-8 byte sequence. */
341 Lisp_Object
342 cfstring_to_lisp_nodecode (string)
343 CFStringRef string;
345 Lisp_Object result = Qnil;
346 const char *s = CFStringGetCStringPtr (string, kCFStringEncodingUTF8);
348 if (s)
349 result = make_unibyte_string (s, strlen (s));
350 else
352 CFDataRef data =
353 CFStringCreateExternalRepresentation (NULL, string,
354 kCFStringEncodingUTF8, '?');
356 if (data)
358 result = cfdata_to_lisp (data);
359 CFRelease (data);
363 return result;
367 /* From CFString to a lisp string. Never returns a unibyte string
368 (even if it only contains ASCII characters).
369 This may cause GC during code conversion. */
371 Lisp_Object
372 cfstring_to_lisp (string)
373 CFStringRef string;
375 Lisp_Object result = cfstring_to_lisp_nodecode (string);
377 if (!NILP (result))
379 result = code_convert_string_norecord (result, Qutf_8, 0);
380 /* This may be superfluous. Just to make sure that the result
381 is a multibyte string. */
382 result = string_to_multibyte (result);
385 return result;
389 /* CFNumber to a lisp integer or a lisp float. */
391 Lisp_Object
392 cfnumber_to_lisp (number)
393 CFNumberRef number;
395 Lisp_Object result = Qnil;
396 #if BITS_PER_EMACS_INT > 32
397 SInt64 int_val;
398 CFNumberType emacs_int_type = kCFNumberSInt64Type;
399 #else
400 SInt32 int_val;
401 CFNumberType emacs_int_type = kCFNumberSInt32Type;
402 #endif
403 double float_val;
405 if (CFNumberGetValue (number, emacs_int_type, &int_val)
406 && !FIXNUM_OVERFLOW_P (int_val))
407 result = make_number (int_val);
408 else
409 if (CFNumberGetValue (number, kCFNumberDoubleType, &float_val))
410 result = make_float (float_val);
411 return result;
415 /* CFDate to a list of three integers as in a return value of
416 `current-time'. */
418 Lisp_Object
419 cfdate_to_lisp (date)
420 CFDateRef date;
422 static const CFGregorianDate epoch_gdate = {1970, 1, 1, 0, 0, 0.0};
423 static CFAbsoluteTime epoch = 0.0, sec;
424 int high, low;
426 if (epoch == 0.0)
427 epoch = CFGregorianDateGetAbsoluteTime (epoch_gdate, NULL);
429 sec = CFDateGetAbsoluteTime (date) - epoch;
430 high = sec / 65536.0;
431 low = sec - high * 65536.0;
433 return list3 (make_number (high), make_number (low), make_number (0));
437 /* CFBoolean to a lisp symbol, `t' or `nil'. */
439 Lisp_Object
440 cfboolean_to_lisp (boolean)
441 CFBooleanRef boolean;
443 return CFBooleanGetValue (boolean) ? Qt : Qnil;
447 /* Any Core Foundation object to a (lengthy) lisp string. */
449 Lisp_Object
450 cfobject_desc_to_lisp (object)
451 CFTypeRef object;
453 Lisp_Object result = Qnil;
454 CFStringRef desc = CFCopyDescription (object);
456 if (desc)
458 result = cfstring_to_lisp (desc);
459 CFRelease (desc);
462 return result;
466 /* Callback functions for cfproperty_list_to_lisp. */
468 static void
469 cfdictionary_add_to_list (key, value, context)
470 const void *key;
471 const void *value;
472 void *context;
474 struct cfdict_context *cxt = (struct cfdict_context *)context;
476 *cxt->result =
477 Fcons (Fcons (cfstring_to_lisp (key),
478 cfproperty_list_to_lisp (value, cxt->with_tag,
479 cxt->hash_bound)),
480 *cxt->result);
483 static void
484 cfdictionary_puthash (key, value, context)
485 const void *key;
486 const void *value;
487 void *context;
489 Lisp_Object lisp_key = cfstring_to_lisp (key);
490 struct cfdict_context *cxt = (struct cfdict_context *)context;
491 struct Lisp_Hash_Table *h = XHASH_TABLE (*(cxt->result));
492 unsigned hash_code;
494 hash_lookup (h, lisp_key, &hash_code);
495 hash_put (h, lisp_key,
496 cfproperty_list_to_lisp (value, cxt->with_tag, cxt->hash_bound),
497 hash_code);
501 /* Convert CFPropertyList PLIST to a lisp object. If WITH_TAG is
502 non-zero, a symbol that represents the type of the original Core
503 Foundation object is prepended. HASH_BOUND specifies which kinds
504 of the lisp objects, alists or hash tables, are used as the targets
505 of the conversion from CFDictionary. If HASH_BOUND is negative,
506 always generate alists. If HASH_BOUND >= 0, generate an alist if
507 the number of keys in the dictionary is smaller than HASH_BOUND,
508 and a hash table otherwise. */
510 Lisp_Object
511 cfproperty_list_to_lisp (plist, with_tag, hash_bound)
512 CFPropertyListRef plist;
513 int with_tag, hash_bound;
515 CFTypeID type_id = CFGetTypeID (plist);
516 Lisp_Object tag = Qnil, result = Qnil;
517 struct gcpro gcpro1, gcpro2;
519 GCPRO2 (tag, result);
521 if (type_id == CFStringGetTypeID ())
523 tag = Qstring;
524 result = cfstring_to_lisp (plist);
526 else if (type_id == CFNumberGetTypeID ())
528 tag = Qnumber;
529 result = cfnumber_to_lisp (plist);
531 else if (type_id == CFBooleanGetTypeID ())
533 tag = Qboolean;
534 result = cfboolean_to_lisp (plist);
536 else if (type_id == CFDateGetTypeID ())
538 tag = Qdate;
539 result = cfdate_to_lisp (plist);
541 else if (type_id == CFDataGetTypeID ())
543 tag = Qdata;
544 result = cfdata_to_lisp (plist);
546 else if (type_id == CFArrayGetTypeID ())
548 CFIndex index, count = CFArrayGetCount (plist);
550 tag = Qarray;
551 result = Fmake_vector (make_number (count), Qnil);
552 for (index = 0; index < count; index++)
553 XVECTOR (result)->contents[index] =
554 cfproperty_list_to_lisp (CFArrayGetValueAtIndex (plist, index),
555 with_tag, hash_bound);
557 else if (type_id == CFDictionaryGetTypeID ())
559 struct cfdict_context context;
560 CFIndex count = CFDictionaryGetCount (plist);
562 tag = Qdictionary;
563 context.result = &result;
564 context.with_tag = with_tag;
565 context.hash_bound = hash_bound;
566 if (hash_bound < 0 || count < hash_bound)
568 result = Qnil;
569 CFDictionaryApplyFunction (plist, cfdictionary_add_to_list,
570 &context);
572 else
574 result = make_hash_table (Qequal,
575 make_number (count),
576 make_float (DEFAULT_REHASH_SIZE),
577 make_float (DEFAULT_REHASH_THRESHOLD),
578 Qnil, Qnil, Qnil);
579 CFDictionaryApplyFunction (plist, cfdictionary_puthash,
580 &context);
583 else
584 abort ();
586 UNGCPRO;
588 if (with_tag)
589 result = Fcons (tag, result);
591 return result;
593 #endif
596 /***********************************************************************
597 Emulation of the X Resource Manager
598 ***********************************************************************/
600 /* Parser functions for resource lines. Each function takes an
601 address of a variable whose value points to the head of a string.
602 The value will be advanced so that it points to the next character
603 of the parsed part when the function returns.
605 A resource name such as "Emacs*font" is parsed into a non-empty
606 list called `quarks'. Each element is either a Lisp string that
607 represents a concrete component, a Lisp symbol LOOSE_BINDING
608 (actually Qlambda) that represents any number (>=0) of intervening
609 components, or a Lisp symbol SINGLE_COMPONENT (actually Qquote)
610 that represents as any single component. */
612 #define P (*p)
614 #define LOOSE_BINDING Qlambda /* '*' ("L"oose) */
615 #define SINGLE_COMPONENT Qquote /* '?' ("Q"uestion) */
617 static void
618 skip_white_space (p)
619 char **p;
621 /* WhiteSpace = {<space> | <horizontal tab>} */
622 while (*P == ' ' || *P == '\t')
623 P++;
626 static int
627 parse_comment (p)
628 char **p;
630 /* Comment = "!" {<any character except null or newline>} */
631 if (*P == '!')
633 P++;
634 while (*P)
635 if (*P++ == '\n')
636 break;
637 return 1;
639 else
640 return 0;
643 /* Don't interpret filename. Just skip until the newline. */
644 static int
645 parse_include_file (p)
646 char **p;
648 /* IncludeFile = "#" WhiteSpace "include" WhiteSpace FileName WhiteSpace */
649 if (*P == '#')
651 P++;
652 while (*P)
653 if (*P++ == '\n')
654 break;
655 return 1;
657 else
658 return 0;
661 static char
662 parse_binding (p)
663 char **p;
665 /* Binding = "." | "*" */
666 if (*P == '.' || *P == '*')
668 char binding = *P++;
670 while (*P == '.' || *P == '*')
671 if (*P++ == '*')
672 binding = '*';
673 return binding;
675 else
676 return '\0';
679 static Lisp_Object
680 parse_component (p)
681 char **p;
683 /* Component = "?" | ComponentName
684 ComponentName = NameChar {NameChar}
685 NameChar = "a"-"z" | "A"-"Z" | "0"-"9" | "_" | "-" */
686 if (*P == '?')
688 P++;
689 return SINGLE_COMPONENT;
691 else if (isalnum (*P) || *P == '_' || *P == '-')
693 char *start = P++;
695 while (isalnum (*P) || *P == '_' || *P == '-')
696 P++;
698 return make_unibyte_string (start, P - start);
700 else
701 return Qnil;
704 static Lisp_Object
705 parse_resource_name (p)
706 char **p;
708 Lisp_Object result = Qnil, component;
709 char binding;
711 /* ResourceName = [Binding] {Component Binding} ComponentName */
712 if (parse_binding (p) == '*')
713 result = Fcons (LOOSE_BINDING, result);
715 component = parse_component (p);
716 if (NILP (component))
717 return Qnil;
719 result = Fcons (component, result);
720 while ((binding = parse_binding (p)) != '\0')
722 if (binding == '*')
723 result = Fcons (LOOSE_BINDING, result);
724 component = parse_component (p);
725 if (NILP (component))
726 return Qnil;
727 else
728 result = Fcons (component, result);
731 /* The final component should not be '?'. */
732 if (EQ (component, SINGLE_COMPONENT))
733 return Qnil;
735 return Fnreverse (result);
738 static Lisp_Object
739 parse_value (p)
740 char **p;
742 char *q, *buf;
743 Lisp_Object seq = Qnil, result;
744 int buf_len, total_len = 0, len, continue_p;
746 q = strchr (P, '\n');
747 buf_len = q ? q - P : strlen (P);
748 buf = xmalloc (buf_len);
750 while (1)
752 q = buf;
753 continue_p = 0;
754 while (*P)
756 if (*P == '\n')
758 P++;
759 break;
761 else if (*P == '\\')
763 P++;
764 if (*P == '\0')
765 break;
766 else if (*P == '\n')
768 P++;
769 continue_p = 1;
770 break;
772 else if (*P == 'n')
774 *q++ = '\n';
775 P++;
777 else if ('0' <= P[0] && P[0] <= '7'
778 && '0' <= P[1] && P[1] <= '7'
779 && '0' <= P[2] && P[2] <= '7')
781 *q++ = (P[0] - '0' << 6) + (P[1] - '0' << 3) + (P[2] - '0');
782 P += 3;
784 else
785 *q++ = *P++;
787 else
788 *q++ = *P++;
790 len = q - buf;
791 seq = Fcons (make_unibyte_string (buf, len), seq);
792 total_len += len;
794 if (continue_p)
796 q = strchr (P, '\n');
797 len = q ? q - P : strlen (P);
798 if (len > buf_len)
800 xfree (buf);
801 buf_len = len;
802 buf = xmalloc (buf_len);
805 else
806 break;
808 xfree (buf);
810 if (SBYTES (XCAR (seq)) == total_len)
811 return make_string (SDATA (XCAR (seq)), total_len);
812 else
814 buf = xmalloc (total_len);
815 q = buf + total_len;
816 for (; CONSP (seq); seq = XCDR (seq))
818 len = SBYTES (XCAR (seq));
819 q -= len;
820 memcpy (q, SDATA (XCAR (seq)), len);
822 result = make_string (buf, total_len);
823 xfree (buf);
824 return result;
828 static Lisp_Object
829 parse_resource_line (p)
830 char **p;
832 Lisp_Object quarks, value;
834 /* ResourceLine = Comment | IncludeFile | ResourceSpec | <empty line> */
835 if (parse_comment (p) || parse_include_file (p))
836 return Qnil;
838 /* ResourceSpec = WhiteSpace ResourceName WhiteSpace ":" WhiteSpace Value */
839 skip_white_space (p);
840 quarks = parse_resource_name (p);
841 if (NILP (quarks))
842 goto cleanup;
843 skip_white_space (p);
844 if (*P != ':')
845 goto cleanup;
846 P++;
847 skip_white_space (p);
848 value = parse_value (p);
849 return Fcons (quarks, value);
851 cleanup:
852 /* Skip the remaining data as a dummy value. */
853 parse_value (p);
854 return Qnil;
857 #undef P
859 /* Equivalents of X Resource Manager functions.
861 An X Resource Database acts as a collection of resource names and
862 associated values. It is implemented as a trie on quarks. Namely,
863 each edge is labeled by either a string, LOOSE_BINDING, or
864 SINGLE_COMPONENT. Each node has a node id, which is a unique
865 nonnegative integer, and the root node id is 0. A database is
866 implemented as a hash table that maps a pair (SRC-NODE-ID .
867 EDGE-LABEL) to DEST-NODE-ID. It also holds a maximum node id used
868 in the table as a value for HASHKEY_MAX_NID. A value associated to
869 a node is recorded as a value for the node id.
871 A database also has a cache for past queries as a value for
872 HASHKEY_QUERY_CACHE. It is another hash table that maps
873 "NAME-STRING\0CLASS-STRING" to the result of the query. */
875 #define HASHKEY_MAX_NID (make_number (0))
876 #define HASHKEY_QUERY_CACHE (make_number (-1))
878 static XrmDatabase
879 xrm_create_database ()
881 XrmDatabase database;
883 database = make_hash_table (Qequal, make_number (DEFAULT_HASH_SIZE),
884 make_float (DEFAULT_REHASH_SIZE),
885 make_float (DEFAULT_REHASH_THRESHOLD),
886 Qnil, Qnil, Qnil);
887 Fputhash (HASHKEY_MAX_NID, make_number (0), database);
888 Fputhash (HASHKEY_QUERY_CACHE, Qnil, database);
890 return database;
893 static void
894 xrm_q_put_resource (database, quarks, value)
895 XrmDatabase database;
896 Lisp_Object quarks, value;
898 struct Lisp_Hash_Table *h = XHASH_TABLE (database);
899 unsigned hash_code;
900 int max_nid, i;
901 Lisp_Object node_id, key;
903 max_nid = XINT (Fgethash (HASHKEY_MAX_NID, database, Qnil));
905 XSETINT (node_id, 0);
906 for (; CONSP (quarks); quarks = XCDR (quarks))
908 key = Fcons (node_id, XCAR (quarks));
909 i = hash_lookup (h, key, &hash_code);
910 if (i < 0)
912 max_nid++;
913 XSETINT (node_id, max_nid);
914 hash_put (h, key, node_id, hash_code);
916 else
917 node_id = HASH_VALUE (h, i);
919 Fputhash (node_id, value, database);
921 Fputhash (HASHKEY_MAX_NID, make_number (max_nid), database);
922 Fputhash (HASHKEY_QUERY_CACHE, Qnil, database);
925 /* Merge multiple resource entries specified by DATA into a resource
926 database DATABASE. DATA points to the head of a null-terminated
927 string consisting of multiple resource lines. It's like a
928 combination of XrmGetStringDatabase and XrmMergeDatabases. */
930 void
931 xrm_merge_string_database (database, data)
932 XrmDatabase database;
933 char *data;
935 Lisp_Object quarks_value;
937 while (*data)
939 quarks_value = parse_resource_line (&data);
940 if (!NILP (quarks_value))
941 xrm_q_put_resource (database,
942 XCAR (quarks_value), XCDR (quarks_value));
946 static Lisp_Object
947 xrm_q_get_resource_1 (database, node_id, quark_name, quark_class)
948 XrmDatabase database;
949 Lisp_Object node_id, quark_name, quark_class;
951 struct Lisp_Hash_Table *h = XHASH_TABLE (database);
952 Lisp_Object key, labels[3], value;
953 int i, k;
955 if (!CONSP (quark_name))
956 return Fgethash (node_id, database, Qnil);
958 /* First, try tight bindings */
959 labels[0] = XCAR (quark_name);
960 labels[1] = XCAR (quark_class);
961 labels[2] = SINGLE_COMPONENT;
963 key = Fcons (node_id, Qnil);
964 for (k = 0; k < sizeof (labels) / sizeof (*labels); k++)
966 XSETCDR (key, labels[k]);
967 i = hash_lookup (h, key, NULL);
968 if (i >= 0)
970 value = xrm_q_get_resource_1 (database, HASH_VALUE (h, i),
971 XCDR (quark_name), XCDR (quark_class));
972 if (!NILP (value))
973 return value;
977 /* Then, try loose bindings */
978 XSETCDR (key, LOOSE_BINDING);
979 i = hash_lookup (h, key, NULL);
980 if (i >= 0)
982 value = xrm_q_get_resource_1 (database, HASH_VALUE (h, i),
983 quark_name, quark_class);
984 if (!NILP (value))
985 return value;
986 else
987 return xrm_q_get_resource_1 (database, node_id,
988 XCDR (quark_name), XCDR (quark_class));
990 else
991 return Qnil;
994 static Lisp_Object
995 xrm_q_get_resource (database, quark_name, quark_class)
996 XrmDatabase database;
997 Lisp_Object quark_name, quark_class;
999 return xrm_q_get_resource_1 (database, make_number (0),
1000 quark_name, quark_class);
1003 /* Retrieve a resource value for the specified NAME and CLASS from the
1004 resource database DATABASE. It corresponds to XrmGetResource. */
1006 Lisp_Object
1007 xrm_get_resource (database, name, class)
1008 XrmDatabase database;
1009 char *name, *class;
1011 Lisp_Object key, query_cache, quark_name, quark_class, tmp;
1012 int i, nn, nc;
1013 struct Lisp_Hash_Table *h;
1014 unsigned hash_code;
1016 nn = strlen (name);
1017 nc = strlen (class);
1018 key = make_uninit_string (nn + nc + 1);
1019 strcpy (SDATA (key), name);
1020 strncpy (SDATA (key) + nn + 1, class, nc);
1022 query_cache = Fgethash (HASHKEY_QUERY_CACHE, database, Qnil);
1023 if (NILP (query_cache))
1025 query_cache = make_hash_table (Qequal, make_number (DEFAULT_HASH_SIZE),
1026 make_float (DEFAULT_REHASH_SIZE),
1027 make_float (DEFAULT_REHASH_THRESHOLD),
1028 Qnil, Qnil, Qnil);
1029 Fputhash (HASHKEY_QUERY_CACHE, query_cache, database);
1031 h = XHASH_TABLE (query_cache);
1032 i = hash_lookup (h, key, &hash_code);
1033 if (i >= 0)
1034 return HASH_VALUE (h, i);
1036 quark_name = parse_resource_name (&name);
1037 if (*name != '\0')
1038 return Qnil;
1039 for (tmp = quark_name, nn = 0; CONSP (tmp); tmp = XCDR (tmp), nn++)
1040 if (!STRINGP (XCAR (tmp)))
1041 return Qnil;
1043 quark_class = parse_resource_name (&class);
1044 if (*class != '\0')
1045 return Qnil;
1046 for (tmp = quark_class, nc = 0; CONSP (tmp); tmp = XCDR (tmp), nc++)
1047 if (!STRINGP (XCAR (tmp)))
1048 return Qnil;
1050 if (nn != nc)
1051 return Qnil;
1052 else
1054 tmp = xrm_q_get_resource (database, quark_name, quark_class);
1055 hash_put (h, key, tmp, hash_code);
1056 return tmp;
1060 #if TARGET_API_MAC_CARBON
1061 static Lisp_Object
1062 xrm_cfproperty_list_to_value (plist)
1063 CFPropertyListRef plist;
1065 CFTypeID type_id = CFGetTypeID (plist);
1067 if (type_id == CFStringGetTypeID ())
1068 return cfstring_to_lisp (plist);
1069 else if (type_id == CFNumberGetTypeID ())
1071 CFStringRef string;
1072 Lisp_Object result = Qnil;
1074 string = CFStringCreateWithFormat (NULL, NULL, CFSTR ("%@"), plist);
1075 if (string)
1077 result = cfstring_to_lisp (string);
1078 CFRelease (string);
1080 return result;
1082 else if (type_id == CFBooleanGetTypeID ())
1083 return build_string (CFBooleanGetValue (plist) ? "true" : "false");
1084 else if (type_id == CFDataGetTypeID ())
1085 return cfdata_to_lisp (plist);
1086 else
1087 return Qnil;
1089 #endif
1091 /* Create a new resource database from the preferences for the
1092 application APPLICATION. APPLICATION is either a string that
1093 specifies an application ID, or NULL that represents the current
1094 application. */
1096 XrmDatabase
1097 xrm_get_preference_database (application)
1098 char *application;
1100 #if TARGET_API_MAC_CARBON
1101 CFStringRef app_id, *keys, user_doms[2], host_doms[2];
1102 CFMutableSetRef key_set = NULL;
1103 CFArrayRef key_array;
1104 CFIndex index, count;
1105 char *res_name;
1106 XrmDatabase database;
1107 Lisp_Object quarks = Qnil, value = Qnil;
1108 CFPropertyListRef plist;
1109 int iu, ih;
1110 struct gcpro gcpro1, gcpro2, gcpro3;
1112 user_doms[0] = kCFPreferencesCurrentUser;
1113 user_doms[1] = kCFPreferencesAnyUser;
1114 host_doms[0] = kCFPreferencesCurrentHost;
1115 host_doms[1] = kCFPreferencesAnyHost;
1117 database = xrm_create_database ();
1119 GCPRO3 (database, quarks, value);
1121 BLOCK_INPUT;
1123 app_id = kCFPreferencesCurrentApplication;
1124 if (application)
1126 app_id = cfstring_create_with_utf8_cstring (application);
1127 if (app_id == NULL)
1128 goto out;
1131 key_set = CFSetCreateMutable (NULL, 0, &kCFCopyStringSetCallBacks);
1132 if (key_set == NULL)
1133 goto out;
1134 for (iu = 0; iu < sizeof (user_doms) / sizeof (*user_doms) ; iu++)
1135 for (ih = 0; ih < sizeof (host_doms) / sizeof (*host_doms); ih++)
1137 key_array = CFPreferencesCopyKeyList (app_id, user_doms[iu],
1138 host_doms[ih]);
1139 if (key_array)
1141 count = CFArrayGetCount (key_array);
1142 for (index = 0; index < count; index++)
1143 CFSetAddValue (key_set,
1144 CFArrayGetValueAtIndex (key_array, index));
1145 CFRelease (key_array);
1149 count = CFSetGetCount (key_set);
1150 keys = xmalloc (sizeof (CFStringRef) * count);
1151 if (keys == NULL)
1152 goto out;
1153 CFSetGetValues (key_set, (const void **)keys);
1154 for (index = 0; index < count; index++)
1156 res_name = SDATA (cfstring_to_lisp_nodecode (keys[index]));
1157 quarks = parse_resource_name (&res_name);
1158 if (!(NILP (quarks) || *res_name))
1160 plist = CFPreferencesCopyAppValue (keys[index], app_id);
1161 value = xrm_cfproperty_list_to_value (plist);
1162 CFRelease (plist);
1163 if (!NILP (value))
1164 xrm_q_put_resource (database, quarks, value);
1168 xfree (keys);
1169 out:
1170 if (key_set)
1171 CFRelease (key_set);
1172 CFRelease (app_id);
1174 UNBLOCK_INPUT;
1176 UNGCPRO;
1178 return database;
1179 #else
1180 return xrm_create_database ();
1181 #endif
1185 #ifndef MAC_OSX
1187 /* The following functions with "sys_" prefix are stubs to Unix
1188 functions that have already been implemented by CW or MPW. The
1189 calls to them in Emacs source course are #define'd to call the sys_
1190 versions by the header files s-mac.h. In these stubs pathnames are
1191 converted between their Unix and Mac forms. */
1194 /* Unix epoch is Jan 1, 1970 while Mac epoch is Jan 1, 1904: 66 years
1195 + 17 leap days. These are for adjusting time values returned by
1196 MacOS Toolbox functions. */
1198 #define MAC_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
1200 #ifdef __MWERKS__
1201 #if __MSL__ < 0x6000
1202 /* CW Pro 5 epoch is Jan 1, 1900 (aaarghhhhh!); remember, 1900 is not
1203 a leap year! This is for adjusting time_t values returned by MSL
1204 functions. */
1205 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 70 + 17) * 24 * 60 * 60)
1206 #else /* __MSL__ >= 0x6000 */
1207 /* CW changes Pro 6 to follow Unix! */
1208 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
1209 #endif /* __MSL__ >= 0x6000 */
1210 #elif __MRC__
1211 /* MPW library functions follow Unix (confused?). */
1212 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
1213 #else /* not __MRC__ */
1214 You lose!!!
1215 #endif /* not __MRC__ */
1218 /* Define our own stat function for both MrC and CW. The reason for
1219 doing this: "stat" is both the name of a struct and function name:
1220 can't use the same trick like that for sys_open, sys_close, etc. to
1221 redirect Emacs's calls to our own version that converts Unix style
1222 filenames to Mac style filename because all sorts of compilation
1223 errors will be generated if stat is #define'd to be sys_stat. */
1226 stat_noalias (const char *path, struct stat *buf)
1228 char mac_pathname[MAXPATHLEN+1];
1229 CInfoPBRec cipb;
1231 if (posix_to_mac_pathname (path, mac_pathname, MAXPATHLEN+1) == 0)
1232 return -1;
1234 c2pstr (mac_pathname);
1235 cipb.hFileInfo.ioNamePtr = mac_pathname;
1236 cipb.hFileInfo.ioVRefNum = 0;
1237 cipb.hFileInfo.ioDirID = 0;
1238 cipb.hFileInfo.ioFDirIndex = 0;
1239 /* set to 0 to get information about specific dir or file */
1241 errno = PBGetCatInfo (&cipb, false);
1242 if (errno == -43) /* -43: fnfErr defined in Errors.h */
1243 errno = ENOENT;
1244 if (errno != noErr)
1245 return -1;
1247 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* bit 4 = 1 for directories */
1249 buf->st_mode = S_IFDIR | S_IREAD | S_IEXEC;
1251 if (!(cipb.hFileInfo.ioFlAttrib & 0x1))
1252 buf->st_mode |= S_IWRITE; /* bit 1 = 1 for locked files/directories */
1253 buf->st_ino = cipb.dirInfo.ioDrDirID;
1254 buf->st_dev = cipb.dirInfo.ioVRefNum;
1255 buf->st_size = cipb.dirInfo.ioDrNmFls;
1256 /* size of dir = number of files and dirs */
1257 buf->st_atime
1258 = buf->st_mtime
1259 = cipb.dirInfo.ioDrMdDat - MAC_UNIX_EPOCH_DIFF;
1260 buf->st_ctime = cipb.dirInfo.ioDrCrDat - MAC_UNIX_EPOCH_DIFF;
1262 else
1264 buf->st_mode = S_IFREG | S_IREAD;
1265 if (!(cipb.hFileInfo.ioFlAttrib & 0x1))
1266 buf->st_mode |= S_IWRITE; /* bit 1 = 1 for locked files/directories */
1267 if (cipb.hFileInfo.ioFlFndrInfo.fdType == 'APPL')
1268 buf->st_mode |= S_IEXEC;
1269 buf->st_ino = cipb.hFileInfo.ioDirID;
1270 buf->st_dev = cipb.hFileInfo.ioVRefNum;
1271 buf->st_size = cipb.hFileInfo.ioFlLgLen;
1272 buf->st_atime
1273 = buf->st_mtime
1274 = cipb.hFileInfo.ioFlMdDat - MAC_UNIX_EPOCH_DIFF;
1275 buf->st_ctime = cipb.hFileInfo.ioFlCrDat - MAC_UNIX_EPOCH_DIFF;
1278 if (cipb.hFileInfo.ioFlFndrInfo.fdFlags & 0x8000)
1280 /* identify alias files as symlinks */
1281 buf->st_mode &= ~S_IFREG;
1282 buf->st_mode |= S_IFLNK;
1285 buf->st_nlink = 1;
1286 buf->st_uid = getuid ();
1287 buf->st_gid = getgid ();
1288 buf->st_rdev = 0;
1290 return 0;
1295 lstat (const char *path, struct stat *buf)
1297 int result;
1298 char true_pathname[MAXPATHLEN+1];
1300 /* Try looking for the file without resolving aliases first. */
1301 if ((result = stat_noalias (path, buf)) >= 0)
1302 return result;
1304 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1305 return -1;
1307 return stat_noalias (true_pathname, buf);
1312 stat (const char *path, struct stat *sb)
1314 int result;
1315 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
1316 int len;
1318 if ((result = stat_noalias (path, sb)) >= 0 &&
1319 ! (sb->st_mode & S_IFLNK))
1320 return result;
1322 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1323 return -1;
1325 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
1326 if (len > -1)
1328 fully_resolved_name[len] = '\0';
1329 /* in fact our readlink terminates strings */
1330 return lstat (fully_resolved_name, sb);
1332 else
1333 return lstat (true_pathname, sb);
1337 #if __MRC__
1338 /* CW defines fstat in stat.mac.c while MPW does not provide this
1339 function. Without the information of how to get from a file
1340 descriptor in MPW StdCLib to a Mac OS file spec, it should be hard
1341 to implement this function. Fortunately, there is only one place
1342 where this function is called in our configuration: in fileio.c,
1343 where only the st_dev and st_ino fields are used to determine
1344 whether two fildes point to different i-nodes to prevent copying
1345 a file onto itself equal. What we have here probably needs
1346 improvement. */
1349 fstat (int fildes, struct stat *buf)
1351 buf->st_dev = 0;
1352 buf->st_ino = fildes;
1353 buf->st_mode = S_IFREG; /* added by T.I. for the copy-file */
1354 return 0; /* success */
1356 #endif /* __MRC__ */
1360 mkdir (const char *dirname, int mode)
1362 #pragma unused(mode)
1364 HFileParam hfpb;
1365 char true_pathname[MAXPATHLEN+1], mac_pathname[MAXPATHLEN+1];
1367 if (find_true_pathname (dirname, true_pathname, MAXPATHLEN+1) == -1)
1368 return -1;
1370 if (posix_to_mac_pathname (true_pathname, mac_pathname, MAXPATHLEN+1) == 0)
1371 return -1;
1373 c2pstr (mac_pathname);
1374 hfpb.ioNamePtr = mac_pathname;
1375 hfpb.ioVRefNum = 0; /* ignored unless name is invalid */
1376 hfpb.ioDirID = 0; /* parent is the root */
1378 errno = PBDirCreate ((HParmBlkPtr) &hfpb, false);
1379 /* just return the Mac OSErr code for now */
1380 return errno == noErr ? 0 : -1;
1384 #undef rmdir
1385 sys_rmdir (const char *dirname)
1387 HFileParam hfpb;
1388 char mac_pathname[MAXPATHLEN+1];
1390 if (posix_to_mac_pathname (dirname, mac_pathname, MAXPATHLEN+1) == 0)
1391 return -1;
1393 c2pstr (mac_pathname);
1394 hfpb.ioNamePtr = mac_pathname;
1395 hfpb.ioVRefNum = 0; /* ignored unless name is invalid */
1396 hfpb.ioDirID = 0; /* parent is the root */
1398 errno = PBHDelete ((HParmBlkPtr) &hfpb, false);
1399 return errno == noErr ? 0 : -1;
1403 #ifdef __MRC__
1404 /* No implementation yet. */
1406 execvp (const char *path, ...)
1408 return -1;
1410 #endif /* __MRC__ */
1414 utime (const char *path, const struct utimbuf *times)
1416 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
1417 int len;
1418 char mac_pathname[MAXPATHLEN+1];
1419 CInfoPBRec cipb;
1421 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1422 return -1;
1424 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
1425 if (len > -1)
1426 fully_resolved_name[len] = '\0';
1427 else
1428 strcpy (fully_resolved_name, true_pathname);
1430 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
1431 return -1;
1433 c2pstr (mac_pathname);
1434 cipb.hFileInfo.ioNamePtr = mac_pathname;
1435 cipb.hFileInfo.ioVRefNum = 0;
1436 cipb.hFileInfo.ioDirID = 0;
1437 cipb.hFileInfo.ioFDirIndex = 0;
1438 /* set to 0 to get information about specific dir or file */
1440 errno = PBGetCatInfo (&cipb, false);
1441 if (errno != noErr)
1442 return -1;
1444 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* bit 4 = 1 for directories */
1446 if (times)
1447 cipb.dirInfo.ioDrMdDat = times->modtime + MAC_UNIX_EPOCH_DIFF;
1448 else
1449 GetDateTime (&cipb.dirInfo.ioDrMdDat);
1451 else
1453 if (times)
1454 cipb.hFileInfo.ioFlMdDat = times->modtime + MAC_UNIX_EPOCH_DIFF;
1455 else
1456 GetDateTime (&cipb.hFileInfo.ioFlMdDat);
1459 errno = PBSetCatInfo (&cipb, false);
1460 return errno == noErr ? 0 : -1;
1464 #ifndef F_OK
1465 #define F_OK 0
1466 #endif
1467 #ifndef X_OK
1468 #define X_OK 1
1469 #endif
1470 #ifndef W_OK
1471 #define W_OK 2
1472 #endif
1474 /* Like stat, but test for access mode in hfpb.ioFlAttrib */
1476 access (const char *path, int mode)
1478 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
1479 int len;
1480 char mac_pathname[MAXPATHLEN+1];
1481 CInfoPBRec cipb;
1483 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1484 return -1;
1486 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
1487 if (len > -1)
1488 fully_resolved_name[len] = '\0';
1489 else
1490 strcpy (fully_resolved_name, true_pathname);
1492 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
1493 return -1;
1495 c2pstr (mac_pathname);
1496 cipb.hFileInfo.ioNamePtr = mac_pathname;
1497 cipb.hFileInfo.ioVRefNum = 0;
1498 cipb.hFileInfo.ioDirID = 0;
1499 cipb.hFileInfo.ioFDirIndex = 0;
1500 /* set to 0 to get information about specific dir or file */
1502 errno = PBGetCatInfo (&cipb, false);
1503 if (errno != noErr)
1504 return -1;
1506 if (mode == F_OK) /* got this far, file exists */
1507 return 0;
1509 if (mode & X_OK)
1510 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* path refers to a directory */
1511 return 0;
1512 else
1514 if (cipb.hFileInfo.ioFlFndrInfo.fdType == 'APPL')
1515 return 0;
1516 else
1517 return -1;
1520 if (mode & W_OK)
1521 return (cipb.hFileInfo.ioFlAttrib & 0x1) ? -1 : 0;
1522 /* don't allow if lock bit is on */
1524 return -1;
1528 #define DEV_NULL_FD 0x10000
1530 #undef open
1532 sys_open (const char *path, int oflag)
1534 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
1535 int len;
1536 char mac_pathname[MAXPATHLEN+1];
1538 if (strcmp (path, "/dev/null") == 0)
1539 return DEV_NULL_FD; /* some bogus fd to be ignored in write */
1541 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1542 return -1;
1544 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
1545 if (len > -1)
1546 fully_resolved_name[len] = '\0';
1547 else
1548 strcpy (fully_resolved_name, true_pathname);
1550 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
1551 return -1;
1552 else
1554 #ifdef __MRC__
1555 int res = open (mac_pathname, oflag);
1556 /* if (oflag == O_WRONLY || oflag == O_RDWR) */
1557 if (oflag & O_CREAT)
1558 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
1559 return res;
1560 #else /* not __MRC__ */
1561 return open (mac_pathname, oflag);
1562 #endif /* not __MRC__ */
1567 #undef creat
1569 sys_creat (const char *path, mode_t mode)
1571 char true_pathname[MAXPATHLEN+1];
1572 int len;
1573 char mac_pathname[MAXPATHLEN+1];
1575 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1576 return -1;
1578 if (!posix_to_mac_pathname (true_pathname, mac_pathname, MAXPATHLEN+1))
1579 return -1;
1580 else
1582 #ifdef __MRC__
1583 int result = creat (mac_pathname);
1584 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
1585 return result;
1586 #else /* not __MRC__ */
1587 return creat (mac_pathname, mode);
1588 #endif /* not __MRC__ */
1593 #undef unlink
1595 sys_unlink (const char *path)
1597 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
1598 int len;
1599 char mac_pathname[MAXPATHLEN+1];
1601 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
1602 return -1;
1604 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
1605 if (len > -1)
1606 fully_resolved_name[len] = '\0';
1607 else
1608 strcpy (fully_resolved_name, true_pathname);
1610 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
1611 return -1;
1612 else
1613 return unlink (mac_pathname);
1617 #undef read
1619 sys_read (int fildes, char *buf, int count)
1621 if (fildes == 0) /* this should not be used for console input */
1622 return -1;
1623 else
1624 #if __MSL__ >= 0x6000
1625 return _read (fildes, buf, count);
1626 #else
1627 return read (fildes, buf, count);
1628 #endif
1632 #undef write
1634 sys_write (int fildes, const char *buf, int count)
1636 if (fildes == DEV_NULL_FD)
1637 return count;
1638 else
1639 #if __MSL__ >= 0x6000
1640 return _write (fildes, buf, count);
1641 #else
1642 return write (fildes, buf, count);
1643 #endif
1647 #undef rename
1649 sys_rename (const char * old_name, const char * new_name)
1651 char true_old_pathname[MAXPATHLEN+1], true_new_pathname[MAXPATHLEN+1];
1652 char fully_resolved_old_name[MAXPATHLEN+1];
1653 int len;
1654 char mac_old_name[MAXPATHLEN+1], mac_new_name[MAXPATHLEN+1];
1656 if (find_true_pathname (old_name, true_old_pathname, MAXPATHLEN+1) == -1)
1657 return -1;
1659 len = readlink (true_old_pathname, fully_resolved_old_name, MAXPATHLEN);
1660 if (len > -1)
1661 fully_resolved_old_name[len] = '\0';
1662 else
1663 strcpy (fully_resolved_old_name, true_old_pathname);
1665 if (find_true_pathname (new_name, true_new_pathname, MAXPATHLEN+1) == -1)
1666 return -1;
1668 if (strcmp (fully_resolved_old_name, true_new_pathname) == 0)
1669 return 0;
1671 if (!posix_to_mac_pathname (fully_resolved_old_name,
1672 mac_old_name,
1673 MAXPATHLEN+1))
1674 return -1;
1676 if (!posix_to_mac_pathname(true_new_pathname, mac_new_name, MAXPATHLEN+1))
1677 return -1;
1679 /* If a file with new_name already exists, rename deletes the old
1680 file in Unix. CW version fails in these situation. So we add a
1681 call to unlink here. */
1682 (void) unlink (mac_new_name);
1684 return rename (mac_old_name, mac_new_name);
1688 #undef fopen
1689 extern FILE *fopen (const char *name, const char *mode);
1690 FILE *
1691 sys_fopen (const char *name, const char *mode)
1693 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
1694 int len;
1695 char mac_pathname[MAXPATHLEN+1];
1697 if (find_true_pathname (name, true_pathname, MAXPATHLEN+1) == -1)
1698 return 0;
1700 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
1701 if (len > -1)
1702 fully_resolved_name[len] = '\0';
1703 else
1704 strcpy (fully_resolved_name, true_pathname);
1706 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
1707 return 0;
1708 else
1710 #ifdef __MRC__
1711 if (mode[0] == 'w' || mode[0] == 'a')
1712 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
1713 #endif /* not __MRC__ */
1714 return fopen (mac_pathname, mode);
1719 #include "keyboard.h"
1720 extern Boolean mac_wait_next_event (EventRecord *, UInt32, Boolean);
1723 select (n, rfds, wfds, efds, timeout)
1724 int n;
1725 SELECT_TYPE *rfds;
1726 SELECT_TYPE *wfds;
1727 SELECT_TYPE *efds;
1728 struct timeval *timeout;
1730 OSErr err;
1731 #if TARGET_API_MAC_CARBON
1732 EventTimeout timeout_sec =
1733 (timeout
1734 ? (EMACS_SECS (*timeout) * kEventDurationSecond
1735 + EMACS_USECS (*timeout) * kEventDurationMicrosecond)
1736 : kEventDurationForever);
1738 BLOCK_INPUT;
1739 err = ReceiveNextEvent (0, NULL, timeout_sec, kEventLeaveInQueue, NULL);
1740 UNBLOCK_INPUT;
1741 #else /* not TARGET_API_MAC_CARBON */
1742 EventRecord e;
1743 UInt32 sleep_time = EMACS_SECS (*timeout) * 60 +
1744 ((EMACS_USECS (*timeout) * 60) / 1000000);
1746 /* Can only handle wait for keyboard input. */
1747 if (n > 1 || wfds || efds)
1748 return -1;
1750 /* Also return true if an event other than a keyDown has occurred.
1751 This causes kbd_buffer_get_event in keyboard.c to call
1752 read_avail_input which in turn calls XTread_socket to poll for
1753 these events. Otherwise these never get processed except but a
1754 very slow poll timer. */
1755 if (mac_wait_next_event (&e, sleep_time, false))
1756 err = noErr;
1757 else
1758 err = -9875; /* eventLoopTimedOutErr */
1759 #endif /* not TARGET_API_MAC_CARBON */
1761 if (FD_ISSET (0, rfds))
1762 if (err == noErr)
1763 return 1;
1764 else
1766 FD_ZERO (rfds);
1767 return 0;
1769 else
1770 if (err == noErr)
1772 if (input_polling_used ())
1774 /* It could be confusing if a real alarm arrives while
1775 processing the fake one. Turn it off and let the
1776 handler reset it. */
1777 extern void poll_for_input_1 P_ ((void));
1778 int old_poll_suppress_count = poll_suppress_count;
1779 poll_suppress_count = 1;
1780 poll_for_input_1 ();
1781 poll_suppress_count = old_poll_suppress_count;
1783 errno = EINTR;
1784 return -1;
1786 else
1787 return 0;
1791 /* Simulation of SIGALRM. The stub for function signal stores the
1792 signal handler function in alarm_signal_func if a SIGALRM is
1793 encountered. */
1795 #include <signal.h>
1796 #include "syssignal.h"
1798 static TMTask mac_atimer_task;
1800 static QElemPtr mac_atimer_qlink = (QElemPtr) &mac_atimer_task;
1802 static int signal_mask = 0;
1804 #ifdef __MRC__
1805 __sigfun alarm_signal_func = (__sigfun) 0;
1806 #elif __MWERKS__
1807 __signal_func_ptr alarm_signal_func = (__signal_func_ptr) 0;
1808 #else /* not __MRC__ and not __MWERKS__ */
1809 You lose!!!
1810 #endif /* not __MRC__ and not __MWERKS__ */
1812 #undef signal
1813 #ifdef __MRC__
1814 extern __sigfun signal (int signal, __sigfun signal_func);
1815 __sigfun
1816 sys_signal (int signal_num, __sigfun signal_func)
1817 #elif __MWERKS__
1818 extern __signal_func_ptr signal (int signal, __signal_func_ptr signal_func);
1819 __signal_func_ptr
1820 sys_signal (int signal_num, __signal_func_ptr signal_func)
1821 #else /* not __MRC__ and not __MWERKS__ */
1822 You lose!!!
1823 #endif /* not __MRC__ and not __MWERKS__ */
1825 if (signal_num != SIGALRM)
1826 return signal (signal_num, signal_func);
1827 else
1829 #ifdef __MRC__
1830 __sigfun old_signal_func;
1831 #elif __MWERKS__
1832 __signal_func_ptr old_signal_func;
1833 #else
1834 You lose!!!
1835 #endif
1836 old_signal_func = alarm_signal_func;
1837 alarm_signal_func = signal_func;
1838 return old_signal_func;
1843 static pascal void
1844 mac_atimer_handler (qlink)
1845 TMTaskPtr qlink;
1847 if (alarm_signal_func)
1848 (alarm_signal_func) (SIGALRM);
1852 static void
1853 set_mac_atimer (count)
1854 long count;
1856 static TimerUPP mac_atimer_handlerUPP = NULL;
1858 if (mac_atimer_handlerUPP == NULL)
1859 mac_atimer_handlerUPP = NewTimerUPP (mac_atimer_handler);
1860 mac_atimer_task.tmCount = 0;
1861 mac_atimer_task.tmAddr = mac_atimer_handlerUPP;
1862 mac_atimer_qlink = (QElemPtr) &mac_atimer_task;
1863 InsTime (mac_atimer_qlink);
1864 if (count)
1865 PrimeTime (mac_atimer_qlink, count);
1870 remove_mac_atimer (remaining_count)
1871 long *remaining_count;
1873 if (mac_atimer_qlink)
1875 RmvTime (mac_atimer_qlink);
1876 if (remaining_count)
1877 *remaining_count = mac_atimer_task.tmCount;
1878 mac_atimer_qlink = NULL;
1880 return 0;
1882 else
1883 return -1;
1888 sigblock (int mask)
1890 int old_mask = signal_mask;
1892 signal_mask |= mask;
1894 if ((old_mask ^ signal_mask) & sigmask (SIGALRM))
1895 remove_mac_atimer (NULL);
1897 return old_mask;
1902 sigsetmask (int mask)
1904 int old_mask = signal_mask;
1906 signal_mask = mask;
1908 if ((old_mask ^ signal_mask) & sigmask (SIGALRM))
1909 if (signal_mask & sigmask (SIGALRM))
1910 remove_mac_atimer (NULL);
1911 else
1912 set_mac_atimer (mac_atimer_task.tmCount);
1914 return old_mask;
1919 alarm (int seconds)
1921 long remaining_count;
1923 if (remove_mac_atimer (&remaining_count) == 0)
1925 set_mac_atimer (seconds * 1000);
1927 return remaining_count / 1000;
1929 else
1931 mac_atimer_task.tmCount = seconds * 1000;
1933 return 0;
1939 setitimer (which, value, ovalue)
1940 int which;
1941 const struct itimerval *value;
1942 struct itimerval *ovalue;
1944 long remaining_count;
1945 long count = (EMACS_SECS (value->it_value) * 1000
1946 + (EMACS_USECS (value->it_value) + 999) / 1000);
1948 if (remove_mac_atimer (&remaining_count) == 0)
1950 if (ovalue)
1952 bzero (ovalue, sizeof (*ovalue));
1953 EMACS_SET_SECS_USECS (ovalue->it_value, remaining_count / 1000,
1954 (remaining_count % 1000) * 1000);
1956 set_mac_atimer (count);
1958 else
1959 mac_atimer_task.tmCount = count;
1961 return 0;
1965 /* gettimeofday should return the amount of time (in a timeval
1966 structure) since midnight today. The toolbox function Microseconds
1967 returns the number of microseconds (in a UnsignedWide value) since
1968 the machine was booted. Also making this complicated is WideAdd,
1969 WideSubtract, etc. take wide values. */
1972 gettimeofday (tp)
1973 struct timeval *tp;
1975 static inited = 0;
1976 static wide wall_clock_at_epoch, clicks_at_epoch;
1977 UnsignedWide uw_microseconds;
1978 wide w_microseconds;
1979 time_t sys_time (time_t *);
1981 /* If this function is called for the first time, record the number
1982 of seconds since midnight and the number of microseconds since
1983 boot at the time of this first call. */
1984 if (!inited)
1986 time_t systime;
1987 inited = 1;
1988 systime = sys_time (NULL);
1989 /* Store microseconds since midnight in wall_clock_at_epoch. */
1990 WideMultiply (systime, 1000000L, &wall_clock_at_epoch);
1991 Microseconds (&uw_microseconds);
1992 /* Store microseconds since boot in clicks_at_epoch. */
1993 clicks_at_epoch.hi = uw_microseconds.hi;
1994 clicks_at_epoch.lo = uw_microseconds.lo;
1997 /* Get time since boot */
1998 Microseconds (&uw_microseconds);
2000 /* Convert to time since midnight*/
2001 w_microseconds.hi = uw_microseconds.hi;
2002 w_microseconds.lo = uw_microseconds.lo;
2003 WideSubtract (&w_microseconds, &clicks_at_epoch);
2004 WideAdd (&w_microseconds, &wall_clock_at_epoch);
2005 tp->tv_sec = WideDivide (&w_microseconds, 1000000L, &tp->tv_usec);
2007 return 0;
2011 #ifdef __MRC__
2012 unsigned int
2013 sleep (unsigned int seconds)
2015 unsigned long time_up;
2016 EventRecord e;
2018 time_up = TickCount () + seconds * 60;
2019 while (TickCount () < time_up)
2021 /* Accept no event; just wait. by T.I. */
2022 WaitNextEvent (0, &e, 30, NULL);
2025 return (0);
2027 #endif /* __MRC__ */
2030 /* The time functions adjust time values according to the difference
2031 between the Unix and CW epoches. */
2033 #undef gmtime
2034 extern struct tm *gmtime (const time_t *);
2035 struct tm *
2036 sys_gmtime (const time_t *timer)
2038 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
2040 return gmtime (&unix_time);
2044 #undef localtime
2045 extern struct tm *localtime (const time_t *);
2046 struct tm *
2047 sys_localtime (const time_t *timer)
2049 #if __MSL__ >= 0x6000
2050 time_t unix_time = *timer;
2051 #else
2052 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
2053 #endif
2055 return localtime (&unix_time);
2059 #undef ctime
2060 extern char *ctime (const time_t *);
2061 char *
2062 sys_ctime (const time_t *timer)
2064 #if __MSL__ >= 0x6000
2065 time_t unix_time = *timer;
2066 #else
2067 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
2068 #endif
2070 return ctime (&unix_time);
2074 #undef time
2075 extern time_t time (time_t *);
2076 time_t
2077 sys_time (time_t *timer)
2079 #if __MSL__ >= 0x6000
2080 time_t mac_time = time (NULL);
2081 #else
2082 time_t mac_time = time (NULL) - CW_OR_MPW_UNIX_EPOCH_DIFF;
2083 #endif
2085 if (timer)
2086 *timer = mac_time;
2088 return mac_time;
2092 /* no subprocesses, empty wait */
2095 wait (int pid)
2097 return 0;
2101 void
2102 croak (char *badfunc)
2104 printf ("%s not yet implemented\r\n", badfunc);
2105 exit (1);
2109 char *
2110 mktemp (char *template)
2112 int len, k;
2113 static seqnum = 0;
2115 len = strlen (template);
2116 k = len - 1;
2117 while (k >= 0 && template[k] == 'X')
2118 k--;
2120 k++; /* make k index of first 'X' */
2122 if (k < len)
2124 /* Zero filled, number of digits equal to the number of X's. */
2125 sprintf (&template[k], "%0*d", len-k, seqnum++);
2127 return template;
2129 else
2130 return 0;
2134 /* Emulate getpwuid, getpwnam and others. */
2136 #define PASSWD_FIELD_SIZE 256
2138 static char my_passwd_name[PASSWD_FIELD_SIZE];
2139 static char my_passwd_dir[MAXPATHLEN+1];
2141 static struct passwd my_passwd =
2143 my_passwd_name,
2144 my_passwd_dir,
2147 static struct group my_group =
2149 /* There are no groups on the mac, so we just return "root" as the
2150 group name. */
2151 "root",
2155 /* Initialized by main () in macterm.c to pathname of emacs directory. */
2157 char emacs_passwd_dir[MAXPATHLEN+1];
2159 char *
2160 getwd (char *);
2162 void
2163 init_emacs_passwd_dir ()
2165 int found = false;
2167 if (getwd (emacs_passwd_dir) && getwd (my_passwd_dir))
2169 /* Need pathname of first ancestor that begins with "emacs"
2170 since Mac emacs application is somewhere in the emacs-*
2171 tree. */
2172 int len = strlen (emacs_passwd_dir);
2173 int j = len - 1;
2174 /* j points to the "/" following the directory name being
2175 compared. */
2176 int i = j - 1;
2177 while (i >= 0 && !found)
2179 while (i >= 0 && emacs_passwd_dir[i] != '/')
2180 i--;
2181 if (emacs_passwd_dir[i] == '/' && i+5 < len)
2182 found = (strncmp (&(emacs_passwd_dir[i+1]), "emacs", 5) == 0);
2183 if (found)
2184 emacs_passwd_dir[j+1] = '\0';
2185 else
2187 j = i;
2188 i = j - 1;
2193 if (!found)
2195 /* Setting to "/" probably won't work but set it to something
2196 anyway. */
2197 strcpy (emacs_passwd_dir, "/");
2198 strcpy (my_passwd_dir, "/");
2203 static struct passwd emacs_passwd =
2205 "emacs",
2206 emacs_passwd_dir,
2209 static int my_passwd_inited = 0;
2212 static void
2213 init_my_passwd ()
2215 char **owner_name;
2217 /* Note: my_passwd_dir initialized in int_emacs_passwd_dir to
2218 directory where Emacs was started. */
2220 owner_name = (char **) GetResource ('STR ',-16096);
2221 if (owner_name)
2223 HLock (owner_name);
2224 BlockMove ((unsigned char *) *owner_name,
2225 (unsigned char *) my_passwd_name,
2226 *owner_name[0]+1);
2227 HUnlock (owner_name);
2228 p2cstr ((unsigned char *) my_passwd_name);
2230 else
2231 my_passwd_name[0] = 0;
2235 struct passwd *
2236 getpwuid (uid_t uid)
2238 if (!my_passwd_inited)
2240 init_my_passwd ();
2241 my_passwd_inited = 1;
2244 return &my_passwd;
2248 struct group *
2249 getgrgid (gid_t gid)
2251 return &my_group;
2255 struct passwd *
2256 getpwnam (const char *name)
2258 if (strcmp (name, "emacs") == 0)
2259 return &emacs_passwd;
2261 if (!my_passwd_inited)
2263 init_my_passwd ();
2264 my_passwd_inited = 1;
2267 return &my_passwd;
2271 /* The functions fork, kill, sigsetmask, sigblock, request_sigio,
2272 setpgrp, setpriority, and unrequest_sigio are defined to be empty
2273 as in msdos.c. */
2277 fork ()
2279 return -1;
2284 kill (int x, int y)
2286 return -1;
2290 void
2291 sys_subshell ()
2293 error ("Can't spawn subshell");
2297 void
2298 request_sigio (void)
2303 void
2304 unrequest_sigio (void)
2310 setpgrp ()
2312 return 0;
2316 /* No pipes yet. */
2319 pipe (int _fildes[2])
2321 errno = EACCES;
2322 return -1;
2326 /* Hard and symbolic links. */
2329 symlink (const char *name1, const char *name2)
2331 errno = ENOENT;
2332 return -1;
2337 link (const char *name1, const char *name2)
2339 errno = ENOENT;
2340 return -1;
2343 #endif /* ! MAC_OSX */
2345 /* Determine the path name of the file specified by VREFNUM, DIRID,
2346 and NAME and place that in the buffer PATH of length
2347 MAXPATHLEN. */
2349 path_from_vol_dir_name (char *path, int man_path_len, short vol_ref_num,
2350 long dir_id, ConstStr255Param name)
2352 Str255 dir_name;
2353 CInfoPBRec cipb;
2354 OSErr err;
2356 if (strlen (name) > man_path_len)
2357 return 0;
2359 memcpy (dir_name, name, name[0]+1);
2360 memcpy (path, name, name[0]+1);
2361 p2cstr (path);
2363 cipb.dirInfo.ioDrParID = dir_id;
2364 cipb.dirInfo.ioNamePtr = dir_name;
2368 cipb.dirInfo.ioVRefNum = vol_ref_num;
2369 cipb.dirInfo.ioFDirIndex = -1;
2370 cipb.dirInfo.ioDrDirID = cipb.dirInfo.ioDrParID;
2371 /* go up to parent each time */
2373 err = PBGetCatInfo (&cipb, false);
2374 if (err != noErr)
2375 return 0;
2377 p2cstr (dir_name);
2378 if (strlen (dir_name) + strlen (path) + 1 >= man_path_len)
2379 return 0;
2381 strcat (dir_name, ":");
2382 strcat (dir_name, path);
2383 /* attach to front since we're going up directory tree */
2384 strcpy (path, dir_name);
2386 while (cipb.dirInfo.ioDrDirID != fsRtDirID);
2387 /* stop when we see the volume's root directory */
2389 return 1; /* success */
2393 OSErr
2394 posix_pathname_to_fsspec (ufn, fs)
2395 const char *ufn;
2396 FSSpec *fs;
2398 Str255 mac_pathname;
2400 if (posix_to_mac_pathname (ufn, mac_pathname, sizeof (mac_pathname)) == 0)
2401 return fnfErr;
2402 else
2404 c2pstr (mac_pathname);
2405 return FSMakeFSSpec (0, 0, mac_pathname, fs);
2409 OSErr
2410 fsspec_to_posix_pathname (fs, ufn, ufnbuflen)
2411 const FSSpec *fs;
2412 char *ufn;
2413 int ufnbuflen;
2415 char mac_pathname[MAXPATHLEN];
2417 if (path_from_vol_dir_name (mac_pathname, sizeof (mac_pathname) - 1,
2418 fs->vRefNum, fs->parID, fs->name)
2419 && mac_to_posix_pathname (mac_pathname, ufn, ufnbuflen))
2420 return noErr;
2421 else
2422 return fnfErr;
2425 #ifndef MAC_OSX
2428 readlink (const char *path, char *buf, int bufsiz)
2430 char mac_sym_link_name[MAXPATHLEN+1];
2431 OSErr err;
2432 FSSpec fsspec;
2433 Boolean target_is_folder, was_aliased;
2434 Str255 directory_name, mac_pathname;
2435 CInfoPBRec cipb;
2437 if (posix_to_mac_pathname (path, mac_sym_link_name, MAXPATHLEN+1) == 0)
2438 return -1;
2440 c2pstr (mac_sym_link_name);
2441 err = FSMakeFSSpec (0, 0, mac_sym_link_name, &fsspec);
2442 if (err != noErr)
2444 errno = ENOENT;
2445 return -1;
2448 err = ResolveAliasFile (&fsspec, true, &target_is_folder, &was_aliased);
2449 if (err != noErr || !was_aliased)
2451 errno = ENOENT;
2452 return -1;
2455 if (path_from_vol_dir_name (mac_pathname, 255, fsspec.vRefNum, fsspec.parID,
2456 fsspec.name) == 0)
2458 errno = ENOENT;
2459 return -1;
2462 if (mac_to_posix_pathname (mac_pathname, buf, bufsiz) == 0)
2464 errno = ENOENT;
2465 return -1;
2468 return strlen (buf);
2472 /* Convert a path to one with aliases fully expanded. */
2474 static int
2475 find_true_pathname (const char *path, char *buf, int bufsiz)
2477 char *q, temp[MAXPATHLEN+1];
2478 const char *p;
2479 int len;
2481 if (bufsiz <= 0 || path == 0 || path[0] == '\0')
2482 return -1;
2484 buf[0] = '\0';
2486 p = path;
2487 if (*p == '/')
2488 q = strchr (p + 1, '/');
2489 else
2490 q = strchr (p, '/');
2491 len = 0; /* loop may not be entered, e.g., for "/" */
2493 while (q)
2495 strcpy (temp, buf);
2496 strncat (temp, p, q - p);
2497 len = readlink (temp, buf, bufsiz);
2498 if (len <= -1)
2500 if (strlen (temp) + 1 > bufsiz)
2501 return -1;
2502 strcpy (buf, temp);
2504 strcat (buf, "/");
2505 len++;
2506 p = q + 1;
2507 q = strchr(p, '/');
2510 if (len + strlen (p) + 1 >= bufsiz)
2511 return -1;
2513 strcat (buf, p);
2514 return len + strlen (p);
2518 mode_t
2519 umask (mode_t numask)
2521 static mode_t mask = 022;
2522 mode_t oldmask = mask;
2523 mask = numask;
2524 return oldmask;
2529 chmod (const char *path, mode_t mode)
2531 /* say it always succeed for now */
2532 return 0;
2537 fchmod (int fd, mode_t mode)
2539 /* say it always succeed for now */
2540 return 0;
2545 fchown (int fd, uid_t owner, gid_t group)
2547 /* say it always succeed for now */
2548 return 0;
2553 dup (int oldd)
2555 #ifdef __MRC__
2556 return fcntl (oldd, F_DUPFD, 0);
2557 #elif __MWERKS__
2558 /* current implementation of fcntl in fcntl.mac.c simply returns old
2559 descriptor */
2560 return fcntl (oldd, F_DUPFD);
2561 #else
2562 You lose!!!
2563 #endif
2567 /* This is from the original sysdep.c. Emulate BSD dup2. First close
2568 newd if it already exists. Then, attempt to dup oldd. If not
2569 successful, call dup2 recursively until we are, then close the
2570 unsuccessful ones. */
2573 dup2 (int oldd, int newd)
2575 int fd, ret;
2577 close (newd);
2579 fd = dup (oldd);
2580 if (fd == -1)
2581 return -1;
2582 if (fd == newd)
2583 return newd;
2584 ret = dup2 (oldd, newd);
2585 close (fd);
2586 return ret;
2590 /* let it fail for now */
2592 char *
2593 sbrk (int incr)
2595 return (char *) -1;
2600 fsync (int fd)
2602 return 0;
2607 ioctl (int d, int request, void *argp)
2609 return -1;
2613 #ifdef __MRC__
2615 isatty (int fildes)
2617 if (fildes >=0 && fildes <= 2)
2618 return 1;
2619 else
2620 return 0;
2625 getgid ()
2627 return 100;
2632 getegid ()
2634 return 100;
2639 getuid ()
2641 return 200;
2646 geteuid ()
2648 return 200;
2650 #endif /* __MRC__ */
2653 #ifdef __MWERKS__
2654 #if __MSL__ < 0x6000
2655 #undef getpid
2657 getpid ()
2659 return 9999;
2661 #endif
2662 #endif /* __MWERKS__ */
2664 #endif /* ! MAC_OSX */
2667 /* Return the path to the directory in which Emacs can create
2668 temporary files. The MacOS "temporary items" directory cannot be
2669 used because it removes the file written by a process when it
2670 exits. In that sense it's more like "/dev/null" than "/tmp" (but
2671 again not exactly). And of course Emacs needs to read back the
2672 files written by its subprocesses. So here we write the files to a
2673 directory "Emacs" in the Preferences Folder. This directory is
2674 created if it does not exist. */
2676 char *
2677 get_temp_dir_name ()
2679 static char *temp_dir_name = NULL;
2680 short vol_ref_num;
2681 long dir_id;
2682 OSErr err;
2683 Str255 dir_name, full_path;
2684 CInfoPBRec cpb;
2685 char unix_dir_name[MAXPATHLEN+1];
2686 DIR *dir;
2688 /* Cache directory name with pointer temp_dir_name.
2689 Look for it only the first time. */
2690 if (!temp_dir_name)
2692 err = FindFolder (kOnSystemDisk, kPreferencesFolderType, kCreateFolder,
2693 &vol_ref_num, &dir_id);
2694 if (err != noErr)
2695 return NULL;
2697 if (!path_from_vol_dir_name (full_path, 255, vol_ref_num, dir_id, "\p"))
2698 return NULL;
2700 if (strlen (full_path) + 6 <= MAXPATHLEN)
2701 strcat (full_path, "Emacs:");
2702 else
2703 return NULL;
2705 if (!mac_to_posix_pathname (full_path, unix_dir_name, MAXPATHLEN+1))
2706 return NULL;
2708 dir = opendir (unix_dir_name); /* check whether temp directory exists */
2709 if (dir)
2710 closedir (dir);
2711 else if (mkdir (unix_dir_name, 0700) != 0) /* create it if not */
2712 return NULL;
2714 temp_dir_name = (char *) malloc (strlen (unix_dir_name) + 1);
2715 strcpy (temp_dir_name, unix_dir_name);
2718 return temp_dir_name;
2721 #ifndef MAC_OSX
2723 /* Allocate and construct an array of pointers to strings from a list
2724 of strings stored in a 'STR#' resource. The returned pointer array
2725 is stored in the style of argv and environ: if the 'STR#' resource
2726 contains numString strings, a pointer array with numString+1
2727 elements is returned in which the last entry contains a null
2728 pointer. The pointer to the pointer array is passed by pointer in
2729 parameter t. The resource ID of the 'STR#' resource is passed in
2730 parameter StringListID.
2733 void
2734 get_string_list (char ***t, short string_list_id)
2736 Handle h;
2737 Ptr p;
2738 int i, num_strings;
2740 h = GetResource ('STR#', string_list_id);
2741 if (h)
2743 HLock (h);
2744 p = *h;
2745 num_strings = * (short *) p;
2746 p += sizeof(short);
2747 *t = (char **) malloc (sizeof (char *) * (num_strings + 1));
2748 for (i = 0; i < num_strings; i++)
2750 short length = *p++;
2751 (*t)[i] = (char *) malloc (length + 1);
2752 strncpy ((*t)[i], p, length);
2753 (*t)[i][length] = '\0';
2754 p += length;
2756 (*t)[num_strings] = 0;
2757 HUnlock (h);
2759 else
2761 /* Return no string in case GetResource fails. Bug fixed by
2762 Ikegami Tsutomu. Caused MPW build to crash without sym -on
2763 option (no sym -on implies -opt local). */
2764 *t = (char **) malloc (sizeof (char *));
2765 (*t)[0] = 0;
2770 static char *
2771 get_path_to_system_folder ()
2773 short vol_ref_num;
2774 long dir_id;
2775 OSErr err;
2776 Str255 dir_name, full_path;
2777 CInfoPBRec cpb;
2778 static char system_folder_unix_name[MAXPATHLEN+1];
2779 DIR *dir;
2781 err = FindFolder (kOnSystemDisk, kSystemFolderType, kDontCreateFolder,
2782 &vol_ref_num, &dir_id);
2783 if (err != noErr)
2784 return NULL;
2786 if (!path_from_vol_dir_name (full_path, 255, vol_ref_num, dir_id, "\p"))
2787 return NULL;
2789 if (!mac_to_posix_pathname (full_path, system_folder_unix_name,
2790 MAXPATHLEN+1))
2791 return NULL;
2793 return system_folder_unix_name;
2797 char **environ;
2799 #define ENVIRON_STRING_LIST_ID 128
2801 /* Get environment variable definitions from STR# resource. */
2803 void
2804 init_environ ()
2806 int i;
2808 get_string_list (&environ, ENVIRON_STRING_LIST_ID);
2810 i = 0;
2811 while (environ[i])
2812 i++;
2814 /* Make HOME directory the one Emacs starts up in if not specified
2815 by resource. */
2816 if (getenv ("HOME") == NULL)
2818 environ = (char **) realloc (environ, sizeof (char *) * (i + 2));
2819 if (environ)
2821 environ[i] = (char *) malloc (strlen (my_passwd_dir) + 6);
2822 if (environ[i])
2824 strcpy (environ[i], "HOME=");
2825 strcat (environ[i], my_passwd_dir);
2827 environ[i+1] = 0;
2828 i++;
2832 /* Make HOME directory the one Emacs starts up in if not specified
2833 by resource. */
2834 if (getenv ("MAIL") == NULL)
2836 environ = (char **) realloc (environ, sizeof (char *) * (i + 2));
2837 if (environ)
2839 char * path_to_system_folder = get_path_to_system_folder ();
2840 environ[i] = (char *) malloc (strlen (path_to_system_folder) + 22);
2841 if (environ[i])
2843 strcpy (environ[i], "MAIL=");
2844 strcat (environ[i], path_to_system_folder);
2845 strcat (environ[i], "Eudora Folder/In");
2847 environ[i+1] = 0;
2853 /* Return the value of the environment variable NAME. */
2855 char *
2856 getenv (const char *name)
2858 int length = strlen(name);
2859 char **e;
2861 for (e = environ; *e != 0; e++)
2862 if (strncmp(*e, name, length) == 0 && (*e)[length] == '=')
2863 return &(*e)[length + 1];
2865 if (strcmp (name, "TMPDIR") == 0)
2866 return get_temp_dir_name ();
2868 return 0;
2872 #ifdef __MRC__
2873 /* see Interfaces&Libraries:Interfaces:CIncludes:signal.h */
2874 char *sys_siglist[] =
2876 "Zero is not a signal!!!",
2877 "Abort", /* 1 */
2878 "Interactive user interrupt", /* 2 */ "?",
2879 "Floating point exception", /* 4 */ "?", "?", "?",
2880 "Illegal instruction", /* 8 */ "?", "?", "?", "?", "?", "?", "?",
2881 "Segment violation", /* 16 */ "?", "?", "?", "?", "?", "?", "?",
2882 "?", "?", "?", "?", "?", "?", "?", "?",
2883 "Terminal" /* 32 */
2885 #elif __MWERKS__
2886 char *sys_siglist[] =
2888 "Zero is not a signal!!!",
2889 "Abort",
2890 "Floating point exception",
2891 "Illegal instruction",
2892 "Interactive user interrupt",
2893 "Segment violation",
2894 "Terminal"
2896 #else /* not __MRC__ and not __MWERKS__ */
2897 You lose!!!
2898 #endif /* not __MRC__ and not __MWERKS__ */
2901 #include <utsname.h>
2904 uname (struct utsname *name)
2906 char **system_name;
2907 system_name = GetString (-16413); /* IM - Resource Manager Reference */
2908 if (system_name)
2910 BlockMove (*system_name, name->nodename, (*system_name)[0]+1);
2911 p2cstr (name->nodename);
2912 return 0;
2914 else
2915 return -1;
2919 /* Event class of HLE sent to subprocess. */
2920 const OSType kEmacsSubprocessSend = 'ESND';
2922 /* Event class of HLE sent back from subprocess. */
2923 const OSType kEmacsSubprocessReply = 'ERPY';
2926 char *
2927 mystrchr (char *s, char c)
2929 while (*s && *s != c)
2931 if (*s == '\\')
2932 s++;
2933 s++;
2936 if (*s)
2938 *s = '\0';
2939 return s;
2941 else
2942 return NULL;
2946 char *
2947 mystrtok (char *s)
2949 while (*s)
2950 s++;
2952 return s + 1;
2956 void
2957 mystrcpy (char *to, char *from)
2959 while (*from)
2961 if (*from == '\\')
2962 from++;
2963 *to++ = *from++;
2965 *to = '\0';
2969 /* Start a Mac subprocess. Arguments for it is passed in argv (null
2970 terminated). The process should run with the default directory
2971 "workdir", read input from "infn", and write output and error to
2972 "outfn" and "errfn", resp. The Process Manager call
2973 LaunchApplication is used to start the subprocess. We use high
2974 level events as the mechanism to pass arguments to the subprocess
2975 and to make Emacs wait for the subprocess to terminate and pass
2976 back a result code. The bulk of the code here packs the arguments
2977 into one message to be passed together with the high level event.
2978 Emacs also sometimes starts a subprocess using a shell to perform
2979 wildcard filename expansion. Since we don't really have a shell on
2980 the Mac, this case is detected and the starting of the shell is
2981 by-passed. We really need to add code here to do filename
2982 expansion to support such functionality. */
2985 run_mac_command (argv, workdir, infn, outfn, errfn)
2986 unsigned char **argv;
2987 const char *workdir;
2988 const char *infn, *outfn, *errfn;
2990 #if TARGET_API_MAC_CARBON
2991 return -1;
2992 #else /* not TARGET_API_MAC_CARBON */
2993 char macappname[MAXPATHLEN+1], macworkdir[MAXPATHLEN+1];
2994 char macinfn[MAXPATHLEN+1], macoutfn[MAXPATHLEN+1], macerrfn[MAXPATHLEN+1];
2995 int paramlen, argc, newargc, j, retries;
2996 char **newargv, *param, *p;
2997 OSErr iErr;
2998 FSSpec spec;
2999 LaunchParamBlockRec lpbr;
3000 EventRecord send_event, reply_event;
3001 RgnHandle cursor_region_handle;
3002 TargetID targ;
3003 unsigned long ref_con, len;
3005 if (posix_to_mac_pathname (workdir, macworkdir, MAXPATHLEN+1) == 0)
3006 return -1;
3007 if (posix_to_mac_pathname (infn, macinfn, MAXPATHLEN+1) == 0)
3008 return -1;
3009 if (posix_to_mac_pathname (outfn, macoutfn, MAXPATHLEN+1) == 0)
3010 return -1;
3011 if (posix_to_mac_pathname (errfn, macerrfn, MAXPATHLEN+1) == 0)
3012 return -1;
3014 paramlen = strlen (macworkdir) + strlen (macinfn) + strlen (macoutfn)
3015 + strlen (macerrfn) + 4; /* count nulls at end of strings */
3017 argc = 0;
3018 while (argv[argc])
3019 argc++;
3021 if (argc == 0)
3022 return -1;
3024 /* If a subprocess is invoked with a shell, we receive 3 arguments
3025 of the form: "<path to emacs bins>/sh" "-c" "<path to emacs
3026 bins>/<command> <command args>" */
3027 j = strlen (argv[0]);
3028 if (j >= 3 && strcmp (argv[0]+j-3, "/sh") == 0
3029 && argc == 3 && strcmp (argv[1], "-c") == 0)
3031 char *command, *t, tempmacpathname[MAXPATHLEN+1];
3033 /* The arguments for the command in argv[2] are separated by
3034 spaces. Count them and put the count in newargc. */
3035 command = (char *) alloca (strlen (argv[2])+2);
3036 strcpy (command, argv[2]);
3037 if (command[strlen (command) - 1] != ' ')
3038 strcat (command, " ");
3040 t = command;
3041 newargc = 0;
3042 t = mystrchr (t, ' ');
3043 while (t)
3045 newargc++;
3046 t = mystrchr (t+1, ' ');
3049 newargv = (char **) alloca (sizeof (char *) * newargc);
3051 t = command;
3052 for (j = 0; j < newargc; j++)
3054 newargv[j] = (char *) alloca (strlen (t) + 1);
3055 mystrcpy (newargv[j], t);
3057 t = mystrtok (t);
3058 paramlen += strlen (newargv[j]) + 1;
3061 if (strncmp (newargv[0], "~emacs/", 7) == 0)
3063 if (posix_to_mac_pathname (newargv[0], tempmacpathname, MAXPATHLEN+1)
3064 == 0)
3065 return -1;
3067 else
3068 { /* sometimes Emacs call "sh" without a path for the command */
3069 #if 0
3070 char *t = (char *) alloca (strlen (newargv[0]) + 7 + 1);
3071 strcpy (t, "~emacs/");
3072 strcat (t, newargv[0]);
3073 #endif /* 0 */
3074 Lisp_Object path;
3075 openp (Vexec_path, build_string (newargv[0]), Vexec_suffixes, &path,
3076 make_number (X_OK));
3078 if (NILP (path))
3079 return -1;
3080 if (posix_to_mac_pathname (SDATA (path), tempmacpathname,
3081 MAXPATHLEN+1) == 0)
3082 return -1;
3084 strcpy (macappname, tempmacpathname);
3086 else
3088 if (posix_to_mac_pathname (argv[0], macappname, MAXPATHLEN+1) == 0)
3089 return -1;
3091 newargv = (char **) alloca (sizeof (char *) * argc);
3092 newargc = argc;
3093 for (j = 1; j < argc; j++)
3095 if (strncmp (argv[j], "~emacs/", 7) == 0)
3097 char *t = strchr (argv[j], ' ');
3098 if (t)
3100 char tempcmdname[MAXPATHLEN+1], tempmaccmdname[MAXPATHLEN+1];
3101 strncpy (tempcmdname, argv[j], t-argv[j]);
3102 tempcmdname[t-argv[j]] = '\0';
3103 if (posix_to_mac_pathname (tempcmdname, tempmaccmdname,
3104 MAXPATHLEN+1) == 0)
3105 return -1;
3106 newargv[j] = (char *) alloca (strlen (tempmaccmdname)
3107 + strlen (t) + 1);
3108 strcpy (newargv[j], tempmaccmdname);
3109 strcat (newargv[j], t);
3111 else
3113 char tempmaccmdname[MAXPATHLEN+1];
3114 if (posix_to_mac_pathname (argv[j], tempmaccmdname,
3115 MAXPATHLEN+1) == 0)
3116 return -1;
3117 newargv[j] = (char *) alloca (strlen (tempmaccmdname)+1);
3118 strcpy (newargv[j], tempmaccmdname);
3121 else
3122 newargv[j] = argv[j];
3123 paramlen += strlen (newargv[j]) + 1;
3127 /* After expanding all the arguments, we now know the length of the
3128 parameter block to be sent to the subprocess as a message
3129 attached to the HLE. */
3130 param = (char *) malloc (paramlen + 1);
3131 if (!param)
3132 return -1;
3134 p = param;
3135 *p++ = newargc;
3136 /* first byte of message contains number of arguments for command */
3137 strcpy (p, macworkdir);
3138 p += strlen (macworkdir);
3139 *p++ = '\0';
3140 /* null terminate strings sent so it's possible to use strcpy over there */
3141 strcpy (p, macinfn);
3142 p += strlen (macinfn);
3143 *p++ = '\0';
3144 strcpy (p, macoutfn);
3145 p += strlen (macoutfn);
3146 *p++ = '\0';
3147 strcpy (p, macerrfn);
3148 p += strlen (macerrfn);
3149 *p++ = '\0';
3150 for (j = 1; j < newargc; j++)
3152 strcpy (p, newargv[j]);
3153 p += strlen (newargv[j]);
3154 *p++ = '\0';
3157 c2pstr (macappname);
3159 iErr = FSMakeFSSpec (0, 0, macappname, &spec);
3161 if (iErr != noErr)
3163 free (param);
3164 return -1;
3167 lpbr.launchBlockID = extendedBlock;
3168 lpbr.launchEPBLength = extendedBlockLen;
3169 lpbr.launchControlFlags = launchContinue + launchNoFileFlags;
3170 lpbr.launchAppSpec = &spec;
3171 lpbr.launchAppParameters = NULL;
3173 iErr = LaunchApplication (&lpbr); /* call the subprocess */
3174 if (iErr != noErr)
3176 free (param);
3177 return -1;
3180 send_event.what = kHighLevelEvent;
3181 send_event.message = kEmacsSubprocessSend;
3182 /* Event ID stored in "where" unused */
3184 retries = 3;
3185 /* OS may think current subprocess has terminated if previous one
3186 terminated recently. */
3189 iErr = PostHighLevelEvent (&send_event, &lpbr.launchProcessSN, 0, param,
3190 paramlen + 1, receiverIDisPSN);
3192 while (iErr == sessClosedErr && retries-- > 0);
3194 if (iErr != noErr)
3196 free (param);
3197 return -1;
3200 cursor_region_handle = NewRgn ();
3202 /* Wait for the subprocess to finish, when it will send us a ERPY
3203 high level event. */
3204 while (1)
3205 if (WaitNextEvent (highLevelEventMask, &reply_event, 180,
3206 cursor_region_handle)
3207 && reply_event.message == kEmacsSubprocessReply)
3208 break;
3210 /* The return code is sent through the refCon */
3211 iErr = AcceptHighLevelEvent (&targ, &ref_con, NULL, &len);
3212 if (iErr != noErr)
3214 DisposeHandle ((Handle) cursor_region_handle);
3215 free (param);
3216 return -1;
3219 DisposeHandle ((Handle) cursor_region_handle);
3220 free (param);
3222 return ref_con;
3223 #endif /* not TARGET_API_MAC_CARBON */
3227 DIR *
3228 opendir (const char *dirname)
3230 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
3231 char mac_pathname[MAXPATHLEN+1], vol_name[MAXPATHLEN+1];
3232 DIR *dirp;
3233 CInfoPBRec cipb;
3234 HVolumeParam vpb;
3235 int len, vol_name_len;
3237 if (find_true_pathname (dirname, true_pathname, MAXPATHLEN+1) == -1)
3238 return 0;
3240 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
3241 if (len > -1)
3242 fully_resolved_name[len] = '\0';
3243 else
3244 strcpy (fully_resolved_name, true_pathname);
3246 dirp = (DIR *) malloc (sizeof(DIR));
3247 if (!dirp)
3248 return 0;
3250 /* Handle special case when dirname is "/": sets up for readir to
3251 get all mount volumes. */
3252 if (strcmp (fully_resolved_name, "/") == 0)
3254 dirp->getting_volumes = 1; /* special all mounted volumes DIR struct */
3255 dirp->current_index = 1; /* index for first volume */
3256 return dirp;
3259 /* Handle typical cases: not accessing all mounted volumes. */
3260 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
3261 return 0;
3263 /* Emacs calls opendir without the trailing '/', Mac needs trailing ':' */
3264 len = strlen (mac_pathname);
3265 if (mac_pathname[len - 1] != ':' && len < MAXPATHLEN)
3266 strcat (mac_pathname, ":");
3268 /* Extract volume name */
3269 vol_name_len = strchr (mac_pathname, ':') - mac_pathname;
3270 strncpy (vol_name, mac_pathname, vol_name_len);
3271 vol_name[vol_name_len] = '\0';
3272 strcat (vol_name, ":");
3274 c2pstr (mac_pathname);
3275 cipb.hFileInfo.ioNamePtr = mac_pathname;
3276 /* using full pathname so vRefNum and DirID ignored */
3277 cipb.hFileInfo.ioVRefNum = 0;
3278 cipb.hFileInfo.ioDirID = 0;
3279 cipb.hFileInfo.ioFDirIndex = 0;
3280 /* set to 0 to get information about specific dir or file */
3282 errno = PBGetCatInfo (&cipb, false);
3283 if (errno != noErr)
3285 errno = ENOENT;
3286 return 0;
3289 if (!(cipb.hFileInfo.ioFlAttrib & 0x10)) /* bit 4 = 1 for directories */
3290 return 0; /* not a directory */
3292 dirp->dir_id = cipb.dirInfo.ioDrDirID; /* used later in readdir */
3293 dirp->getting_volumes = 0;
3294 dirp->current_index = 1; /* index for first file/directory */
3296 c2pstr (vol_name);
3297 vpb.ioNamePtr = vol_name;
3298 /* using full pathname so vRefNum and DirID ignored */
3299 vpb.ioVRefNum = 0;
3300 vpb.ioVolIndex = -1;
3301 errno = PBHGetVInfo ((union HParamBlockRec *) &vpb, false);
3302 if (errno != noErr)
3304 errno = ENOENT;
3305 return 0;
3308 dirp->vol_ref_num = vpb.ioVRefNum;
3310 return dirp;
3314 closedir (DIR *dp)
3316 free (dp);
3318 return 0;
3322 struct dirent *
3323 readdir (DIR *dp)
3325 HParamBlockRec hpblock;
3326 CInfoPBRec cipb;
3327 static struct dirent s_dirent;
3328 static Str255 s_name;
3329 int done;
3330 char *p;
3332 /* Handle the root directory containing the mounted volumes. Call
3333 PBHGetVInfo specifying an index to obtain the info for a volume.
3334 PBHGetVInfo returns an error when it receives an index beyond the
3335 last volume, at which time we should return a nil dirent struct
3336 pointer. */
3337 if (dp->getting_volumes)
3339 hpblock.volumeParam.ioNamePtr = s_name;
3340 hpblock.volumeParam.ioVRefNum = 0;
3341 hpblock.volumeParam.ioVolIndex = dp->current_index;
3343 errno = PBHGetVInfo (&hpblock, false);
3344 if (errno != noErr)
3346 errno = ENOENT;
3347 return 0;
3350 p2cstr (s_name);
3351 strcat (s_name, "/"); /* need "/" for stat to work correctly */
3353 dp->current_index++;
3355 s_dirent.d_ino = hpblock.volumeParam.ioVRefNum;
3356 s_dirent.d_name = s_name;
3358 return &s_dirent;
3360 else
3362 cipb.hFileInfo.ioVRefNum = dp->vol_ref_num;
3363 cipb.hFileInfo.ioNamePtr = s_name;
3364 /* location to receive filename returned */
3366 /* return only visible files */
3367 done = false;
3368 while (!done)
3370 cipb.hFileInfo.ioDirID = dp->dir_id;
3371 /* directory ID found by opendir */
3372 cipb.hFileInfo.ioFDirIndex = dp->current_index;
3374 errno = PBGetCatInfo (&cipb, false);
3375 if (errno != noErr)
3377 errno = ENOENT;
3378 return 0;
3381 /* insist on a visible entry */
3382 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* directory? */
3383 done = !(cipb.dirInfo.ioDrUsrWds.frFlags & fInvisible);
3384 else
3385 done = !(cipb.hFileInfo.ioFlFndrInfo.fdFlags & fInvisible);
3387 dp->current_index++;
3390 p2cstr (s_name);
3392 p = s_name;
3393 while (*p)
3395 if (*p == '/')
3396 *p = ':';
3397 p++;
3400 s_dirent.d_ino = cipb.dirInfo.ioDrDirID;
3401 /* value unimportant: non-zero for valid file */
3402 s_dirent.d_name = s_name;
3404 return &s_dirent;
3409 char *
3410 getwd (char *path)
3412 char mac_pathname[MAXPATHLEN+1];
3413 Str255 directory_name;
3414 OSErr errno;
3415 CInfoPBRec cipb;
3417 if (path_from_vol_dir_name (mac_pathname, 255, 0, 0, "\p") == 0)
3418 return NULL;
3420 if (mac_to_posix_pathname (mac_pathname, path, MAXPATHLEN+1) == 0)
3421 return 0;
3422 else
3423 return path;
3426 #endif /* ! MAC_OSX */
3429 void
3430 initialize_applescript ()
3432 AEDesc null_desc;
3433 OSAError osaerror;
3435 /* if open fails, as_scripting_component is set to NULL. Its
3436 subsequent use in OSA calls will fail with badComponentInstance
3437 error. */
3438 as_scripting_component = OpenDefaultComponent (kOSAComponentType,
3439 kAppleScriptSubtype);
3441 null_desc.descriptorType = typeNull;
3442 null_desc.dataHandle = 0;
3443 osaerror = OSAMakeContext (as_scripting_component, &null_desc,
3444 kOSANullScript, &as_script_context);
3445 if (osaerror)
3446 as_script_context = kOSANullScript;
3447 /* use default context if create fails */
3451 void
3452 terminate_applescript()
3454 OSADispose (as_scripting_component, as_script_context);
3455 CloseComponent (as_scripting_component);
3458 /* Convert a lisp string to the 4 byte character code. */
3460 OSType
3461 mac_get_code_from_arg(Lisp_Object arg, OSType defCode)
3463 OSType result;
3464 if (NILP(arg))
3466 result = defCode;
3468 else
3470 /* check type string */
3471 CHECK_STRING(arg);
3472 if (SBYTES (arg) != 4)
3474 error ("Wrong argument: need string of length 4 for code");
3476 result = EndianU32_BtoN (*((UInt32 *) SDATA (arg)));
3478 return result;
3481 /* Convert the 4 byte character code into a 4 byte string. */
3483 Lisp_Object
3484 mac_get_object_from_code(OSType defCode)
3486 UInt32 code = EndianU32_NtoB (defCode);
3488 return make_unibyte_string ((char *)&code, 4);
3492 DEFUN ("mac-get-file-creator", Fmac_get_file_creator, Smac_get_file_creator, 1, 1, 0,
3493 doc: /* Get the creator code of FILENAME as a four character string. */)
3494 (filename)
3495 Lisp_Object filename;
3497 OSErr status;
3498 #ifdef MAC_OSX
3499 FSRef fref;
3500 #else
3501 FSSpec fss;
3502 #endif
3503 OSType cCode;
3504 Lisp_Object result = Qnil;
3505 CHECK_STRING (filename);
3507 if (NILP(Ffile_exists_p(filename)) || !NILP(Ffile_directory_p(filename))) {
3508 return Qnil;
3510 filename = Fexpand_file_name (filename, Qnil);
3512 BLOCK_INPUT;
3513 #ifdef MAC_OSX
3514 status = FSPathMakeRef(SDATA(ENCODE_FILE(filename)), &fref, NULL);
3515 #else
3516 status = posix_pathname_to_fsspec (SDATA (ENCODE_FILE (filename)), &fss);
3517 #endif
3519 if (status == noErr)
3521 #ifdef MAC_OSX
3522 FSCatalogInfo catalogInfo;
3524 status = FSGetCatalogInfo(&fref, kFSCatInfoFinderInfo,
3525 &catalogInfo, NULL, NULL, NULL);
3526 #else
3527 FInfo finder_info;
3529 status = FSpGetFInfo (&fss, &finder_info);
3530 #endif
3531 if (status == noErr)
3533 #ifdef MAC_OSX
3534 result = mac_get_object_from_code(((FileInfo*)&catalogInfo.finderInfo)->fileCreator);
3535 #else
3536 result = mac_get_object_from_code (finder_info.fdCreator);
3537 #endif
3540 UNBLOCK_INPUT;
3541 if (status != noErr) {
3542 error ("Error while getting file information.");
3544 return result;
3547 DEFUN ("mac-get-file-type", Fmac_get_file_type, Smac_get_file_type, 1, 1, 0,
3548 doc: /* Get the type code of FILENAME as a four character string. */)
3549 (filename)
3550 Lisp_Object filename;
3552 OSErr status;
3553 #ifdef MAC_OSX
3554 FSRef fref;
3555 #else
3556 FSSpec fss;
3557 #endif
3558 OSType cCode;
3559 Lisp_Object result = Qnil;
3560 CHECK_STRING (filename);
3562 if (NILP(Ffile_exists_p(filename)) || !NILP(Ffile_directory_p(filename))) {
3563 return Qnil;
3565 filename = Fexpand_file_name (filename, Qnil);
3567 BLOCK_INPUT;
3568 #ifdef MAC_OSX
3569 status = FSPathMakeRef(SDATA(ENCODE_FILE(filename)), &fref, NULL);
3570 #else
3571 status = posix_pathname_to_fsspec (SDATA (ENCODE_FILE (filename)), &fss);
3572 #endif
3574 if (status == noErr)
3576 #ifdef MAC_OSX
3577 FSCatalogInfo catalogInfo;
3579 status = FSGetCatalogInfo(&fref, kFSCatInfoFinderInfo,
3580 &catalogInfo, NULL, NULL, NULL);
3581 #else
3582 FInfo finder_info;
3584 status = FSpGetFInfo (&fss, &finder_info);
3585 #endif
3586 if (status == noErr)
3588 #ifdef MAC_OSX
3589 result = mac_get_object_from_code(((FileInfo*)&catalogInfo.finderInfo)->fileType);
3590 #else
3591 result = mac_get_object_from_code (finder_info.fdType);
3592 #endif
3595 UNBLOCK_INPUT;
3596 if (status != noErr) {
3597 error ("Error while getting file information.");
3599 return result;
3602 DEFUN ("mac-set-file-creator", Fmac_set_file_creator, Smac_set_file_creator, 1, 2, 0,
3603 doc: /* Set creator code of file FILENAME to CODE.
3604 If non-nil, CODE must be a 4-character string. Otherwise, 'EMAx' is
3605 assumed. Return non-nil if successful. */)
3606 (filename, code)
3607 Lisp_Object filename, code;
3609 OSErr status;
3610 #ifdef MAC_OSX
3611 FSRef fref;
3612 #else
3613 FSSpec fss;
3614 #endif
3615 OSType cCode;
3616 CHECK_STRING (filename);
3618 cCode = mac_get_code_from_arg(code, 'EMAx');
3620 if (NILP(Ffile_exists_p(filename)) || !NILP(Ffile_directory_p(filename))) {
3621 return Qnil;
3623 filename = Fexpand_file_name (filename, Qnil);
3625 BLOCK_INPUT;
3626 #ifdef MAC_OSX
3627 status = FSPathMakeRef(SDATA(ENCODE_FILE(filename)), &fref, NULL);
3628 #else
3629 status = posix_pathname_to_fsspec (SDATA (ENCODE_FILE (filename)), &fss);
3630 #endif
3632 if (status == noErr)
3634 #ifdef MAC_OSX
3635 FSCatalogInfo catalogInfo;
3636 FSRef parentDir;
3637 status = FSGetCatalogInfo(&fref, kFSCatInfoFinderInfo,
3638 &catalogInfo, NULL, NULL, &parentDir);
3639 #else
3640 FInfo finder_info;
3642 status = FSpGetFInfo (&fss, &finder_info);
3643 #endif
3644 if (status == noErr)
3646 #ifdef MAC_OSX
3647 ((FileInfo*)&catalogInfo.finderInfo)->fileCreator = cCode;
3648 status = FSSetCatalogInfo(&fref, kFSCatInfoFinderInfo, &catalogInfo);
3649 /* TODO: on Mac OS 10.2, we need to touch the parent dir, FNNotify? */
3650 #else
3651 finder_info.fdCreator = cCode;
3652 status = FSpSetFInfo (&fss, &finder_info);
3653 #endif
3656 UNBLOCK_INPUT;
3657 if (status != noErr) {
3658 error ("Error while setting creator information.");
3660 return Qt;
3663 DEFUN ("mac-set-file-type", Fmac_set_file_type, Smac_set_file_type, 2, 2, 0,
3664 doc: /* Set file code of file FILENAME to CODE.
3665 CODE must be a 4-character string. Return non-nil if successful. */)
3666 (filename, code)
3667 Lisp_Object filename, code;
3669 OSErr status;
3670 #ifdef MAC_OSX
3671 FSRef fref;
3672 #else
3673 FSSpec fss;
3674 #endif
3675 OSType cCode;
3676 CHECK_STRING (filename);
3678 cCode = mac_get_code_from_arg(code, 0); /* Default to empty code*/
3680 if (NILP(Ffile_exists_p(filename)) || !NILP(Ffile_directory_p(filename))) {
3681 return Qnil;
3683 filename = Fexpand_file_name (filename, Qnil);
3685 BLOCK_INPUT;
3686 #ifdef MAC_OSX
3687 status = FSPathMakeRef(SDATA(ENCODE_FILE(filename)), &fref, NULL);
3688 #else
3689 status = posix_pathname_to_fsspec (SDATA (ENCODE_FILE (filename)), &fss);
3690 #endif
3692 if (status == noErr)
3694 #ifdef MAC_OSX
3695 FSCatalogInfo catalogInfo;
3696 FSRef parentDir;
3697 status = FSGetCatalogInfo(&fref, kFSCatInfoFinderInfo,
3698 &catalogInfo, NULL, NULL, &parentDir);
3699 #else
3700 FInfo finder_info;
3702 status = FSpGetFInfo (&fss, &finder_info);
3703 #endif
3704 if (status == noErr)
3706 #ifdef MAC_OSX
3707 ((FileInfo*)&catalogInfo.finderInfo)->fileType = cCode;
3708 status = FSSetCatalogInfo(&fref, kFSCatInfoFinderInfo, &catalogInfo);
3709 /* TODO: on Mac OS 10.2, we need to touch the parent dir, FNNotify? */
3710 #else
3711 finder_info.fdType = cCode;
3712 status = FSpSetFInfo (&fss, &finder_info);
3713 #endif
3716 UNBLOCK_INPUT;
3717 if (status != noErr) {
3718 error ("Error while setting creator information.");
3720 return Qt;
3724 /* Compile and execute the AppleScript SCRIPT and return the error
3725 status as function value. A zero is returned if compilation and
3726 execution is successful, in which case RESULT returns a pointer to
3727 a string containing the resulting script value. Otherwise, the Mac
3728 error code is returned and RESULT returns a pointer to an error
3729 string. In both cases the caller should deallocate the storage
3730 used by the string pointed to by RESULT if it is non-NULL. For
3731 documentation on the MacOS scripting architecture, see Inside
3732 Macintosh - Interapplication Communications: Scripting Components. */
3734 static long
3735 do_applescript (char *script, char **result)
3737 AEDesc script_desc, result_desc, error_desc;
3738 OSErr error;
3739 OSAError osaerror;
3740 long length;
3742 *result = 0;
3744 if (!as_scripting_component)
3745 initialize_applescript();
3747 error = AECreateDesc (typeChar, script, strlen(script), &script_desc);
3748 if (error)
3749 return error;
3751 osaerror = OSADoScript (as_scripting_component, &script_desc, kOSANullScript,
3752 typeChar, kOSAModeNull, &result_desc);
3754 if (osaerror == errOSAScriptError)
3756 /* error executing AppleScript: retrieve error message */
3757 if (!OSAScriptError (as_scripting_component, kOSAErrorMessage, typeChar,
3758 &error_desc))
3760 #if TARGET_API_MAC_CARBON
3761 length = AEGetDescDataSize (&error_desc);
3762 *result = (char *) xmalloc (length + 1);
3763 if (*result)
3765 AEGetDescData (&error_desc, *result, length);
3766 *(*result + length) = '\0';
3768 #else /* not TARGET_API_MAC_CARBON */
3769 HLock (error_desc.dataHandle);
3770 length = GetHandleSize(error_desc.dataHandle);
3771 *result = (char *) xmalloc (length + 1);
3772 if (*result)
3774 memcpy (*result, *(error_desc.dataHandle), length);
3775 *(*result + length) = '\0';
3777 HUnlock (error_desc.dataHandle);
3778 #endif /* not TARGET_API_MAC_CARBON */
3779 AEDisposeDesc (&error_desc);
3782 else if (osaerror == noErr) /* success: retrieve resulting script value */
3784 #if TARGET_API_MAC_CARBON
3785 length = AEGetDescDataSize (&result_desc);
3786 *result = (char *) xmalloc (length + 1);
3787 if (*result)
3789 AEGetDescData (&result_desc, *result, length);
3790 *(*result + length) = '\0';
3792 #else /* not TARGET_API_MAC_CARBON */
3793 HLock (result_desc.dataHandle);
3794 length = GetHandleSize(result_desc.dataHandle);
3795 *result = (char *) xmalloc (length + 1);
3796 if (*result)
3798 memcpy (*result, *(result_desc.dataHandle), length);
3799 *(*result + length) = '\0';
3801 HUnlock (result_desc.dataHandle);
3802 #endif /* not TARGET_API_MAC_CARBON */
3803 AEDisposeDesc (&result_desc);
3806 AEDisposeDesc (&script_desc);
3808 return osaerror;
3812 DEFUN ("do-applescript", Fdo_applescript, Sdo_applescript, 1, 1, 0,
3813 doc: /* Compile and execute AppleScript SCRIPT and return the result.
3814 If compilation and execution are successful, the resulting script
3815 value is returned as a string. Otherwise the function aborts and
3816 displays the error message returned by the AppleScript scripting
3817 component. */)
3818 (script)
3819 Lisp_Object script;
3821 char *result, *temp;
3822 Lisp_Object lisp_result;
3823 long status;
3825 CHECK_STRING (script);
3827 BLOCK_INPUT;
3828 status = do_applescript (SDATA (script), &result);
3829 UNBLOCK_INPUT;
3830 if (status)
3832 if (!result)
3833 error ("AppleScript error %d", status);
3834 else
3836 /* Unfortunately only OSADoScript in do_applescript knows how
3837 how large the resulting script value or error message is
3838 going to be and therefore as caller memory must be
3839 deallocated here. It is necessary to free the error
3840 message before calling error to avoid a memory leak. */
3841 temp = (char *) alloca (strlen (result) + 1);
3842 strcpy (temp, result);
3843 xfree (result);
3844 error (temp);
3847 else
3849 lisp_result = build_string (result);
3850 xfree (result);
3851 return lisp_result;
3856 DEFUN ("mac-file-name-to-posix", Fmac_file_name_to_posix,
3857 Smac_file_name_to_posix, 1, 1, 0,
3858 doc: /* Convert Macintosh FILENAME to Posix form. */)
3859 (filename)
3860 Lisp_Object filename;
3862 char posix_filename[MAXPATHLEN+1];
3864 CHECK_STRING (filename);
3866 if (mac_to_posix_pathname (SDATA (filename), posix_filename, MAXPATHLEN))
3867 return build_string (posix_filename);
3868 else
3869 return Qnil;
3873 DEFUN ("posix-file-name-to-mac", Fposix_file_name_to_mac,
3874 Sposix_file_name_to_mac, 1, 1, 0,
3875 doc: /* Convert Posix FILENAME to Mac form. */)
3876 (filename)
3877 Lisp_Object filename;
3879 char mac_filename[MAXPATHLEN+1];
3881 CHECK_STRING (filename);
3883 if (posix_to_mac_pathname (SDATA (filename), mac_filename, MAXPATHLEN))
3884 return build_string (mac_filename);
3885 else
3886 return Qnil;
3890 #if TARGET_API_MAC_CARBON
3891 static Lisp_Object Qxml, Qmime_charset;
3892 static Lisp_Object QNFD, QNFKD, QNFC, QNFKC, QHFS_plus_D, QHFS_plus_C;
3894 DEFUN ("mac-get-preference", Fmac_get_preference, Smac_get_preference, 1, 4, 0,
3895 doc: /* Return the application preference value for KEY.
3896 KEY is either a string specifying a preference key, or a list of key
3897 strings. If it is a list, the (i+1)-th element is used as a key for
3898 the CFDictionary value obtained by the i-th element. Return nil if
3899 lookup is failed at some stage.
3901 Optional arg APPLICATION is an application ID string. If omitted or
3902 nil, that stands for the current application.
3904 Optional arg FORMAT specifies the data format of the return value. If
3905 omitted or nil, each Core Foundation object is converted into a
3906 corresponding Lisp object as follows:
3908 Core Foundation Lisp Tag
3909 ------------------------------------------------------------
3910 CFString Multibyte string string
3911 CFNumber Integer or float number
3912 CFBoolean Symbol (t or nil) boolean
3913 CFDate List of three integers date
3914 (cf. `current-time')
3915 CFData Unibyte string data
3916 CFArray Vector array
3917 CFDictionary Alist or hash table dictionary
3918 (depending on HASH-BOUND)
3920 If it is t, a symbol that represents the type of the original Core
3921 Foundation object is prepended. If it is `xml', the value is returned
3922 as an XML representation.
3924 Optional arg HASH-BOUND specifies which kinds of the list objects,
3925 alists or hash tables, are used as the targets of the conversion from
3926 CFDictionary. If HASH-BOUND is a negative integer or nil, always
3927 generate alists. If HASH-BOUND >= 0, generate an alist if the number
3928 of keys in the dictionary is smaller than HASH-BOUND, and a hash table
3929 otherwise. */)
3930 (key, application, format, hash_bound)
3931 Lisp_Object key, application, format, hash_bound;
3933 CFStringRef app_id, key_str;
3934 CFPropertyListRef app_plist = NULL, plist;
3935 Lisp_Object result = Qnil, tmp;
3937 if (STRINGP (key))
3938 key = Fcons (key, Qnil);
3939 else
3941 CHECK_CONS (key);
3942 for (tmp = key; CONSP (tmp); tmp = XCDR (tmp))
3943 CHECK_STRING_CAR (tmp);
3944 if (!NILP (tmp))
3945 wrong_type_argument (Qlistp, key);
3947 if (!NILP (application))
3948 CHECK_STRING (application);
3949 CHECK_SYMBOL (format);
3950 if (!NILP (hash_bound))
3951 CHECK_NUMBER (hash_bound);
3953 BLOCK_INPUT;
3955 app_id = kCFPreferencesCurrentApplication;
3956 if (!NILP (application))
3958 app_id = cfstring_create_with_string (application);
3959 if (app_id == NULL)
3960 goto out;
3962 key_str = cfstring_create_with_string (XCAR (key));
3963 if (key_str == NULL)
3964 goto out;
3965 app_plist = CFPreferencesCopyAppValue (key_str, app_id);
3966 CFRelease (key_str);
3967 if (app_plist == NULL)
3968 goto out;
3970 plist = app_plist;
3971 for (key = XCDR (key); CONSP (key); key = XCDR (key))
3973 if (CFGetTypeID (plist) != CFDictionaryGetTypeID ())
3974 break;
3975 key_str = cfstring_create_with_string (XCAR (key));
3976 if (key_str == NULL)
3977 goto out;
3978 plist = CFDictionaryGetValue (plist, key_str);
3979 CFRelease (key_str);
3980 if (plist == NULL)
3981 goto out;
3984 if (NILP (key))
3985 if (EQ (format, Qxml))
3987 CFDataRef data = CFPropertyListCreateXMLData (NULL, plist);
3988 if (data == NULL)
3989 goto out;
3990 result = cfdata_to_lisp (data);
3991 CFRelease (data);
3993 else
3994 result =
3995 cfproperty_list_to_lisp (plist, EQ (format, Qt),
3996 NILP (hash_bound) ? -1 : XINT (hash_bound));
3998 out:
3999 if (app_plist)
4000 CFRelease (app_plist);
4001 CFRelease (app_id);
4003 UNBLOCK_INPUT;
4005 return result;
4009 static CFStringEncoding
4010 get_cfstring_encoding_from_lisp (obj)
4011 Lisp_Object obj;
4013 CFStringRef iana_name;
4014 CFStringEncoding encoding = kCFStringEncodingInvalidId;
4016 if (NILP (obj))
4017 return kCFStringEncodingUnicode;
4019 if (INTEGERP (obj))
4020 return XINT (obj);
4022 if (SYMBOLP (obj) && !NILP (Fcoding_system_p (obj)))
4024 Lisp_Object coding_spec, plist;
4026 coding_spec = Fget (obj, Qcoding_system);
4027 plist = XVECTOR (coding_spec)->contents[3];
4028 obj = Fplist_get (XVECTOR (coding_spec)->contents[3], Qmime_charset);
4031 if (SYMBOLP (obj))
4032 obj = SYMBOL_NAME (obj);
4034 if (STRINGP (obj))
4036 iana_name = cfstring_create_with_string (obj);
4037 if (iana_name)
4039 encoding = CFStringConvertIANACharSetNameToEncoding (iana_name);
4040 CFRelease (iana_name);
4044 return encoding;
4047 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
4048 static CFStringRef
4049 cfstring_create_normalized (str, symbol)
4050 CFStringRef str;
4051 Lisp_Object symbol;
4053 int form = -1;
4054 TextEncodingVariant variant;
4055 float initial_mag = 0.0;
4056 CFStringRef result = NULL;
4058 if (EQ (symbol, QNFD))
4059 form = kCFStringNormalizationFormD;
4060 else if (EQ (symbol, QNFKD))
4061 form = kCFStringNormalizationFormKD;
4062 else if (EQ (symbol, QNFC))
4063 form = kCFStringNormalizationFormC;
4064 else if (EQ (symbol, QNFKC))
4065 form = kCFStringNormalizationFormKC;
4066 else if (EQ (symbol, QHFS_plus_D))
4068 variant = kUnicodeHFSPlusDecompVariant;
4069 initial_mag = 1.5;
4071 else if (EQ (symbol, QHFS_plus_C))
4073 variant = kUnicodeHFSPlusCompVariant;
4074 initial_mag = 1.0;
4077 if (form >= 0)
4079 CFMutableStringRef mut_str = CFStringCreateMutableCopy (NULL, 0, str);
4081 if (mut_str)
4083 CFStringNormalize (mut_str, form);
4084 result = mut_str;
4087 else if (initial_mag > 0.0)
4089 UnicodeToTextInfo uni = NULL;
4090 UnicodeMapping map;
4091 CFIndex length;
4092 UniChar *in_text, *buffer = NULL, *out_buf = NULL;
4093 OSErr err = noErr;
4094 ByteCount out_read, out_size, out_len;
4096 map.unicodeEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault,
4097 kUnicodeNoSubset,
4098 kTextEncodingDefaultFormat);
4099 map.otherEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault,
4100 variant,
4101 kTextEncodingDefaultFormat);
4102 map.mappingVersion = kUnicodeUseLatestMapping;
4104 length = CFStringGetLength (str);
4105 out_size = (int)((float)length * initial_mag) * sizeof (UniChar);
4106 if (out_size < 32)
4107 out_size = 32;
4109 in_text = (UniChar *)CFStringGetCharactersPtr (str);
4110 if (in_text == NULL)
4112 buffer = xmalloc (sizeof (UniChar) * length);
4113 if (buffer)
4115 CFStringGetCharacters (str, CFRangeMake (0, length), buffer);
4116 in_text = buffer;
4120 if (in_text)
4121 err = CreateUnicodeToTextInfo(&map, &uni);
4122 while (err == noErr)
4124 out_buf = xmalloc (out_size);
4125 if (out_buf == NULL)
4126 err = mFulErr;
4127 else
4128 err = ConvertFromUnicodeToText (uni, length * sizeof (UniChar),
4129 in_text,
4130 kUnicodeDefaultDirectionMask,
4131 0, NULL, NULL, NULL,
4132 out_size, &out_read, &out_len,
4133 out_buf);
4134 if (err == noErr && out_read < length * sizeof (UniChar))
4136 xfree (out_buf);
4137 out_size += length;
4139 else
4140 break;
4142 if (err == noErr)
4143 result = CFStringCreateWithCharacters (NULL, out_buf,
4144 out_len / sizeof (UniChar));
4145 if (uni)
4146 DisposeUnicodeToTextInfo (&uni);
4147 if (out_buf)
4148 xfree (out_buf);
4149 if (buffer)
4150 xfree (buffer);
4152 else
4154 result = str;
4155 CFRetain (result);
4158 return result;
4160 #endif
4162 DEFUN ("mac-code-convert-string", Fmac_code_convert_string, Smac_code_convert_string, 3, 4, 0,
4163 doc: /* Convert STRING from SOURCE encoding to TARGET encoding.
4164 The conversion is performed using the converter provided by the system.
4165 Each encoding is specified by either a coding system symbol, a mime
4166 charset string, or an integer as a CFStringEncoding value. Nil for
4167 encoding means UTF-16 in native byte order, no byte order mark.
4168 On Mac OS X 10.2 and later, you can do Unicode Normalization by
4169 specifying the optional argument NORMALIZATION-FORM with a symbol NFD,
4170 NFKD, NFC, NFKC, HFS+D, or HFS+C.
4171 On successful conversion, return the result string, else return nil. */)
4172 (string, source, target, normalization_form)
4173 Lisp_Object string, source, target, normalization_form;
4175 Lisp_Object result = Qnil;
4176 CFStringEncoding src_encoding, tgt_encoding;
4177 CFStringRef str = NULL;
4179 CHECK_STRING (string);
4180 if (!INTEGERP (source) && !STRINGP (source))
4181 CHECK_SYMBOL (source);
4182 if (!INTEGERP (target) && !STRINGP (target))
4183 CHECK_SYMBOL (target);
4184 CHECK_SYMBOL (normalization_form);
4186 BLOCK_INPUT;
4188 src_encoding = get_cfstring_encoding_from_lisp (source);
4189 tgt_encoding = get_cfstring_encoding_from_lisp (target);
4191 /* We really want string_to_unibyte, but since it doesn't exist yet, we
4192 use string_as_unibyte which works as well, except for the fact that
4193 it's too permissive (it doesn't check that the multibyte string only
4194 contain single-byte chars). */
4195 string = Fstring_as_unibyte (string);
4196 if (src_encoding != kCFStringEncodingInvalidId
4197 && tgt_encoding != kCFStringEncodingInvalidId)
4198 str = CFStringCreateWithBytes (NULL, SDATA (string), SBYTES (string),
4199 src_encoding, !NILP (source));
4200 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1020
4201 if (str)
4203 CFStringRef saved_str = str;
4205 str = cfstring_create_normalized (saved_str, normalization_form);
4206 CFRelease (saved_str);
4208 #endif
4209 if (str)
4211 CFIndex str_len, buf_len;
4213 str_len = CFStringGetLength (str);
4214 if (CFStringGetBytes (str, CFRangeMake (0, str_len), tgt_encoding, 0,
4215 !NILP (target), NULL, 0, &buf_len) == str_len)
4217 result = make_uninit_string (buf_len);
4218 CFStringGetBytes (str, CFRangeMake (0, str_len), tgt_encoding, 0,
4219 !NILP (target), SDATA (result), buf_len, NULL);
4221 CFRelease (str);
4224 UNBLOCK_INPUT;
4226 return result;
4228 #endif /* TARGET_API_MAC_CARBON */
4231 DEFUN ("mac-clear-font-name-table", Fmac_clear_font_name_table, Smac_clear_font_name_table, 0, 0, 0,
4232 doc: /* Clear the font name table. */)
4235 check_mac ();
4236 mac_clear_font_name_table ();
4237 return Qnil;
4241 static Lisp_Object
4242 mac_get_system_locale ()
4244 OSErr err;
4245 LangCode lang;
4246 RegionCode region;
4247 LocaleRef locale;
4248 Str255 str;
4250 lang = GetScriptVariable (smSystemScript, smScriptLang);
4251 region = GetScriptManagerVariable (smRegionCode);
4252 err = LocaleRefFromLangOrRegionCode (lang, region, &locale);
4253 if (err == noErr)
4254 err = LocaleRefGetPartString (locale, kLocaleAllPartsMask,
4255 sizeof (str), str);
4256 if (err == noErr)
4257 return build_string (str);
4258 else
4259 return Qnil;
4263 #ifdef MAC_OSX
4264 #undef select
4266 extern int inhibit_window_system;
4267 extern int noninteractive;
4269 /* Unlike in X11, window events in Carbon do not come from sockets.
4270 So we cannot simply use `select' to monitor two kinds of inputs:
4271 window events and process outputs. We emulate such functionality
4272 by regarding fd 0 as the window event channel and simultaneously
4273 monitoring both kinds of input channels. It is implemented by
4274 dividing into some cases:
4275 1. The window event channel is not involved.
4276 -> Use `select'.
4277 2. Sockets are not involved.
4278 -> Use ReceiveNextEvent.
4279 3. [If SELECT_USE_CFSOCKET is defined]
4280 Only the window event channel and socket read channels are
4281 involved, and timeout is not too short (greater than
4282 SELECT_TIMEOUT_THRESHHOLD_RUNLOOP seconds).
4283 -> Create CFSocket for each socket and add it into the current
4284 event RunLoop so that a `ready-to-read' event can be posted
4285 to the event queue that is also used for window events. Then
4286 ReceiveNextEvent can wait for both kinds of inputs.
4287 4. Otherwise.
4288 -> Periodically poll the window input channel while repeatedly
4289 executing `select' with a short timeout
4290 (SELECT_POLLING_PERIOD_USEC microseconds). */
4292 #define SELECT_POLLING_PERIOD_USEC 20000
4293 #ifdef SELECT_USE_CFSOCKET
4294 #define SELECT_TIMEOUT_THRESHOLD_RUNLOOP 0.2
4295 #define EVENT_CLASS_SOCK 'Sock'
4297 static void
4298 socket_callback (s, type, address, data, info)
4299 CFSocketRef s;
4300 CFSocketCallBackType type;
4301 CFDataRef address;
4302 const void *data;
4303 void *info;
4305 EventRef event;
4307 CreateEvent (NULL, EVENT_CLASS_SOCK, 0, 0, kEventAttributeNone, &event);
4308 PostEventToQueue (GetCurrentEventQueue (), event, kEventPriorityStandard);
4309 ReleaseEvent (event);
4311 #endif /* SELECT_USE_CFSOCKET */
4313 static int
4314 select_and_poll_event (n, rfds, wfds, efds, timeout)
4315 int n;
4316 SELECT_TYPE *rfds;
4317 SELECT_TYPE *wfds;
4318 SELECT_TYPE *efds;
4319 struct timeval *timeout;
4321 int r;
4322 OSErr err;
4324 r = select (n, rfds, wfds, efds, timeout);
4325 if (r != -1)
4327 BLOCK_INPUT;
4328 err = ReceiveNextEvent (0, NULL, kEventDurationNoWait,
4329 kEventLeaveInQueue, NULL);
4330 UNBLOCK_INPUT;
4331 if (err == noErr)
4333 FD_SET (0, rfds);
4334 r++;
4337 return r;
4340 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1020
4341 #undef SELECT_INVALIDATE_CFSOCKET
4342 #endif
4345 sys_select (n, rfds, wfds, efds, timeout)
4346 int n;
4347 SELECT_TYPE *rfds;
4348 SELECT_TYPE *wfds;
4349 SELECT_TYPE *efds;
4350 struct timeval *timeout;
4352 OSErr err;
4353 int i, r;
4354 EMACS_TIME select_timeout;
4356 if (inhibit_window_system || noninteractive
4357 || rfds == NULL || !FD_ISSET (0, rfds))
4358 return select (n, rfds, wfds, efds, timeout);
4360 FD_CLR (0, rfds);
4362 if (wfds == NULL && efds == NULL)
4364 int nsocks = 0;
4365 SELECT_TYPE orfds = *rfds;
4367 EventTimeout timeout_sec =
4368 (timeout
4369 ? (EMACS_SECS (*timeout) * kEventDurationSecond
4370 + EMACS_USECS (*timeout) * kEventDurationMicrosecond)
4371 : kEventDurationForever);
4373 for (i = 1; i < n; i++)
4374 if (FD_ISSET (i, rfds))
4375 nsocks++;
4377 if (nsocks == 0)
4379 BLOCK_INPUT;
4380 err = ReceiveNextEvent (0, NULL, timeout_sec,
4381 kEventLeaveInQueue, NULL);
4382 UNBLOCK_INPUT;
4383 if (err == noErr)
4385 FD_SET (0, rfds);
4386 return 1;
4388 else
4389 return 0;
4392 /* Avoid initial overhead of RunLoop setup for the case that
4393 some input is already available. */
4394 EMACS_SET_SECS_USECS (select_timeout, 0, 0);
4395 r = select_and_poll_event (n, rfds, wfds, efds, &select_timeout);
4396 if (r != 0 || timeout_sec == 0.0)
4397 return r;
4399 *rfds = orfds;
4401 #ifdef SELECT_USE_CFSOCKET
4402 if (timeout_sec > 0 && timeout_sec <= SELECT_TIMEOUT_THRESHOLD_RUNLOOP)
4403 goto poll_periodically;
4406 CFRunLoopRef runloop =
4407 (CFRunLoopRef) GetCFRunLoopFromEventLoop (GetCurrentEventLoop ());
4408 EventTypeSpec specs[] = {{EVENT_CLASS_SOCK, 0}};
4409 #ifdef SELECT_INVALIDATE_CFSOCKET
4410 CFSocketRef *shead, *s;
4411 #else
4412 CFRunLoopSourceRef *shead, *s;
4413 #endif
4415 BLOCK_INPUT;
4417 #ifdef SELECT_INVALIDATE_CFSOCKET
4418 shead = xmalloc (sizeof (CFSocketRef) * nsocks);
4419 #else
4420 shead = xmalloc (sizeof (CFRunLoopSourceRef) * nsocks);
4421 #endif
4422 s = shead;
4423 for (i = 1; i < n; i++)
4424 if (FD_ISSET (i, rfds))
4426 CFSocketRef socket =
4427 CFSocketCreateWithNative (NULL, i, kCFSocketReadCallBack,
4428 socket_callback, NULL);
4429 CFRunLoopSourceRef source =
4430 CFSocketCreateRunLoopSource (NULL, socket, 0);
4432 #ifdef SELECT_INVALIDATE_CFSOCKET
4433 CFSocketSetSocketFlags (socket, 0);
4434 #endif
4435 CFRunLoopAddSource (runloop, source, kCFRunLoopDefaultMode);
4436 #ifdef SELECT_INVALIDATE_CFSOCKET
4437 CFRelease (source);
4438 *s = socket;
4439 #else
4440 CFRelease (socket);
4441 *s = source;
4442 #endif
4443 s++;
4446 err = ReceiveNextEvent (0, NULL, timeout_sec, kEventLeaveInQueue, NULL);
4450 --s;
4451 #ifdef SELECT_INVALIDATE_CFSOCKET
4452 CFSocketInvalidate (*s);
4453 #else
4454 CFRunLoopRemoveSource (runloop, *s, kCFRunLoopDefaultMode);
4455 #endif
4456 CFRelease (*s);
4458 while (s != shead);
4460 xfree (shead);
4462 if (err)
4464 FD_ZERO (rfds);
4465 r = 0;
4467 else
4469 FlushEventsMatchingListFromQueue (GetCurrentEventQueue (),
4470 GetEventTypeCount (specs),
4471 specs);
4472 EMACS_SET_SECS_USECS (select_timeout, 0, 0);
4473 r = select_and_poll_event (n, rfds, wfds, efds, &select_timeout);
4476 UNBLOCK_INPUT;
4478 return r;
4480 #endif /* SELECT_USE_CFSOCKET */
4483 poll_periodically:
4485 EMACS_TIME end_time, now, remaining_time;
4486 SELECT_TYPE orfds = *rfds, owfds, oefds;
4488 if (wfds)
4489 owfds = *wfds;
4490 if (efds)
4491 oefds = *efds;
4492 if (timeout)
4494 remaining_time = *timeout;
4495 EMACS_GET_TIME (now);
4496 EMACS_ADD_TIME (end_time, now, remaining_time);
4501 EMACS_SET_SECS_USECS (select_timeout, 0, SELECT_POLLING_PERIOD_USEC);
4502 if (timeout && EMACS_TIME_LT (remaining_time, select_timeout))
4503 select_timeout = remaining_time;
4504 r = select_and_poll_event (n, rfds, wfds, efds, &select_timeout);
4505 if (r != 0)
4506 return r;
4508 *rfds = orfds;
4509 if (wfds)
4510 *wfds = owfds;
4511 if (efds)
4512 *efds = oefds;
4514 if (timeout)
4516 EMACS_GET_TIME (now);
4517 EMACS_SUB_TIME (remaining_time, end_time, now);
4520 while (!timeout || EMACS_TIME_LT (now, end_time));
4522 FD_ZERO (rfds);
4523 if (wfds)
4524 FD_ZERO (wfds);
4525 if (efds)
4526 FD_ZERO (efds);
4527 return 0;
4531 /* Set up environment variables so that Emacs can correctly find its
4532 support files when packaged as an application bundle. Directories
4533 placed in /usr/local/share/emacs/<emacs-version>/, /usr/local/bin,
4534 and /usr/local/libexec/emacs/<emacs-version>/<system-configuration>
4535 by `make install' by default can instead be placed in
4536 .../Emacs.app/Contents/Resources/ and
4537 .../Emacs.app/Contents/MacOS/. Each of these environment variables
4538 is changed only if it is not already set. Presumably if the user
4539 sets an environment variable, he will want to use files in his path
4540 instead of ones in the application bundle. */
4541 void
4542 init_mac_osx_environment ()
4544 CFBundleRef bundle;
4545 CFURLRef bundleURL;
4546 CFStringRef cf_app_bundle_pathname;
4547 int app_bundle_pathname_len;
4548 char *app_bundle_pathname;
4549 char *p, *q;
4550 struct stat st;
4552 /* Initialize locale related variables. */
4553 mac_system_script_code =
4554 (ScriptCode) GetScriptManagerVariable (smSysScript);
4555 Vmac_system_locale = mac_get_system_locale ();
4557 /* Fetch the pathname of the application bundle as a C string into
4558 app_bundle_pathname. */
4560 bundle = CFBundleGetMainBundle ();
4561 if (!bundle || CFBundleGetIdentifier (bundle) == NULL)
4563 /* We could not find the bundle identifier. For now, prevent
4564 the fatal error by bringing it up in the terminal. */
4565 inhibit_window_system = 1;
4566 return;
4569 bundleURL = CFBundleCopyBundleURL (bundle);
4570 if (!bundleURL)
4571 return;
4573 cf_app_bundle_pathname = CFURLCopyFileSystemPath (bundleURL,
4574 kCFURLPOSIXPathStyle);
4575 app_bundle_pathname_len = CFStringGetLength (cf_app_bundle_pathname);
4576 app_bundle_pathname = (char *) alloca (app_bundle_pathname_len + 1);
4578 if (!CFStringGetCString (cf_app_bundle_pathname,
4579 app_bundle_pathname,
4580 app_bundle_pathname_len + 1,
4581 kCFStringEncodingISOLatin1))
4583 CFRelease (cf_app_bundle_pathname);
4584 return;
4587 CFRelease (cf_app_bundle_pathname);
4589 /* P should have sufficient room for the pathname of the bundle plus
4590 the subpath in it leading to the respective directories. Q
4591 should have three times that much room because EMACSLOADPATH can
4592 have the value "<path to lisp dir>:<path to leim dir>:<path to
4593 site-lisp dir>". */
4594 p = (char *) alloca (app_bundle_pathname_len + 50);
4595 q = (char *) alloca (3 * app_bundle_pathname_len + 150);
4596 if (!getenv ("EMACSLOADPATH"))
4598 q[0] = '\0';
4600 strcpy (p, app_bundle_pathname);
4601 strcat (p, "/Contents/Resources/lisp");
4602 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
4603 strcat (q, p);
4605 strcpy (p, app_bundle_pathname);
4606 strcat (p, "/Contents/Resources/leim");
4607 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
4609 if (q[0] != '\0')
4610 strcat (q, ":");
4611 strcat (q, p);
4614 strcpy (p, app_bundle_pathname);
4615 strcat (p, "/Contents/Resources/site-lisp");
4616 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
4618 if (q[0] != '\0')
4619 strcat (q, ":");
4620 strcat (q, p);
4623 if (q[0] != '\0')
4624 setenv ("EMACSLOADPATH", q, 1);
4627 if (!getenv ("EMACSPATH"))
4629 q[0] = '\0';
4631 strcpy (p, app_bundle_pathname);
4632 strcat (p, "/Contents/MacOS/libexec");
4633 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
4634 strcat (q, p);
4636 strcpy (p, app_bundle_pathname);
4637 strcat (p, "/Contents/MacOS/bin");
4638 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
4640 if (q[0] != '\0')
4641 strcat (q, ":");
4642 strcat (q, p);
4645 if (q[0] != '\0')
4646 setenv ("EMACSPATH", q, 1);
4649 if (!getenv ("EMACSDATA"))
4651 strcpy (p, app_bundle_pathname);
4652 strcat (p, "/Contents/Resources/etc");
4653 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
4654 setenv ("EMACSDATA", p, 1);
4657 if (!getenv ("EMACSDOC"))
4659 strcpy (p, app_bundle_pathname);
4660 strcat (p, "/Contents/Resources/etc");
4661 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
4662 setenv ("EMACSDOC", p, 1);
4665 if (!getenv ("INFOPATH"))
4667 strcpy (p, app_bundle_pathname);
4668 strcat (p, "/Contents/Resources/info");
4669 if (stat (p, &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR)
4670 setenv ("INFOPATH", p, 1);
4673 #endif /* MAC_OSX */
4676 void
4677 syms_of_mac ()
4679 #if TARGET_API_MAC_CARBON
4680 Qstring = intern ("string"); staticpro (&Qstring);
4681 Qnumber = intern ("number"); staticpro (&Qnumber);
4682 Qboolean = intern ("boolean"); staticpro (&Qboolean);
4683 Qdate = intern ("date"); staticpro (&Qdate);
4684 Qdata = intern ("data"); staticpro (&Qdata);
4685 Qarray = intern ("array"); staticpro (&Qarray);
4686 Qdictionary = intern ("dictionary"); staticpro (&Qdictionary);
4688 Qxml = intern ("xml");
4689 staticpro (&Qxml);
4691 Qmime_charset = intern ("mime-charset");
4692 staticpro (&Qmime_charset);
4694 QNFD = intern ("NFD"); staticpro (&QNFD);
4695 QNFKD = intern ("NFKD"); staticpro (&QNFKD);
4696 QNFC = intern ("NFC"); staticpro (&QNFC);
4697 QNFKC = intern ("NFKC"); staticpro (&QNFKC);
4698 QHFS_plus_D = intern ("HFS+D"); staticpro (&QHFS_plus_D);
4699 QHFS_plus_C = intern ("HFS+C"); staticpro (&QHFS_plus_C);
4700 #endif
4702 #if TARGET_API_MAC_CARBON
4703 defsubr (&Smac_get_preference);
4704 defsubr (&Smac_code_convert_string);
4705 #endif
4706 defsubr (&Smac_clear_font_name_table);
4708 defsubr (&Smac_set_file_creator);
4709 defsubr (&Smac_set_file_type);
4710 defsubr (&Smac_get_file_creator);
4711 defsubr (&Smac_get_file_type);
4712 defsubr (&Sdo_applescript);
4713 defsubr (&Smac_file_name_to_posix);
4714 defsubr (&Sposix_file_name_to_mac);
4716 DEFVAR_INT ("mac-system-script-code", &mac_system_script_code,
4717 doc: /* The system script code. */);
4718 mac_system_script_code = (ScriptCode) GetScriptManagerVariable (smSysScript);
4720 DEFVAR_LISP ("mac-system-locale", &Vmac_system_locale,
4721 doc: /* The system locale identifier string.
4722 This is not a POSIX locale ID, but an ICU locale ID. So encoding
4723 information is not included. */);
4724 Vmac_system_locale = mac_get_system_locale ();
4727 /* arch-tag: 29d30c1f-0c6b-4f88-8a6d-0558d7f9dbff
4728 (do not change this comment) */