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