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