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