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