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