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