(customize-face-other-window): Make it work similarly.
[bpt/emacs.git] / mac / src / mac.c
1 /* Unix emulation routines for GNU Emacs on the Mac OS.
2 Copyright (C) 2000 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@users.sourceforge.net). */
22
23 #include <config.h>
24
25 #include <stdio.h>
26 #include <errno.h>
27 #include <utime.h>
28 #include <dirent.h>
29 #include <sys/stat.h>
30 #include <string.h>
31 #include <pwd.h>
32 #include <sys/param.h>
33 #if __MWERKS__
34 #include <unistd.h>
35 #endif
36
37 #include <Files.h>
38 #include <MacTypes.h>
39 #include <TextUtils.h>
40 #include <Folders.h>
41 #include <Resources.h>
42 #include <Aliases.h>
43 #include <FixMath.h>
44 #include <Timer.h>
45 #include <OSA.h>
46 #include <AppleScript.h>
47
48 #include "lisp.h"
49 #include "process.h"
50 #include "sysselect.h"
51 #include "systime.h"
52
53 Lisp_Object QCLIPBOARD;
54
55 /* An instance of the AppleScript component. */
56 static ComponentInstance as_scripting_component;
57 /* The single script context used for all script executions. */
58 static OSAID as_script_context;
59
60
61 /* When converting from Mac to Unix pathnames, /'s in folder names are
62 converted to :'s. This function, used in copying folder names,
63 performs a strncat and converts all character a to b in the copy of
64 the string s2 appended to the end of s1. */
65
66 void
67 string_cat_and_replace (char *s1, const char *s2, int n, char a, char b)
68 {
69 int l1 = strlen (s1);
70 int l2 = strlen (s2);
71 char *p = s1 + l1;
72 int i;
73
74 strncat (s1, s2, n);
75 for (i = 0; i < l2; i++)
76 {
77 if (*p == a)
78 *p = b;
79 p++;
80 }
81 }
82
83
84 /* Convert a Mac pathname to Unix form. A Mac full pathname is one
85 that does not begin with a ':' and contains at least one ':'. A Mac
86 full pathname causes an '/' to be prepended to the Unix pathname.
87 The algorithm for the rest of the pathname is as follows:
88 For each segment between two ':',
89 if it is non-null, copy as is and then add a '/' at the end,
90 otherwise, insert a "../" into the Unix pathname.
91 Returns 1 if successful; 0 if fails. */
92
93 int
94 mac_to_posix_pathname (const char *mfn, char *ufn, int ufnbuflen)
95 {
96 const char *p, *q, *pe;
97
98 strcpy (ufn, "");
99
100 if (*mfn == '\0')
101 return 1;
102
103 p = strchr (mfn, ':');
104 if (p != 0 && p != mfn) /* full pathname */
105 strcat (ufn, "/");
106
107 p = mfn;
108 if (*p == ':')
109 p++;
110
111 pe = mfn + strlen (mfn);
112 while (p < pe)
113 {
114 q = strchr (p, ':');
115 if (q)
116 {
117 if (q == p)
118 { /* two consecutive ':' */
119 if (strlen (ufn) + 3 >= ufnbuflen)
120 return 0;
121 strcat (ufn, "../");
122 }
123 else
124 {
125 if (strlen (ufn) + (q - p) + 1 >= ufnbuflen)
126 return 0;
127 string_cat_and_replace (ufn, p, q - p, '/', ':');
128 strcat (ufn, "/");
129 }
130 p = q + 1;
131 }
132 else
133 {
134 if (strlen (ufn) + (pe - p) >= ufnbuflen)
135 return 0;
136 string_cat_and_replace (ufn, p, pe - p, '/', ':');
137 /* no separator for last one */
138 p = pe;
139 }
140 }
141
142 return 1;
143 }
144
145
146 extern char *get_temp_dir_name ();
147
148
149 /* Convert a Unix pathname to Mac form. Approximately reverse of the
150 above in algorithm. */
151
152 int
153 posix_to_mac_pathname (const char *ufn, char *mfn, int mfnbuflen)
154 {
155 const char *p, *q, *pe;
156 char expanded_pathname[MAXPATHLEN+1];
157
158 strcpy (mfn, "");
159
160 if (*ufn == '\0')
161 return 1;
162
163 p = ufn;
164
165 /* Check for and handle volume names. Last comparison: strangely
166 somewhere "/.emacs" is passed. A temporary fix for now. */
167 if (*p == '/' && strchr (p+1, '/') == NULL && strcmp (p, "/.emacs") != 0)
168 {
169 if (strlen (p) + 1 > mfnbuflen)
170 return 0;
171 strcpy (mfn, p+1);
172 strcat (mfn, ":");
173 return 1;
174 }
175
176 /* expand to emacs dir found by init_emacs_passwd_dir */
177 if (strncmp (p, "~emacs/", 7) == 0)
178 {
179 struct passwd *pw = getpwnam ("emacs");
180 p += 7;
181 if (strlen (pw->pw_dir) + strlen (p) > MAXPATHLEN)
182 return 0;
183 strcpy (expanded_pathname, pw->pw_dir);
184 strcat (expanded_pathname, p);
185 p = expanded_pathname;
186 /* now p points to the pathname with emacs dir prefix */
187 }
188 else if (strncmp (p, "/tmp/", 5) == 0)
189 {
190 char *t = get_temp_dir_name ();
191 p += 5;
192 if (strlen (t) + strlen (p) > MAXPATHLEN)
193 return 0;
194 strcpy (expanded_pathname, t);
195 strcat (expanded_pathname, p);
196 p = expanded_pathname;
197 /* now p points to the pathname with emacs dir prefix */
198 }
199 else if (*p != '/') /* relative pathname */
200 strcat (mfn, ":");
201
202 if (*p == '/')
203 p++;
204
205 pe = p + strlen (p);
206 while (p < pe)
207 {
208 q = strchr (p, '/');
209 if (q)
210 {
211 if (q - p == 2 && *p == '.' && *(p+1) == '.')
212 {
213 if (strlen (mfn) + 1 >= mfnbuflen)
214 return 0;
215 strcat (mfn, ":");
216 }
217 else
218 {
219 if (strlen (mfn) + (q - p) + 1 >= mfnbuflen)
220 return 0;
221 string_cat_and_replace (mfn, p, q - p, ':', '/');
222 strcat (mfn, ":");
223 }
224 p = q + 1;
225 }
226 else
227 {
228 if (strlen (mfn) + (pe - p) >= mfnbuflen)
229 return 0;
230 string_cat_and_replace (mfn, p, pe - p, ':', '/');
231 p = pe;
232 }
233 }
234
235 return 1;
236 }
237
238
239 /* The following functions with "sys_" prefix are stubs to Unix
240 functions that have already been implemented by CW or MPW. The
241 calls to them in Emacs source course are #define'd to call the sys_
242 versions by the header files s-mac.h. In these stubs pathnames are
243 converted between their Unix and Mac forms. */
244
245
246 /* Unix epoch is Jan 1, 1970 while Mac epoch is Jan 1, 1904: 66 years
247 + 17 leap days. These are for adjusting time values returned by
248 MacOS Toolbox functions. */
249
250 #define MAC_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
251
252 #ifdef __MWERKS__
253 #ifndef CODEWARRIOR_VERSION_6
254 /* CW Pro 5 epoch is Jan 1, 1900 (aaarghhhhh!); remember, 1900 is not
255 a leap year! This is for adjusting time_t values returned by MSL
256 functions. */
257 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 70 + 17) * 24 * 60 * 60)
258 #else
259 /* CW changes Pro 6 to following Unix! */
260 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
261 #endif
262 #elif __MRC__
263 /* MPW library functions follow Unix (confused?). */
264 #define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
265 #else
266 You lose!!!
267 #endif
268
269
270 /* Define our own stat function for both MrC and CW. The reason for
271 doing this: "stat" is both the name of a struct and function name:
272 can't use the same trick like that for sys_open, sys_close, etc. to
273 redirect Emacs's calls to our own version that converts Unix style
274 filenames to Mac style filename because all sorts of compilation
275 errors will be generated if stat is #define'd to be sys_stat. */
276
277 int
278 stat_noalias (const char *path, struct stat *buf)
279 {
280 char mac_pathname[MAXPATHLEN+1];
281 CInfoPBRec cipb;
282
283 if (posix_to_mac_pathname (path, mac_pathname, MAXPATHLEN+1) == 0)
284 return -1;
285
286 c2pstr (mac_pathname);
287 cipb.hFileInfo.ioNamePtr = mac_pathname;
288 cipb.hFileInfo.ioVRefNum = 0;
289 cipb.hFileInfo.ioDirID = 0;
290 cipb.hFileInfo.ioFDirIndex = 0;
291 /* set to 0 to get information about specific dir or file */
292
293 errno = PBGetCatInfo (&cipb, false);
294 if (errno == -43) /* -43: fnfErr defined in Errors.h */
295 errno = ENOENT;
296 if (errno != noErr)
297 return -1;
298
299 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* bit 4 = 1 for directories */
300 {
301 buf->st_mode = S_IFDIR | S_IREAD | S_IEXEC;
302
303 if (!(cipb.hFileInfo.ioFlAttrib & 0x1))
304 buf->st_mode |= S_IWRITE; /* bit 1 = 1 for locked files/directories */
305 buf->st_ino = cipb.dirInfo.ioDrDirID;
306 buf->st_dev = cipb.dirInfo.ioVRefNum;
307 buf->st_size = cipb.dirInfo.ioDrNmFls;
308 /* size of dir = number of files and dirs */
309 buf->st_atime
310 = buf->st_mtime
311 = cipb.dirInfo.ioDrMdDat - MAC_UNIX_EPOCH_DIFF;
312 buf->st_ctime = cipb.dirInfo.ioDrCrDat - MAC_UNIX_EPOCH_DIFF;
313 }
314 else
315 {
316 buf->st_mode = S_IFREG | S_IREAD;
317 if (!(cipb.hFileInfo.ioFlAttrib & 0x1))
318 buf->st_mode |= S_IWRITE; /* bit 1 = 1 for locked files/directories */
319 if (cipb.hFileInfo.ioFlFndrInfo.fdType == 'APPL')
320 buf->st_mode |= S_IEXEC;
321 buf->st_ino = cipb.hFileInfo.ioDirID;
322 buf->st_dev = cipb.hFileInfo.ioVRefNum;
323 buf->st_size = cipb.hFileInfo.ioFlLgLen;
324 buf->st_atime
325 = buf->st_mtime
326 = cipb.hFileInfo.ioFlMdDat - MAC_UNIX_EPOCH_DIFF;
327 buf->st_ctime = cipb.hFileInfo.ioFlCrDat - MAC_UNIX_EPOCH_DIFF;
328 }
329
330 if (cipb.hFileInfo.ioFlFndrInfo.fdFlags & 0x8000)
331 {
332 /* identify alias files as symlinks */
333 buf->st_mode &= ~S_IFREG;
334 buf->st_mode |= S_IFLNK;
335 }
336
337 buf->st_nlink = 1;
338 buf->st_uid = getuid ();
339 buf->st_gid = getgid ();
340 buf->st_rdev = 0;
341
342 return 0;
343 }
344
345
346 int
347 lstat (const char *path, struct stat *buf)
348 {
349 int result;
350 char true_pathname[MAXPATHLEN+1];
351
352 /* Try looking for the file without resolving aliases first. */
353 if ((result = stat_noalias (path, buf)) >= 0)
354 return result;
355
356 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
357 return -1;
358
359 return stat_noalias (true_pathname, buf);
360 }
361
362
363 int
364 stat (const char *path, struct stat *sb)
365 {
366 int result;
367 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
368 int len;
369
370 if ((result = stat_noalias (path, sb)) >= 0 &&
371 ! (sb->st_mode & S_IFLNK))
372 return result;
373
374 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
375 return -1;
376
377 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
378 if (len > -1)
379 {
380 fully_resolved_name[len] = '\0';
381 /* in fact our readlink terminates strings */
382 return lstat (fully_resolved_name, sb);
383 }
384 else
385 return lstat (true_pathname, sb);
386 }
387
388
389 #if __MRC__
390 /* CW defines fstat in stat.mac.c while MPW does not provide this
391 function. Without the information of how to get from a file
392 descriptor in MPW StdCLib to a Mac OS file spec, it should be hard
393 to implement this function. Fortunately, there is only one place
394 where this function is called in our configuration: in fileio.c,
395 where only the st_dev and st_ino fields are used to determine
396 whether two fildes point to different i-nodes to prevent copying
397 a file onto itself equal. What we have here probably needs
398 improvement. */
399
400 int
401 fstat (int fildes, struct stat *buf)
402 {
403 buf->st_dev = 0;
404 buf->st_ino = fildes;
405 buf->st_mode = S_IFREG; /* added by T.I. for the copy-file */
406 return 0; /* success */
407 }
408 #endif /* __MRC__ */
409
410
411 /* Adapted from Think Reference code example. */
412
413 int
414 mkdir (const char *dirname, int mode)
415 {
416 #pragma unused(mode)
417
418 HFileParam hfpb;
419 char true_pathname[MAXPATHLEN+1], mac_pathname[MAXPATHLEN+1];
420
421 if (find_true_pathname (dirname, true_pathname, MAXPATHLEN+1) == -1)
422 return -1;
423
424 if (posix_to_mac_pathname (true_pathname, mac_pathname, MAXPATHLEN+1) == 0)
425 return -1;
426
427 c2pstr (mac_pathname);
428 hfpb.ioNamePtr = mac_pathname;
429 hfpb.ioVRefNum = 0; /* ignored unless name is invalid */
430 hfpb.ioDirID = 0; /* parent is the root */
431
432 errno = PBDirCreate ((HParmBlkPtr) &hfpb, false);
433 /* just return the Mac OSErr code for now */
434 return errno == noErr ? 0 : -1;
435 }
436
437
438 #undef rmdir
439 sys_rmdir (const char *dirname)
440 {
441 HFileParam hfpb;
442 char mac_pathname[MAXPATHLEN+1];
443
444 if (posix_to_mac_pathname (dirname, mac_pathname, MAXPATHLEN+1) == 0)
445 return -1;
446
447 c2pstr (mac_pathname);
448 hfpb.ioNamePtr = mac_pathname;
449 hfpb.ioVRefNum = 0; /* ignored unless name is invalid */
450 hfpb.ioDirID = 0; /* parent is the root */
451
452 errno = PBHDelete ((HParmBlkPtr) &hfpb, false);
453 return errno == noErr ? 0 : -1;
454 }
455
456
457 #ifdef __MRC__
458 /* No implementation yet. */
459 int
460 execvp (const char *path, ...)
461 {
462 return -1;
463 }
464 #endif /* __MRC__ */
465
466
467 int
468 utime (const char *path, const struct utimbuf *times)
469 {
470 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
471 int len;
472 char mac_pathname[MAXPATHLEN+1];
473 CInfoPBRec cipb;
474
475 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
476 return -1;
477
478 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
479 if (len > -1)
480 fully_resolved_name[len] = '\0';
481 else
482 strcpy (fully_resolved_name, true_pathname);
483
484 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
485 return -1;
486
487 c2pstr (mac_pathname);
488 cipb.hFileInfo.ioNamePtr = mac_pathname;
489 cipb.hFileInfo.ioVRefNum = 0;
490 cipb.hFileInfo.ioDirID = 0;
491 cipb.hFileInfo.ioFDirIndex = 0;
492 /* set to 0 to get information about specific dir or file */
493
494 errno = PBGetCatInfo (&cipb, false);
495 if (errno != noErr)
496 return -1;
497
498 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* bit 4 = 1 for directories */
499 {
500 if (times)
501 cipb.dirInfo.ioDrMdDat = times->modtime + MAC_UNIX_EPOCH_DIFF;
502 else
503 GetDateTime (&cipb.dirInfo.ioDrMdDat);
504 }
505 else
506 {
507 if (times)
508 cipb.hFileInfo.ioFlMdDat = times->modtime + MAC_UNIX_EPOCH_DIFF;
509 else
510 GetDateTime (&cipb.hFileInfo.ioFlMdDat);
511 }
512
513 errno = PBSetCatInfo (&cipb, false);
514 return errno == noErr ? 0 : -1;
515 }
516
517
518 #ifndef F_OK
519 #define F_OK 0
520 #endif
521 #ifndef X_OK
522 #define X_OK 1
523 #endif
524 #ifndef W_OK
525 #define W_OK 2
526 #endif
527
528 /* Like stat, but test for access mode in hfpb.ioFlAttrib */
529 int
530 access (const char *path, int mode)
531 {
532 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
533 int len;
534 char mac_pathname[MAXPATHLEN+1];
535 CInfoPBRec cipb;
536
537 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
538 return -1;
539
540 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
541 if (len > -1)
542 fully_resolved_name[len] = '\0';
543 else
544 strcpy (fully_resolved_name, true_pathname);
545
546 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
547 return -1;
548
549 c2pstr (mac_pathname);
550 cipb.hFileInfo.ioNamePtr = mac_pathname;
551 cipb.hFileInfo.ioVRefNum = 0;
552 cipb.hFileInfo.ioDirID = 0;
553 cipb.hFileInfo.ioFDirIndex = 0;
554 /* set to 0 to get information about specific dir or file */
555
556 errno = PBGetCatInfo (&cipb, false);
557 if (errno != noErr)
558 return -1;
559
560 if (mode == F_OK) /* got this far, file exists */
561 return 0;
562
563 if (mode & X_OK)
564 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* path refers to a directory */
565 return 0;
566 else
567 {
568 if (cipb.hFileInfo.ioFlFndrInfo.fdType == 'APPL')
569 return 0;
570 else
571 return -1;
572 }
573
574 if (mode & W_OK)
575 return (cipb.hFileInfo.ioFlAttrib & 0x1) ? -1 : 0;
576 /* don't allow if lock bit is on */
577
578 return -1;
579 }
580
581
582 #define DEV_NULL_FD 0x10000
583
584 #undef open
585 int
586 sys_open (const char *path, int oflag)
587 {
588 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
589 int len;
590 char mac_pathname[MAXPATHLEN+1];
591
592 if (strcmp (path, "/dev/null") == 0)
593 return DEV_NULL_FD; /* some bogus fd to be ignored in write */
594
595 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
596 return -1;
597
598 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
599 if (len > -1)
600 fully_resolved_name[len] = '\0';
601 else
602 strcpy (fully_resolved_name, true_pathname);
603
604 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
605 return -1;
606 else
607 {
608 #ifdef __MRC__
609 int res = open (mac_pathname, oflag);
610 /* if (oflag == O_WRONLY || oflag == O_RDWR) */
611 if (oflag & O_CREAT)
612 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
613 return res;
614 #else
615 return open (mac_pathname, oflag);
616 #endif
617 }
618 }
619
620
621 #undef creat
622 int
623 sys_creat (const char *path, mode_t mode)
624 {
625 char true_pathname[MAXPATHLEN+1];
626 int len;
627 char mac_pathname[MAXPATHLEN+1];
628
629 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
630 return -1;
631
632 if (!posix_to_mac_pathname (true_pathname, mac_pathname, MAXPATHLEN+1))
633 return -1;
634 else
635 {
636 #ifdef __MRC__
637 int result = creat (mac_pathname);
638 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
639 return result;
640 #else
641 return creat (mac_pathname, mode);
642 #endif
643 }
644 }
645
646
647 #undef unlink
648 int
649 sys_unlink (const char *path)
650 {
651 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
652 int len;
653 char mac_pathname[MAXPATHLEN+1];
654
655 if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
656 return -1;
657
658 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
659 if (len > -1)
660 fully_resolved_name[len] = '\0';
661 else
662 strcpy (fully_resolved_name, true_pathname);
663
664 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
665 return -1;
666 else
667 return unlink (mac_pathname);
668 }
669
670
671 #undef read
672 int
673 sys_read (int fildes, char *buf, int count)
674 {
675 if (fildes == 0) /* this should not be used for console input */
676 return -1;
677 else
678 #ifdef CODEWARRIOR_VERSION_6
679 return _read (fildes, buf, count);
680 #else
681 return read (fildes, buf, count);
682 #endif
683 }
684
685
686 #undef write
687 int
688 sys_write (int fildes, const char *buf, int count)
689 {
690 if (fildes == DEV_NULL_FD)
691 return count;
692 else
693 #ifdef CODEWARRIOR_VERSION_6
694 return _write (fildes, buf, count);
695 #else
696 return write (fildes, buf, count);
697 #endif
698 }
699
700
701 #undef rename
702 int
703 sys_rename (const char * old_name, const char * new_name)
704 {
705 char true_old_pathname[MAXPATHLEN+1], true_new_pathname[MAXPATHLEN+1];
706 char fully_resolved_old_name[MAXPATHLEN+1];
707 int len;
708 char mac_old_name[MAXPATHLEN+1], mac_new_name[MAXPATHLEN+1];
709
710 if (find_true_pathname (old_name, true_old_pathname, MAXPATHLEN+1) == -1)
711 return -1;
712
713 len = readlink (true_old_pathname, fully_resolved_old_name, MAXPATHLEN);
714 if (len > -1)
715 fully_resolved_old_name[len] = '\0';
716 else
717 strcpy (fully_resolved_old_name, true_old_pathname);
718
719 if (find_true_pathname (new_name, true_new_pathname, MAXPATHLEN+1) == -1)
720 return -1;
721
722 if (strcmp (fully_resolved_old_name, true_new_pathname) == 0)
723 return 0;
724
725 if (!posix_to_mac_pathname (fully_resolved_old_name,
726 mac_old_name,
727 MAXPATHLEN+1))
728 return -1;
729
730 if (!posix_to_mac_pathname(true_new_pathname, mac_new_name, MAXPATHLEN+1))
731 return -1;
732
733 /* If a file with new_name already exists, rename deletes the old
734 file in Unix. CW version fails in these situation. So we add a
735 call to unlink here. */
736 (void) unlink (mac_new_name);
737
738 return rename (mac_old_name, mac_new_name);
739 }
740
741
742 #undef fopen
743 extern FILE *fopen (const char *name, const char *mode);
744 FILE *
745 sys_fopen (const char *name, const char *mode)
746 {
747 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
748 int len;
749 char mac_pathname[MAXPATHLEN+1];
750
751 if (find_true_pathname (name, true_pathname, MAXPATHLEN+1) == -1)
752 return 0;
753
754 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
755 if (len > -1)
756 fully_resolved_name[len] = '\0';
757 else
758 strcpy (fully_resolved_name, true_pathname);
759
760 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
761 return 0;
762 else
763 {
764 #ifdef __MRC__
765 if (mode[0] == 'w' || mode[0] == 'a')
766 fsetfileinfo (mac_pathname, 'EMAx', 'TEXT');
767 #endif
768 return fopen (mac_pathname, mode);
769 }
770 }
771
772
773 #include <Events.h>
774
775 long target_ticks = 0;
776
777 #ifdef __MRC__
778 __sigfun alarm_signal_func = (__sigfun) 0;
779 #elif __MWERKS__
780 __signal_func_ptr alarm_signal_func = (__signal_func_ptr) 0;
781 #else
782 You lose!!!
783 #endif
784
785
786 /* These functions simulate SIG_ALRM. The stub for function signal
787 stores the signal handler function in alarm_signal_func if a
788 SIG_ALRM is encountered. check_alarm is called in XTread_socket,
789 which emacs calls periodically. A pending alarm is represented by
790 a non-zero target_ticks value. check_alarm calls the handler
791 function pointed to by alarm_signal_func if one has been set up and
792 an alarm is pending. */
793
794 void
795 check_alarm ()
796 {
797 if (target_ticks && TickCount () > target_ticks)
798 {
799 target_ticks = 0;
800 if (alarm_signal_func)
801 (*alarm_signal_func)(SIGALRM);
802 }
803 }
804
805
806 int
807 select(n, rfds, wfds, efds, timeout)
808 int n;
809 SELECT_TYPE *rfds;
810 SELECT_TYPE *wfds;
811 SELECT_TYPE *efds;
812 struct timeval *timeout;
813 {
814 EMACS_TIME end_time, now;
815 EventRecord e;
816
817 /* Can only handle wait for keyboard input. */
818 if (n > 1 || wfds || efds)
819 return -1;
820
821 EMACS_GET_TIME (end_time);
822 EMACS_ADD_TIME (end_time, end_time, *timeout);
823
824 do
825 {
826 /* Also return true if an event other than a keyDown has
827 occurred. This causes kbd_buffer_get_event in keyboard.c to
828 call read_avail_input which in turn calls XTread_socket to
829 poll for these events. Otherwise these never get processed
830 except but a very slow poll timer. */
831 if (FD_ISSET (0, rfds) && EventAvail (everyEvent, &e))
832 return 1;
833
834 /* Also check movement of the mouse. */
835 {
836 Point mouse_pos;
837 static Point old_mouse_pos = {-1, -1};
838
839 GetMouse (&mouse_pos);
840 if (!EqualPt (mouse_pos, old_mouse_pos))
841 {
842 old_mouse_pos = mouse_pos;
843 return 1;
844 }
845 }
846
847 WaitNextEvent (0, &e, 1UL, NULL); /* Accept no event; wait 1 tic. by T.I.*/
848
849 EMACS_GET_TIME (now);
850 EMACS_SUB_TIME (now, end_time, now);
851 }
852 while (!EMACS_TIME_NEG_P (now));
853
854 return 0;
855 }
856
857
858 /* Called in sys_select to wait for an alarm signal to arrive. */
859
860 int
861 pause ()
862 {
863 EventRecord e;
864 unsigned long tick;
865
866 if (!target_ticks) /* no alarm pending */
867 return -1;
868
869 if ( (tick = TickCount ()) < target_ticks )
870 WaitNextEvent (0, &e, target_ticks - tick, NULL); /* Accept no event; just wait. by T.I.*/
871
872 target_ticks = 0;
873 if (alarm_signal_func)
874 (*alarm_signal_func)(SIGALRM);
875
876 return 0;
877 }
878
879
880 int
881 alarm (int seconds)
882 {
883 long remaining = target_ticks ? (TickCount () - target_ticks) / 60 : 0;
884
885 target_ticks = seconds ? TickCount () + 60 * seconds : 0;
886
887 return (remaining < 0) ? 0 : (unsigned int) remaining;
888 }
889
890
891 #undef signal
892 #ifdef __MRC__
893 extern __sigfun signal (int signal, __sigfun signal_func);
894 __sigfun
895 sys_signal (int signal_num, __sigfun signal_func)
896 #elif __MWERKS__
897 extern __signal_func_ptr signal (int signal, __signal_func_ptr signal_func);
898 __signal_func_ptr
899 sys_signal (int signal_num, __signal_func_ptr signal_func)
900 #else
901 You lose!!!
902 #endif
903 {
904 if (signal_num != SIGALRM)
905 return signal (signal_num, signal_func);
906 else
907 {
908 #ifdef __MRC__
909 __sigfun old_signal_func;
910 #elif __MWERKS__
911 __signal_func_ptr old_signal_func;
912 #else
913 You lose!!!
914 #endif
915 old_signal_func = alarm_signal_func;
916 alarm_signal_func = signal_func;
917 return old_signal_func;
918 }
919 }
920
921
922 /* gettimeofday should return the amount of time (in a timeval
923 structure) since midnight today. The toolbox function Microseconds
924 returns the number of microseconds (in a UnsignedWide value) since
925 the machine was booted. Also making this complicated is WideAdd,
926 WideSubtract, etc. take wide values. */
927
928 int
929 gettimeofday (tp)
930 struct timeval *tp;
931 {
932 static inited = 0;
933 static wide wall_clock_at_epoch, clicks_at_epoch;
934 UnsignedWide uw_microseconds;
935 wide w_microseconds;
936 time_t sys_time (time_t *);
937
938 /* If this function is called for the first time, record the number
939 of seconds since midnight and the number of microseconds since
940 boot at the time of this first call. */
941 if (!inited)
942 {
943 time_t systime;
944 inited = 1;
945 systime = sys_time (NULL);
946 /* Store microseconds since midnight in wall_clock_at_epoch. */
947 WideMultiply (systime, 1000000L, &wall_clock_at_epoch);
948 Microseconds (&uw_microseconds);
949 /* Store microseconds since boot in clicks_at_epoch. */
950 clicks_at_epoch.hi = uw_microseconds.hi;
951 clicks_at_epoch.lo = uw_microseconds.lo;
952 }
953
954 /* Get time since boot */
955 Microseconds (&uw_microseconds);
956
957 /* Convert to time since midnight*/
958 w_microseconds.hi = uw_microseconds.hi;
959 w_microseconds.lo = uw_microseconds.lo;
960 WideSubtract (&w_microseconds, &clicks_at_epoch);
961 WideAdd (&w_microseconds, &wall_clock_at_epoch);
962 tp->tv_sec = WideDivide (&w_microseconds, 1000000L, &tp->tv_usec);
963
964 return 0;
965 }
966
967
968 #ifdef __MRC__
969 unsigned int
970 sleep (unsigned int seconds)
971 {
972 unsigned long time_up;
973 EventRecord e;
974
975 time_up = TickCount () + seconds * 60;
976 while (TickCount () < time_up)
977 {
978 /* Accept no event; just wait. by T.I. */
979 WaitNextEvent (0, &e, 30, NULL);
980 }
981
982 return (0);
983 }
984 #endif /* __MRC__ */
985
986
987 /* The time functions adjust time values according to the difference
988 between the Unix and CW epoches. */
989
990 #undef gmtime
991 extern struct tm *gmtime (const time_t *);
992 struct tm *
993 sys_gmtime (const time_t *timer)
994 {
995 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
996
997 return gmtime (&unix_time);
998 }
999
1000
1001 #undef localtime
1002 extern struct tm *localtime (const time_t *);
1003 struct tm *
1004 sys_localtime (const time_t *timer)
1005 {
1006 #ifdef CODEWARRIOR_VERSION_6
1007 time_t unix_time = *timer;
1008 #else
1009 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
1010 #endif
1011
1012 return localtime (&unix_time);
1013 }
1014
1015
1016 #undef ctime
1017 extern char *ctime (const time_t *);
1018 char *
1019 sys_ctime (const time_t *timer)
1020 {
1021 #ifdef CODEWARRIOR_VERSION_6
1022 time_t unix_time = *timer;
1023 #else
1024 time_t unix_time = *timer + CW_OR_MPW_UNIX_EPOCH_DIFF;
1025 #endif
1026
1027 return ctime (&unix_time);
1028 }
1029
1030
1031 #undef time
1032 extern time_t time (time_t *);
1033 time_t
1034 sys_time (time_t *timer)
1035 {
1036 #ifdef CODEWARRIOR_VERSION_6
1037 time_t mac_time = time (NULL);
1038 #else
1039 time_t mac_time = time (NULL) - CW_OR_MPW_UNIX_EPOCH_DIFF;
1040 #endif
1041
1042 if (timer)
1043 *timer = mac_time;
1044
1045 return mac_time;
1046 }
1047
1048
1049 /* MPW strftime broken for "%p" format */
1050 #ifdef __MRC__
1051 #undef strftime
1052 #include <time.h>
1053 size_t
1054 sys_strftime (char * s, size_t maxsize, const char * format,
1055 const struct tm * timeptr)
1056 {
1057 if (strcmp (format, "%p") == 0)
1058 {
1059 if (maxsize < 3)
1060 return 0;
1061 if (timeptr->tm_hour < 12)
1062 {
1063 strcpy (s, "AM");
1064 return 2;
1065 }
1066 else
1067 {
1068 strcpy (s, "PM");
1069 return 2;
1070 }
1071 }
1072 else
1073 return strftime (s, maxsize, format, timeptr);
1074 }
1075 #endif /* __MRC__ */
1076
1077
1078 /* no subprocesses, empty wait */
1079
1080 int
1081 wait (int pid)
1082 {
1083 return 0;
1084 }
1085
1086
1087 void
1088 croak (char *badfunc)
1089 {
1090 printf ("%s not yet implemented\r\n", badfunc);
1091 exit (1);
1092 }
1093
1094
1095 char *
1096 index (const char * str, int chr)
1097 {
1098 return strchr (str, chr);
1099 }
1100
1101
1102 char *
1103 mktemp (char *template)
1104 {
1105 int len, k;
1106 static seqnum = 0;
1107
1108 len = strlen (template);
1109 k = len - 1;
1110 while (k >= 0 && template[k] == 'X')
1111 k--;
1112
1113 k++; /* make k index of first 'X' */
1114
1115 if (k < len)
1116 {
1117 /* Zero filled, number of digits equal to the number of X's. */
1118 sprintf (&template[k], "%0*d", len-k, seqnum++);
1119
1120 return template;
1121 }
1122 else
1123 return 0;
1124 }
1125
1126
1127 /* Emulate getpwuid, getpwnam and others. */
1128
1129 #define PASSWD_FIELD_SIZE 256
1130
1131 static char my_passwd_name[PASSWD_FIELD_SIZE];
1132 static char my_passwd_dir[MAXPATHLEN+1];
1133
1134 static struct passwd my_passwd =
1135 {
1136 my_passwd_name,
1137 my_passwd_dir,
1138 };
1139
1140
1141 /* Initialized by main () in macterm.c to pathname of emacs directory. */
1142
1143 char emacs_passwd_dir[MAXPATHLEN+1];
1144
1145 char *
1146 getwd (char *);
1147
1148 void
1149 init_emacs_passwd_dir ()
1150 {
1151 int found = false;
1152
1153 if (getwd (emacs_passwd_dir) && getwd (my_passwd_dir))
1154 {
1155 /* Need pathname of first ancestor that begins with "emacs"
1156 since Mac emacs application is somewhere in the emacs-*
1157 tree. */
1158 int len = strlen (emacs_passwd_dir);
1159 int j = len - 1;
1160 /* j points to the "/" following the directory name being
1161 compared. */
1162 int i = j - 1;
1163 while (i >= 0 && !found)
1164 {
1165 while (i >= 0 && emacs_passwd_dir[i] != '/')
1166 i--;
1167 if (emacs_passwd_dir[i] == '/' && i+5 < len)
1168 found = (strncmp (&(emacs_passwd_dir[i+1]), "emacs", 5) == 0);
1169 if (found)
1170 emacs_passwd_dir[j+1] = '\0';
1171 else
1172 {
1173 j = i;
1174 i = j - 1;
1175 }
1176 }
1177 }
1178
1179 if (!found)
1180 {
1181 /* Setting to "/" probably won't work but set it to something
1182 anyway. */
1183 strcpy (emacs_passwd_dir, "/");
1184 strcpy (my_passwd_dir, "/");
1185 }
1186 }
1187
1188
1189 static struct passwd emacs_passwd =
1190 {
1191 "emacs",
1192 emacs_passwd_dir,
1193 };
1194
1195 static int my_passwd_inited = 0;
1196
1197
1198 static void
1199 init_my_passwd ()
1200 {
1201 char **owner_name;
1202
1203 /* Note: my_passwd_dir initialized in int_emacs_passwd_dir to
1204 directory where Emacs was started. */
1205
1206 owner_name = (char **) GetResource ('STR ',-16096);
1207 if (owner_name)
1208 {
1209 HLock (owner_name);
1210 BlockMove ((unsigned char *) *owner_name,
1211 (unsigned char *) my_passwd_name,
1212 *owner_name[0]+1);
1213 HUnlock (owner_name);
1214 p2cstr ((unsigned char *) my_passwd_name);
1215 }
1216 else
1217 my_passwd_name[0] = 0;
1218 }
1219
1220
1221 struct passwd *
1222 getpwuid (uid_t uid)
1223 {
1224 if (!my_passwd_inited)
1225 {
1226 init_my_passwd ();
1227 my_passwd_inited = 1;
1228 }
1229
1230 return &my_passwd;
1231 }
1232
1233
1234 struct passwd *
1235 getpwnam (const char *name)
1236 {
1237 if (strcmp (name, "emacs") == 0)
1238 return &emacs_passwd;
1239
1240 if (!my_passwd_inited)
1241 {
1242 init_my_passwd ();
1243 my_passwd_inited = 1;
1244 }
1245
1246 return &my_passwd;
1247 }
1248
1249
1250 /* The functions fork, kill, sigsetmask, sigblock, request_sigio,
1251 setpgrp, setpriority, and unrequest_sigio are defined to be empty
1252 as in msdos.c. */
1253
1254
1255 int
1256 fork ()
1257 {
1258 return -1;
1259 }
1260
1261
1262 int
1263 kill (int x, int y)
1264 {
1265 return -1;
1266 }
1267
1268
1269 void
1270 sys_subshell ()
1271 {
1272 error ("Can't spawn subshell");
1273 }
1274
1275
1276 int
1277 sigsetmask (int x)
1278 {
1279 return 0;
1280 }
1281
1282
1283 int
1284 sigblock (int mask)
1285 {
1286 return 0;
1287 }
1288
1289
1290 void
1291 request_sigio (void)
1292 {
1293 }
1294
1295
1296 void
1297 unrequest_sigio (void)
1298 {
1299 }
1300
1301
1302 int
1303 setpgrp ()
1304 {
1305 return 0;
1306 }
1307
1308
1309 /* No pipes yet. */
1310
1311 int
1312 pipe (int _fildes[2])
1313 {
1314 errno = EACCES;
1315 return -1;
1316 }
1317
1318
1319 /* Hard and symbolic links. */
1320
1321 int
1322 symlink (const char *name1, const char *name2)
1323 {
1324 errno = ENOENT;
1325 return -1;
1326 }
1327
1328
1329 int
1330 link (const char *name1, const char *name2)
1331 {
1332 errno = ENOENT;
1333 return -1;
1334 }
1335
1336
1337 /* Determine the path name of the file specified by VREFNUM, DIRID,
1338 and NAME and place that in the buffer PATH of length
1339 MAXPATHLEN. */
1340 int
1341 path_from_vol_dir_name (char *path, int man_path_len, short vol_ref_num,
1342 long dir_id, ConstStr255Param name)
1343 {
1344 Str255 dir_name;
1345 CInfoPBRec cipb;
1346 OSErr err;
1347
1348 if (strlen (name) > man_path_len)
1349 return 0;
1350
1351 memcpy (dir_name, name, name[0]+1);
1352 memcpy (path, name, name[0]+1);
1353 p2cstr (path);
1354
1355 cipb.dirInfo.ioDrParID = dir_id;
1356 cipb.dirInfo.ioNamePtr = dir_name;
1357
1358 do
1359 {
1360 cipb.dirInfo.ioVRefNum = vol_ref_num;
1361 cipb.dirInfo.ioFDirIndex = -1;
1362 cipb.dirInfo.ioDrDirID = cipb.dirInfo.ioDrParID;
1363 /* go up to parent each time */
1364
1365 err = PBGetCatInfo (&cipb, false);
1366 if (err != noErr)
1367 return 0;
1368
1369 p2cstr (dir_name);
1370 if (strlen (dir_name) + strlen (path) + 1 >= man_path_len)
1371 return 0;
1372
1373 strcat (dir_name, ":");
1374 strcat (dir_name, path);
1375 /* attach to front since we're going up directory tree */
1376 strcpy (path, dir_name);
1377 }
1378 while (cipb.dirInfo.ioDrDirID != fsRtDirID);
1379 /* stop when we see the volume's root directory */
1380
1381 return 1; /* success */
1382 }
1383
1384
1385 int
1386 readlink (const char *path, char *buf, int bufsiz)
1387 {
1388 char mac_sym_link_name[MAXPATHLEN+1];
1389 OSErr err;
1390 FSSpec fsspec;
1391 Boolean target_is_folder, was_aliased;
1392 Str255 directory_name, mac_pathname;
1393 CInfoPBRec cipb;
1394
1395 if (posix_to_mac_pathname (path, mac_sym_link_name, MAXPATHLEN+1) == 0)
1396 return -1;
1397
1398 c2pstr (mac_sym_link_name);
1399 err = FSMakeFSSpec (0, 0, mac_sym_link_name, &fsspec);
1400 if (err != noErr)
1401 {
1402 errno = ENOENT;
1403 return -1;
1404 }
1405
1406 err = ResolveAliasFile (&fsspec, true, &target_is_folder, &was_aliased);
1407 if (err != noErr || !was_aliased)
1408 {
1409 errno = ENOENT;
1410 return -1;
1411 }
1412
1413 if (path_from_vol_dir_name (mac_pathname, 255, fsspec.vRefNum, fsspec.parID,
1414 fsspec.name) == 0)
1415 {
1416 errno = ENOENT;
1417 return -1;
1418 }
1419
1420 if (mac_to_posix_pathname (mac_pathname, buf, bufsiz) == 0)
1421 {
1422 errno = ENOENT;
1423 return -1;
1424 }
1425
1426 return strlen (buf);
1427 }
1428
1429
1430 /* Convert a path to one with aliases fully expanded. */
1431
1432 static int
1433 find_true_pathname (const char *path, char *buf, int bufsiz)
1434 {
1435 char *q, temp[MAXPATHLEN+1];
1436 const char *p;
1437 int len;
1438
1439 if (bufsiz <= 0 || path == 0 || path[0] == '\0')
1440 return -1;
1441
1442 buf[0] = '\0';
1443
1444 p = path;
1445 if (*p == '/')
1446 q = strchr (p + 1, '/');
1447 else
1448 q = strchr (p, '/');
1449 len = 0; /* loop may not be entered, e.g., for "/" */
1450
1451 while (q)
1452 {
1453 strcpy (temp, buf);
1454 strncat (temp, p, q - p);
1455 len = readlink (temp, buf, bufsiz);
1456 if (len <= -1)
1457 {
1458 if (strlen (temp) + 1 > bufsiz)
1459 return -1;
1460 strcpy (buf, temp);
1461 }
1462 strcat (buf, "/");
1463 len++;
1464 p = q + 1;
1465 q = strchr(p, '/');
1466 }
1467
1468 if (len + strlen (p) + 1 >= bufsiz)
1469 return -1;
1470
1471 strcat (buf, p);
1472 return len + strlen (p);
1473 }
1474
1475
1476 mode_t
1477 umask (mode_t numask)
1478 {
1479 static mode_t mask = 022;
1480 mode_t oldmask = mask;
1481 mask = numask;
1482 return oldmask;
1483 }
1484
1485
1486 int
1487 chmod (const char *path, mode_t mode)
1488 {
1489 /* say it always succeed for now */
1490 return 0;
1491 }
1492
1493
1494 int
1495 dup (int oldd)
1496 {
1497 #ifdef __MRC__
1498 return fcntl (oldd, F_DUPFD, 0);
1499 #elif __MWERKS__
1500 /* current implementation of fcntl in fcntl.mac.c simply returns old
1501 descriptor */
1502 return fcntl (oldd, F_DUPFD);
1503 #else
1504 You lose!!!
1505 #endif
1506 }
1507
1508
1509 /* This is from the original sysdep.c. Emulate BSD dup2. First close
1510 newd if it already exists. Then, attempt to dup oldd. If not
1511 successful, call dup2 recursively until we are, then close the
1512 unsuccessful ones. */
1513
1514 int
1515 dup2 (int oldd, int newd)
1516 {
1517 int fd, ret;
1518
1519 close (newd);
1520
1521 fd = dup (oldd);
1522 if (fd == -1)
1523 return -1;
1524 if (fd == newd)
1525 return newd;
1526 ret = dup2 (oldd, newd);
1527 close (fd);
1528 return ret;
1529 }
1530
1531
1532 /* let it fail for now */
1533
1534 char *
1535 sbrk (int incr)
1536 {
1537 return (char *) -1;
1538 }
1539
1540
1541 int
1542 fsync (int fd)
1543 {
1544 return 0;
1545 }
1546
1547
1548 int
1549 ioctl (int d, int request, void *argp)
1550 {
1551 return -1;
1552 }
1553
1554
1555 #ifdef __MRC__
1556 int
1557 isatty (int fildes)
1558 {
1559 if (fildes >=0 && fildes <= 2)
1560 return 1;
1561 else
1562 return 0;
1563 }
1564
1565
1566 int
1567 getgid ()
1568 {
1569 return 100;
1570 }
1571
1572
1573 int
1574 getegid ()
1575 {
1576 return 100;
1577 }
1578
1579
1580 int
1581 getuid ()
1582 {
1583 return 200;
1584 }
1585
1586
1587 int
1588 geteuid ()
1589 {
1590 return 200;
1591 }
1592 #endif /* __MRC__ */
1593
1594
1595 #ifdef __MWERKS__
1596 #ifndef CODEWARRIOR_VERSION_6
1597 #undef getpid
1598 int
1599 getpid ()
1600 {
1601 return 9999;
1602 }
1603 #endif
1604 #endif /* __MWERKS__ */
1605
1606
1607 /* Return the path to the directory in which Emacs can create
1608 temporary files. The MacOS "temporary items" directory cannot be
1609 used because it removes the file written by a process when it
1610 exits. In that sense it's more like "/dev/null" than "/tmp" (but
1611 again not exactly). And of course Emacs needs to read back the
1612 files written by its subprocesses. So here we write the files to a
1613 directory "Emacs" in the Preferences Folder. This directory is
1614 created if it does not exist. */
1615
1616 static char *
1617 get_temp_dir_name ()
1618 {
1619 static char *temp_dir_name = NULL;
1620 short vol_ref_num;
1621 long dir_id;
1622 OSErr err;
1623 Str255 dir_name, full_path;
1624 CInfoPBRec cpb;
1625 char unix_dir_name[MAXPATHLEN+1];
1626 DIR *dir;
1627
1628 /* Cache directory name with pointer temp_dir_name.
1629 Look for it only the first time. */
1630 if (!temp_dir_name)
1631 {
1632 err = FindFolder (kOnSystemDisk, kPreferencesFolderType, kCreateFolder,
1633 &vol_ref_num, &dir_id);
1634 if (err != noErr)
1635 return NULL;
1636
1637 if (!path_from_vol_dir_name (full_path, 255, vol_ref_num, dir_id, "\p"))
1638 return NULL;
1639
1640 if (strlen (full_path) + 6 <= MAXPATHLEN)
1641 strcat (full_path, "Emacs:");
1642 else
1643 return NULL;
1644
1645 if (!mac_to_posix_pathname (full_path, unix_dir_name, MAXPATHLEN+1))
1646 return NULL;
1647
1648 dir = opendir (unix_dir_name); /* check whether temp directory exists */
1649 if (dir)
1650 closedir (dir);
1651 else if (mkdir (unix_dir_name, 0700) != 0) /* create it if not */
1652 return NULL;
1653
1654 temp_dir_name = (char *) malloc (strlen (unix_dir_name) + 1);
1655 strcpy (temp_dir_name, unix_dir_name);
1656 }
1657
1658 return temp_dir_name;
1659 }
1660
1661
1662 /* Allocate and construct an array of pointers to strings from a list
1663 of strings stored in a 'STR#' resource. The returned pointer array
1664 is stored in the style of argv and environ: if the 'STR#' resource
1665 contains numString strings, an pointer array with numString+1
1666 elements is returned in which the last entry contains a null
1667 pointer. The pointer to the pointer array is passed by pointer in
1668 parameter t. The resource ID of the 'STR#' resource is passed in
1669 parameter StringListID.
1670 */
1671
1672 void
1673 get_string_list (char ***t, short string_list_id)
1674 {
1675 Handle h;
1676 Ptr p;
1677 int i, num_strings;
1678
1679 h = GetResource ('STR#', string_list_id);
1680 if (h)
1681 {
1682 HLock (h);
1683 p = *h;
1684 num_strings = * (short *) p;
1685 p += sizeof(short);
1686 *t = (char **) malloc (sizeof (char *) * (num_strings + 1));
1687 for (i = 0; i < num_strings; i++)
1688 {
1689 short length = *p++;
1690 (*t)[i] = (char *) malloc (length + 1);
1691 strncpy ((*t)[i], p, length);
1692 (*t)[i][length] = '\0';
1693 p += length;
1694 }
1695 (*t)[num_strings] = 0;
1696 HUnlock (h);
1697 }
1698 else
1699 {
1700 /* Return no string in case GetResource fails. Bug fixed by
1701 Ikegami Tsutomu. Caused MPW build to crash without sym -on
1702 option (no sym -on implies -opt local). */
1703 *t = (char **) malloc (sizeof (char *));
1704 (*t)[0] = 0;
1705 }
1706 }
1707
1708
1709 static char *
1710 get_path_to_system_folder ()
1711 {
1712 short vol_ref_num;
1713 long dir_id;
1714 OSErr err;
1715 Str255 dir_name, full_path;
1716 CInfoPBRec cpb;
1717 static char system_folder_unix_name[MAXPATHLEN+1];
1718 DIR *dir;
1719
1720 err = FindFolder (kOnSystemDisk, kSystemFolderType, kDontCreateFolder,
1721 &vol_ref_num, &dir_id);
1722 if (err != noErr)
1723 return NULL;
1724
1725 if (!path_from_vol_dir_name (full_path, 255, vol_ref_num, dir_id, "\p"))
1726 return NULL;
1727
1728 if (!mac_to_posix_pathname (full_path, system_folder_unix_name, MAXPATHLEN+1))
1729 return NULL;
1730
1731 return system_folder_unix_name;
1732 }
1733
1734
1735 char **environ;
1736
1737 #define ENVIRON_STRING_LIST_ID 128
1738
1739 /* Get environment variable definitions from STR# resource. */
1740
1741 void
1742 init_environ ()
1743 {
1744 int i;
1745
1746 get_string_list (&environ, ENVIRON_STRING_LIST_ID);
1747
1748 i = 0;
1749 while (environ[i])
1750 i++;
1751
1752 /* Make HOME directory the one Emacs starts up in if not specified
1753 by resource. */
1754 if (getenv ("HOME") == NULL)
1755 {
1756 environ = (char **) realloc (environ, sizeof (char *) * (i + 2));
1757 if (environ)
1758 {
1759 environ[i] = (char *) malloc (strlen (my_passwd_dir) + 6);
1760 if (environ[i])
1761 {
1762 strcpy (environ[i], "HOME=");
1763 strcat (environ[i], my_passwd_dir);
1764 }
1765 environ[i+1] = 0;
1766 i++;
1767 }
1768 }
1769
1770 /* Make HOME directory the one Emacs starts up in if not specified
1771 by resource. */
1772 if (getenv ("MAIL") == NULL)
1773 {
1774 environ = (char **) realloc (environ, sizeof (char *) * (i + 2));
1775 if (environ)
1776 {
1777 char * path_to_system_folder = get_path_to_system_folder ();
1778 environ[i] = (char *) malloc (strlen (path_to_system_folder) + 22);
1779 if (environ[i])
1780 {
1781 strcpy (environ[i], "MAIL=");
1782 strcat (environ[i], path_to_system_folder);
1783 strcat (environ[i], "Eudora Folder/In");
1784 }
1785 environ[i+1] = 0;
1786 }
1787 }
1788 }
1789
1790
1791 /* Return the value of the environment variable NAME. */
1792
1793 char *
1794 getenv (const char *name)
1795 {
1796 int length = strlen(name);
1797 char **e;
1798
1799 for (e = environ; *e != 0; e++)
1800 if (strncmp(*e, name, length) == 0 && (*e)[length] == '=')
1801 return &(*e)[length + 1];
1802
1803 if (strcmp (name, "TMPDIR") == 0)
1804 return get_temp_dir_name ();
1805
1806 return 0;
1807 }
1808
1809
1810 #ifdef __MRC__
1811 /* see Interfaces&Libraries:Interfaces:CIncludes:signal.h */
1812 char *sys_siglist[] =
1813 {
1814 "Zero is not a signal!!!",
1815 "Abort", /* 1 */
1816 "Interactive user interrupt", /* 2 */ "?",
1817 "Floating point exception", /* 4 */ "?", "?", "?",
1818 "Illegal instruction", /* 8 */ "?", "?", "?", "?", "?", "?", "?",
1819 "Segment violation", /* 16 */ "?", "?", "?", "?", "?", "?", "?",
1820 "?", "?", "?", "?", "?", "?", "?", "?",
1821 "Terminal" /* 32 */
1822 };
1823 #elif __MWERKS__
1824 char *sys_siglist[] =
1825 {
1826 "Zero is not a signal!!!",
1827 "Abort",
1828 "Floating point exception",
1829 "Illegal instruction",
1830 "Interactive user interrupt",
1831 "Segment violation",
1832 "Terminal"
1833 };
1834 #else
1835 You lose!!!
1836 #endif
1837
1838
1839 #include <utsname.h>
1840
1841 int
1842 uname (struct utsname *name)
1843 {
1844 char **system_name;
1845 system_name = GetString (-16413); /* IM - Resource Manager Reference */
1846 if (system_name)
1847 {
1848 BlockMove (*system_name, name->nodename, (*system_name)[0]+1);
1849 p2cstr (name->nodename);
1850 return 0;
1851 }
1852 else
1853 return -1;
1854 }
1855
1856
1857 #include <Processes.h>
1858 #include <EPPC.h>
1859
1860 /* Event class of HLE sent to subprocess. */
1861 const OSType kEmacsSubprocessSend = 'ESND';
1862
1863 /* Event class of HLE sent back from subprocess. */
1864 const OSType kEmacsSubprocessReply = 'ERPY';
1865
1866
1867 char *
1868 mystrchr (char *s, char c)
1869 {
1870 while (*s && *s != c)
1871 {
1872 if (*s == '\\')
1873 s++;
1874 s++;
1875 }
1876
1877 if (*s)
1878 {
1879 *s = '\0';
1880 return s;
1881 }
1882 else
1883 return NULL;
1884 }
1885
1886
1887 char *
1888 mystrtok (char *s)
1889 {
1890 while (*s)
1891 s++;
1892
1893 return s + 1;
1894 }
1895
1896
1897 void
1898 mystrcpy (char *to, char *from)
1899 {
1900 while (*from)
1901 {
1902 if (*from == '\\')
1903 from++;
1904 *to++ = *from++;
1905 }
1906 *to = '\0';
1907 }
1908
1909
1910 /* Start a Mac subprocess. Arguments for it is passed in argv (null
1911 terminated). The process should run with the default directory
1912 "workdir", read input from "infn", and write output and error to
1913 "outfn" and "errfn", resp. The Process Manager call
1914 LaunchApplication is used to start the subprocess. We use high
1915 level events as the mechanism to pass arguments to the subprocess
1916 and to make Emacs wait for the subprocess to terminate and pass
1917 back a result code. The bulk of the code here packs the arguments
1918 into one message to be passed together with the high level event.
1919 Emacs also sometimes starts a subprocess using a shell to perform
1920 wildcard filename expansion. Since we don't really have a shell on
1921 the Mac, this case is detected and the starting of the shell is
1922 by-passed. We really need to add code here to do filename
1923 expansion to support such functionality. */
1924
1925 int
1926 run_mac_command (argv, workdir, infn, outfn, errfn)
1927 unsigned char **argv;
1928 const char *workdir;
1929 const char *infn, *outfn, *errfn;
1930 {
1931 char macappname[MAXPATHLEN+1], macworkdir[MAXPATHLEN+1];
1932 char macinfn[MAXPATHLEN+1], macoutfn[MAXPATHLEN+1], macerrfn[MAXPATHLEN+1];
1933 int paramlen, argc, newargc, j, retries;
1934 char **newargv, *param, *p;
1935 OSErr iErr;
1936 FSSpec spec;
1937 LaunchParamBlockRec lpbr;
1938 EventRecord send_event, reply_event;
1939 RgnHandle cursor_region_handle;
1940 TargetID targ;
1941 unsigned long ref_con, len;
1942
1943 if (posix_to_mac_pathname (workdir, macworkdir, MAXPATHLEN+1) == 0)
1944 return -1;
1945 if (posix_to_mac_pathname (infn, macinfn, MAXPATHLEN+1) == 0)
1946 return -1;
1947 if (posix_to_mac_pathname (outfn, macoutfn, MAXPATHLEN+1) == 0)
1948 return -1;
1949 if (posix_to_mac_pathname (errfn, macerrfn, MAXPATHLEN+1) == 0)
1950 return -1;
1951
1952 paramlen = strlen (macworkdir) + strlen (macinfn) + strlen (macoutfn)
1953 + strlen (macerrfn) + 4; /* count nulls at end of strings */
1954
1955 argc = 0;
1956 while (argv[argc])
1957 argc++;
1958
1959 if (argc == 0)
1960 return -1;
1961
1962 /* If a subprocess is invoked with a shell, we receive 3 arguments
1963 of the form: "<path to emacs bins>/sh" "-c" "<path to emacs
1964 bins>/<command> <command args>" */
1965 j = strlen (argv[0]);
1966 if (j >= 3 && strcmp (argv[0]+j-3, "/sh") == 0
1967 && argc == 3 && strcmp (argv[1], "-c") == 0)
1968 {
1969 char *command, *t, tempmacpathname[MAXPATHLEN+1];
1970
1971 /* The arguments for the command in argv[2] are separated by
1972 spaces. Count them and put the count in newargc. */
1973 command = (char *) alloca (strlen (argv[2])+2);
1974 strcpy (command, argv[2]);
1975 if (command[strlen (command) - 1] != ' ')
1976 strcat (command, " ");
1977
1978 t = command;
1979 newargc = 0;
1980 t = mystrchr (t, ' ');
1981 while (t)
1982 {
1983 newargc++;
1984 t = mystrchr (t+1, ' ');
1985 }
1986
1987 newargv = (char **) alloca (sizeof (char *) * newargc);
1988
1989 t = command;
1990 for (j = 0; j < newargc; j++)
1991 {
1992 newargv[j] = (char *) alloca (strlen (t) + 1);
1993 mystrcpy (newargv[j], t);
1994
1995 t = mystrtok (t);
1996 paramlen += strlen (newargv[j]) + 1;
1997 }
1998
1999 if (strncmp (newargv[0], "~emacs/", 7) == 0)
2000 {
2001 if (posix_to_mac_pathname (newargv[0], tempmacpathname, MAXPATHLEN+1)
2002 == 0)
2003 return -1;
2004 }
2005 else
2006 { /* sometimes Emacs call "sh" without a path for the command */
2007 #if 0
2008 char *t = (char *) alloca (strlen (newargv[0]) + 7 + 1);
2009 strcpy (t, "~emacs/");
2010 strcat (t, newargv[0]);
2011 #endif
2012 Lisp_Object path;
2013 openp (Vexec_path, build_string (newargv[0]), Vexec_suffixes, &path,
2014 1);
2015
2016 if (NILP (path))
2017 return -1;
2018 if (posix_to_mac_pathname (XSTRING (path)->data, tempmacpathname,
2019 MAXPATHLEN+1) == 0)
2020 return -1;
2021 }
2022 strcpy (macappname, tempmacpathname);
2023 }
2024 else
2025 {
2026 if (posix_to_mac_pathname (argv[0], macappname, MAXPATHLEN+1) == 0)
2027 return -1;
2028
2029 newargv = (char **) alloca (sizeof (char *) * argc);
2030 newargc = argc;
2031 for (j = 1; j < argc; j++)
2032 {
2033 if (strncmp (argv[j], "~emacs/", 7) == 0)
2034 {
2035 char *t = strchr (argv[j], ' ');
2036 if (t)
2037 {
2038 char tempcmdname[MAXPATHLEN+1], tempmaccmdname[MAXPATHLEN+1];
2039 strncpy (tempcmdname, argv[j], t-argv[j]);
2040 tempcmdname[t-argv[j]] = '\0';
2041 if (posix_to_mac_pathname (tempcmdname, tempmaccmdname,
2042 MAXPATHLEN+1) == 0)
2043 return -1;
2044 newargv[j] = (char *) alloca (strlen (tempmaccmdname)
2045 + strlen (t) + 1);
2046 strcpy (newargv[j], tempmaccmdname);
2047 strcat (newargv[j], t);
2048 }
2049 else
2050 {
2051 char tempmaccmdname[MAXPATHLEN+1];
2052 if (posix_to_mac_pathname (argv[j], tempmaccmdname,
2053 MAXPATHLEN+1) == 0)
2054 return -1;
2055 newargv[j] = (char *) alloca (strlen (tempmaccmdname)+1);
2056 strcpy (newargv[j], tempmaccmdname);
2057 }
2058 }
2059 else
2060 newargv[j] = argv[j];
2061 paramlen += strlen (newargv[j]) + 1;
2062 }
2063 }
2064
2065 /* After expanding all the arguments, we now know the length of the
2066 parameter block to be sent to the subprocess as a message
2067 attached to the HLE. */
2068 param = (char *) malloc (paramlen + 1);
2069 if (!param)
2070 return -1;
2071
2072 p = param;
2073 *p++ = newargc;
2074 /* first byte of message contains number of arguments for command */
2075 strcpy (p, macworkdir);
2076 p += strlen (macworkdir);
2077 *p++ = '\0';
2078 /* null terminate strings sent so it's possible to use strcpy over there */
2079 strcpy (p, macinfn);
2080 p += strlen (macinfn);
2081 *p++ = '\0';
2082 strcpy (p, macoutfn);
2083 p += strlen (macoutfn);
2084 *p++ = '\0';
2085 strcpy (p, macerrfn);
2086 p += strlen (macerrfn);
2087 *p++ = '\0';
2088 for (j = 1; j < newargc; j++)
2089 {
2090 strcpy (p, newargv[j]);
2091 p += strlen (newargv[j]);
2092 *p++ = '\0';
2093 }
2094
2095 c2pstr (macappname);
2096
2097 iErr = FSMakeFSSpec (0, 0, macappname, &spec);
2098
2099 if (iErr != noErr)
2100 {
2101 free (param);
2102 return -1;
2103 }
2104
2105 lpbr.launchBlockID = extendedBlock;
2106 lpbr.launchEPBLength = extendedBlockLen;
2107 lpbr.launchControlFlags = launchContinue + launchNoFileFlags;
2108 lpbr.launchAppSpec = &spec;
2109 lpbr.launchAppParameters = NULL;
2110
2111 iErr = LaunchApplication (&lpbr); /* call the subprocess */
2112 if (iErr != noErr)
2113 {
2114 free (param);
2115 return -1;
2116 }
2117
2118 send_event.what = kHighLevelEvent;
2119 send_event.message = kEmacsSubprocessSend;
2120 /* Event ID stored in "where" unused */
2121
2122 retries = 3;
2123 /* OS may think current subprocess has terminated if previous one
2124 terminated recently. */
2125 do
2126 {
2127 iErr = PostHighLevelEvent (&send_event, &lpbr.launchProcessSN, 0, param,
2128 paramlen + 1, receiverIDisPSN);
2129 }
2130 while (iErr == sessClosedErr && retries-- > 0);
2131
2132 if (iErr != noErr)
2133 {
2134 free (param);
2135 return -1;
2136 }
2137
2138 cursor_region_handle = NewRgn ();
2139
2140 /* Wait for the subprocess to finish, when it will send us a ERPY
2141 high level event. */
2142 while (1)
2143 if (WaitNextEvent (highLevelEventMask, &reply_event, 180,
2144 cursor_region_handle)
2145 && reply_event.message == kEmacsSubprocessReply)
2146 break;
2147
2148 /* The return code is sent through the refCon */
2149 iErr = AcceptHighLevelEvent (&targ, &ref_con, NULL, &len);
2150 if (iErr != noErr)
2151 {
2152 DisposeHandle ((Handle) cursor_region_handle);
2153 free (param);
2154 return -1;
2155 }
2156
2157 DisposeHandle ((Handle) cursor_region_handle);
2158 free (param);
2159
2160 return ref_con;
2161 }
2162
2163
2164 DIR *
2165 opendir (const char *dirname)
2166 {
2167 char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
2168 char mac_pathname[MAXPATHLEN+1], vol_name[MAXPATHLEN+1];
2169 DIR *dirp;
2170 CInfoPBRec cipb;
2171 HVolumeParam vpb;
2172 int len, vol_name_len;
2173
2174 if (find_true_pathname (dirname, true_pathname, MAXPATHLEN+1) == -1)
2175 return 0;
2176
2177 len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
2178 if (len > -1)
2179 fully_resolved_name[len] = '\0';
2180 else
2181 strcpy (fully_resolved_name, true_pathname);
2182
2183 dirp = (DIR *) malloc (sizeof(DIR));
2184 if (!dirp)
2185 return 0;
2186
2187 /* Handle special case when dirname is "/": sets up for readir to
2188 get all mount volumes. */
2189 if (strcmp (fully_resolved_name, "/") == 0)
2190 {
2191 dirp->getting_volumes = 1; /* special all mounted volumes DIR struct */
2192 dirp->current_index = 1; /* index for first volume */
2193 return dirp;
2194 }
2195
2196 /* Handle typical cases: not accessing all mounted volumes. */
2197 if (!posix_to_mac_pathname (fully_resolved_name, mac_pathname, MAXPATHLEN+1))
2198 return 0;
2199
2200 /* Emacs calls opendir without the trailing '/', Mac needs trailing ':' */
2201 len = strlen (mac_pathname);
2202 if (mac_pathname[len - 1] != ':' && len < MAXPATHLEN)
2203 strcat (mac_pathname, ":");
2204
2205 /* Extract volume name */
2206 vol_name_len = strchr (mac_pathname, ':') - mac_pathname;
2207 strncpy (vol_name, mac_pathname, vol_name_len);
2208 vol_name[vol_name_len] = '\0';
2209 strcat (vol_name, ":");
2210
2211 c2pstr (mac_pathname);
2212 cipb.hFileInfo.ioNamePtr = mac_pathname;
2213 /* using full pathname so vRefNum and DirID ignored */
2214 cipb.hFileInfo.ioVRefNum = 0;
2215 cipb.hFileInfo.ioDirID = 0;
2216 cipb.hFileInfo.ioFDirIndex = 0;
2217 /* set to 0 to get information about specific dir or file */
2218
2219 errno = PBGetCatInfo (&cipb, false);
2220 if (errno != noErr)
2221 {
2222 errno = ENOENT;
2223 return 0;
2224 }
2225
2226 if (!(cipb.hFileInfo.ioFlAttrib & 0x10)) /* bit 4 = 1 for directories */
2227 return 0; /* not a directory */
2228
2229 dirp->dir_id = cipb.dirInfo.ioDrDirID; /* used later in readdir */
2230 dirp->getting_volumes = 0;
2231 dirp->current_index = 1; /* index for first file/directory */
2232
2233 c2pstr (vol_name);
2234 vpb.ioNamePtr = vol_name;
2235 /* using full pathname so vRefNum and DirID ignored */
2236 vpb.ioVRefNum = 0;
2237 vpb.ioVolIndex = -1;
2238 errno = PBHGetVInfo ((union HParamBlockRec *) &vpb, false);
2239 if (errno != noErr)
2240 {
2241 errno = ENOENT;
2242 return 0;
2243 }
2244
2245 dirp->vol_ref_num = vpb.ioVRefNum;
2246
2247 return dirp;
2248 }
2249
2250 int
2251 closedir (DIR *dp)
2252 {
2253 free (dp);
2254
2255 return 0;
2256 }
2257
2258
2259 struct dirent *
2260 readdir (DIR *dp)
2261 {
2262 HParamBlockRec hpblock;
2263 CInfoPBRec cipb;
2264 static struct dirent s_dirent;
2265 static Str255 s_name;
2266 int done;
2267 char *p;
2268
2269 /* Handle the root directory containing the mounted volumes. Call
2270 PBHGetVInfo specifying an index to obtain the info for a volume.
2271 PBHGetVInfo returns an error when it receives an index beyond the
2272 last volume, at which time we should return a nil dirent struct
2273 pointer. */
2274 if (dp->getting_volumes)
2275 {
2276 hpblock.volumeParam.ioNamePtr = s_name;
2277 hpblock.volumeParam.ioVRefNum = 0;
2278 hpblock.volumeParam.ioVolIndex = dp->current_index;
2279
2280 errno = PBHGetVInfo (&hpblock, false);
2281 if (errno != noErr)
2282 {
2283 errno = ENOENT;
2284 return 0;
2285 }
2286
2287 p2cstr (s_name);
2288 strcat (s_name, "/"); /* need "/" for stat to work correctly */
2289
2290 dp->current_index++;
2291
2292 s_dirent.d_ino = hpblock.volumeParam.ioVRefNum;
2293 s_dirent.d_name = s_name;
2294
2295 return &s_dirent;
2296 }
2297 else
2298 {
2299 cipb.hFileInfo.ioVRefNum = dp->vol_ref_num;
2300 cipb.hFileInfo.ioNamePtr = s_name;
2301 /* location to receive filename returned */
2302
2303 /* return only visible files */
2304 done = false;
2305 while (!done)
2306 {
2307 cipb.hFileInfo.ioDirID = dp->dir_id;
2308 /* directory ID found by opendir */
2309 cipb.hFileInfo.ioFDirIndex = dp->current_index;
2310
2311 errno = PBGetCatInfo (&cipb, false);
2312 if (errno != noErr)
2313 {
2314 errno = ENOENT;
2315 return 0;
2316 }
2317
2318 /* insist on an visibile entry */
2319 if (cipb.hFileInfo.ioFlAttrib & 0x10) /* directory? */
2320 done = !(cipb.dirInfo.ioDrUsrWds.frFlags & fInvisible);
2321 else
2322 done = !(cipb.hFileInfo.ioFlFndrInfo.fdFlags & fInvisible);
2323
2324 dp->current_index++;
2325 }
2326
2327 p2cstr (s_name);
2328
2329 p = s_name;
2330 while (*p)
2331 {
2332 if (*p == '/')
2333 *p = ':';
2334 p++;
2335 }
2336
2337 s_dirent.d_ino = cipb.dirInfo.ioDrDirID;
2338 /* value unimportant: non-zero for valid file */
2339 s_dirent.d_name = s_name;
2340
2341 return &s_dirent;
2342 }
2343 }
2344
2345
2346 char *
2347 getwd (char *path)
2348 {
2349 char mac_pathname[MAXPATHLEN+1];
2350 Str255 directory_name;
2351 OSErr errno;
2352 CInfoPBRec cipb;
2353
2354 if (path_from_vol_dir_name (mac_pathname, 255, 0, 0, "\p") == 0)
2355 return NULL;
2356
2357 if (mac_to_posix_pathname (mac_pathname, path, MAXPATHLEN+1) == 0)
2358 return 0;
2359 else
2360 return path;
2361 }
2362
2363
2364 void
2365 initialize_applescript ()
2366 {
2367 AEDesc null_desc;
2368 OSAError osaerror;
2369
2370 /* if open fails, as_scripting_component is set to NULL. Its
2371 subsequent use in OSA calls will fail with badComponentInstance
2372 error. */
2373 as_scripting_component = OpenDefaultComponent (kOSAComponentType,
2374 kAppleScriptSubtype);
2375
2376 null_desc.descriptorType = typeNull;
2377 null_desc.dataHandle = 0;
2378 osaerror = OSAMakeContext (as_scripting_component, &null_desc,
2379 kOSANullScript, &as_script_context);
2380 if (osaerror)
2381 as_script_context = kOSANullScript;
2382 /* use default context if create fails */
2383 }
2384
2385
2386 void terminate_applescript()
2387 {
2388 OSADispose (as_scripting_component, as_script_context);
2389 CloseComponent (as_scripting_component);
2390 }
2391
2392
2393 /* Compile and execute the AppleScript SCRIPT and return the error
2394 status as function value. A zero is returned if compilation and
2395 execution is successful, in which case RESULT returns a pointer to
2396 a string containing the resulting script value. Otherwise, the Mac
2397 error code is returned and RESULT returns a pointer to an error
2398 string. In both cases the caller should deallocate the storage
2399 used by the string pointed to by RESULT if it is non-NULL. For
2400 documentation on the MacOS scripting architecture, see Inside
2401 Macintosh - Interapplication Communications: Scripting Components. */
2402
2403 static long
2404 do_applescript (char *script, char **result)
2405 {
2406 AEDesc script_desc, result_desc, error_desc;
2407 OSErr error;
2408 OSAError osaerror;
2409 long length;
2410
2411 *result = 0;
2412
2413 error = AECreateDesc (typeChar, script, strlen(script), &script_desc);
2414 if (error)
2415 return error;
2416
2417 osaerror = OSADoScript (as_scripting_component, &script_desc, kOSANullScript,
2418 typeChar, kOSAModeNull, &result_desc);
2419
2420 if (osaerror == errOSAScriptError)
2421 {
2422 /* error executing AppleScript: retrieve error message */
2423 if (!OSAScriptError (as_scripting_component, kOSAErrorMessage, typeChar,
2424 &error_desc))
2425 {
2426 HLock (error_desc.dataHandle);
2427 length = GetHandleSize(error_desc.dataHandle);
2428 *result = (char *) xmalloc (length + 1);
2429 if (*result)
2430 {
2431 memcpy (*result, *(error_desc.dataHandle), length);
2432 *(*result + length) = '\0';
2433 }
2434 HUnlock (error_desc.dataHandle);
2435 AEDisposeDesc (&error_desc);
2436 }
2437 }
2438 else if (osaerror == noErr) /* success: retrieve resulting script value */
2439 {
2440 HLock (result_desc.dataHandle);
2441 length = GetHandleSize(result_desc.dataHandle);
2442 *result = (char *) xmalloc (length + 1);
2443 if (*result)
2444 {
2445 memcpy (*result, *(result_desc.dataHandle), length);
2446 *(*result + length) = '\0';
2447 }
2448 HUnlock (result_desc.dataHandle);
2449 }
2450
2451 AEDisposeDesc (&script_desc);
2452 AEDisposeDesc (&result_desc);
2453
2454 return osaerror;
2455 }
2456
2457
2458 DEFUN ("do-applescript", Fdo_applescript, Sdo_applescript, 1, 1, 0,
2459 "Compile and execute AppleScript SCRIPT and retrieve and return the\n\
2460 result. If compilation and execution are successful, the resulting script\n\
2461 value is returned as a string. Otherwise the function aborts and\n\
2462 displays the error message returned by the AppleScript scripting\n\
2463 component.")
2464 (script)
2465 Lisp_Object script;
2466 {
2467 char *result, *temp;
2468 Lisp_Object lisp_result;
2469 long status;
2470
2471 CHECK_STRING (script, 0);
2472
2473 status = do_applescript (XSTRING (script)->data, &result);
2474 if (status)
2475 {
2476 if (!result)
2477 error ("AppleScript error %ld", status);
2478 else
2479 {
2480 /* Unfortunately only OSADoScript in do_applescript knows how
2481 how large the resulting script value or error message is
2482 going to be and therefore as caller memory must be
2483 deallocated here. It is necessary to free the error
2484 message before calling error to avoid a memory leak. */
2485 temp = (char *) alloca (strlen (result) + 1);
2486 strcpy (temp, result);
2487 xfree (result);
2488 error (temp);
2489 }
2490 }
2491 else
2492 {
2493 lisp_result = build_string (result);
2494 xfree (result);
2495 return lisp_result;
2496 }
2497 }
2498
2499
2500 DEFUN ("mac-file-name-to-posix", Fmac_file_name_to_posix, Smac_file_name_to_posix, 1,
2501 1, 0,
2502 "Convert Macintosh filename to Posix form.")
2503 (mac_filename)
2504 Lisp_Object mac_filename;
2505 {
2506 char posix_filename[MAXPATHLEN+1];
2507
2508 CHECK_STRING (mac_filename, 0);
2509
2510 if (mac_to_posix_pathname (XSTRING (mac_filename)->data, posix_filename,
2511 MAXPATHLEN))
2512 return build_string (posix_filename);
2513 else
2514 return Qnil;
2515 }
2516
2517
2518 DEFUN ("posix-file-name-to-mac", Fposix_file_name_to_mac, Sposix_file_name_to_mac, 1,
2519 1, 0,
2520 "Convert Unix filename to Mac form.")
2521 (posix_filename)
2522 Lisp_Object posix_filename;
2523 {
2524 char mac_filename[MAXPATHLEN+1];
2525
2526 CHECK_STRING (posix_filename, 0);
2527
2528 if (posix_to_mac_pathname (XSTRING (posix_filename)->data, mac_filename,
2529 MAXPATHLEN))
2530 return build_string (mac_filename);
2531 else
2532 return Qnil;
2533 }
2534
2535
2536 /* set interprogram-paste-function to mac-paste-function in mac-win.el
2537 to enable Emacs to obtain the contents of the Mac clipboard. */
2538 DEFUN ("mac-paste-function", Fmac_paste_function, Smac_paste_function, 0, 0, 0,
2539 "Return the contents of the Mac clipboard as a string.")
2540 ()
2541 {
2542 Lisp_Object value;
2543 Handle my_handle;
2544 long scrap_offset, rc, i;
2545
2546 my_handle = NewHandle (0); /* allocate 0-length data area */
2547
2548 rc = GetScrap (my_handle, 'TEXT', &scrap_offset);
2549 if (rc < 0)
2550 return Qnil;
2551
2552 HLock (my_handle);
2553
2554 /* Emacs expects clipboard contents have Unix-style eol's */
2555 for (i = 0; i < rc; i++)
2556 if ((*my_handle)[i] == '\r')
2557 (*my_handle)[i] = '\n';
2558
2559 value = make_string (*my_handle, rc);
2560
2561 HUnlock (my_handle);
2562
2563 DisposeHandle (my_handle);
2564
2565 return value;
2566 }
2567
2568
2569 /* set interprogram-cut-function to mac-cut-function in mac-win.el
2570 to enable Emacs to write the top of the kill-ring to the Mac clipboard. */
2571 DEFUN ("mac-cut-function", Fmac_cut_function, Smac_cut_function, 1, 2, 0,
2572 "Put the value of the string parameter to the Mac clipboard.")
2573 (value, push)
2574 Lisp_Object value, push;
2575 {
2576 char *buf;
2577 int len, i;
2578
2579 /* fixme: ignore the push flag for now */
2580
2581 CHECK_STRING (value, 0);
2582
2583 len = XSTRING (value)->size;
2584 buf = (char *) alloca (len);
2585 bcopy(XSTRING (value)->data, buf, len);
2586
2587 /* convert to Mac-style eol's before sending to clipboard */
2588 for (i = 0; i < len; i++)
2589 if (buf[i] == '\n')
2590 buf[i] = '\r';
2591
2592 ZeroScrap ();
2593 PutScrap (len, 'TEXT', buf);
2594
2595 return Qnil;
2596 }
2597
2598
2599 DEFUN ("x-selection-exists-p", Fx_selection_exists_p, Sx_selection_exists_p,
2600 0, 1, 0,
2601 "Whether there is an owner for the given X Selection.\n\
2602 The arg should be the name of the selection in question, typically one of\n\
2603 the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.\n\
2604 \(Those are literal upper-case symbol names, since that's what X expects.)\n\
2605 For convenience, the symbol nil is the same as `PRIMARY',\n\
2606 and t is the same as `SECONDARY'.")
2607 (selection)
2608 Lisp_Object selection;
2609 {
2610 CHECK_SYMBOL (selection, 0);
2611
2612 /* Return nil for PRIMARY and SECONDARY selections; for CLIPBOARD, check
2613 if the clipboard currently has valid text format contents. */
2614
2615 if (EQ (selection, QCLIPBOARD))
2616 {
2617 Lisp_Object val = Qnil;
2618 Lisp_Object value;
2619 Handle my_handle;
2620 long rc, scrap_offset;
2621
2622 my_handle = NewHandle (0);
2623
2624 rc = GetScrap (my_handle, 'TEXT', &scrap_offset);
2625 if (rc >= 0)
2626 val = Qt;
2627
2628 DisposeHandle (my_handle);
2629
2630 return val;
2631 }
2632 return Qnil;
2633 }
2634
2635
2636 void
2637 syms_of_mac ()
2638 {
2639 QCLIPBOARD = intern ("CLIPBOARD");
2640 staticpro (&QCLIPBOARD);
2641
2642 defsubr (&Smac_paste_function);
2643 defsubr (&Smac_cut_function);
2644 defsubr (&Sx_selection_exists_p);
2645
2646 defsubr (&Sdo_applescript);
2647 defsubr (&Smac_file_name_to_posix);
2648 defsubr (&Sposix_file_name_to_mac);
2649 }