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