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