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