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