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