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