+ CFTypeID type_id = CFGetTypeID (plist);
+ Lisp_Object tag = Qnil, result = Qnil;
+ struct gcpro gcpro1, gcpro2;
+
+ GCPRO2 (tag, result);
+
+ if (type_id == CFStringGetTypeID ())
+ {
+ tag = Qstring;
+ result = cfstring_to_lisp (plist);
+ }
+ else if (type_id == CFNumberGetTypeID ())
+ {
+ tag = Qnumber;
+ result = cfnumber_to_lisp (plist);
+ }
+ else if (type_id == CFBooleanGetTypeID ())
+ {
+ tag = Qboolean;
+ result = cfboolean_to_lisp (plist);
+ }
+ else if (type_id == CFDateGetTypeID ())
+ {
+ tag = Qdate;
+ result = cfdate_to_lisp (plist);
+ }
+ else if (type_id == CFDataGetTypeID ())
+ {
+ tag = Qdata;
+ result = cfdata_to_lisp (plist);
+ }
+ else if (type_id == CFArrayGetTypeID ())
+ {
+ CFIndex index, count = CFArrayGetCount (plist);
+
+ tag = Qarray;
+ result = Fmake_vector (make_number (count), Qnil);
+ for (index = 0; index < count; index++)
+ XVECTOR (result)->contents[index] =
+ cfproperty_list_to_lisp (CFArrayGetValueAtIndex (plist, index),
+ with_tag, hash_bound);
+ }
+ else if (type_id == CFDictionaryGetTypeID ())
+ {
+ struct cfdict_context context;
+ CFIndex count = CFDictionaryGetCount (plist);
+
+ tag = Qdictionary;
+ context.result = &result;
+ context.with_tag = with_tag;
+ context.hash_bound = hash_bound;
+ if (hash_bound < 0 || count < hash_bound)
+ {
+ result = Qnil;
+ CFDictionaryApplyFunction (plist, cfdictionary_add_to_list,
+ &context);
+ }
+ else
+ {
+ result = make_hash_table (Qequal,
+ make_number (count),
+ make_float (DEFAULT_REHASH_SIZE),
+ make_float (DEFAULT_REHASH_THRESHOLD),
+ Qnil, Qnil, Qnil);
+ CFDictionaryApplyFunction (plist, cfdictionary_puthash,
+ &context);
+ }
+ }
+ else
+ abort ();
+
+ UNGCPRO;
+
+ if (with_tag)
+ result = Fcons (tag, result);
+
+ return result;
+}
+#endif
+
+\f
+/***********************************************************************
+ Emulation of the X Resource Manager
+ ***********************************************************************/
+
+/* Parser functions for resource lines. Each function takes an
+ address of a variable whose value points to the head of a string.
+ The value will be advanced so that it points to the next character
+ of the parsed part when the function returns.
+
+ A resource name such as "Emacs*font" is parsed into a non-empty
+ list called `quarks'. Each element is either a Lisp string that
+ represents a concrete component, a Lisp symbol LOOSE_BINDING
+ (actually Qlambda) that represents any number (>=0) of intervening
+ components, or a Lisp symbol SINGLE_COMPONENT (actually Qquote)
+ that represents as any single component. */
+
+#define P (*p)
+
+#define LOOSE_BINDING Qlambda /* '*' ("L"oose) */
+#define SINGLE_COMPONENT Qquote /* '?' ("Q"uestion) */
+
+static void
+skip_white_space (p)
+ char **p;
+{
+ /* WhiteSpace = {<space> | <horizontal tab>} */
+ while (*P == ' ' || *P == '\t')
+ P++;
+}
+
+static int
+parse_comment (p)
+ char **p;
+{
+ /* Comment = "!" {<any character except null or newline>} */
+ if (*P == '!')
+ {
+ P++;
+ while (*P)
+ if (*P++ == '\n')
+ break;
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/* Don't interpret filename. Just skip until the newline. */
+static int
+parse_include_file (p)
+ char **p;
+{
+ /* IncludeFile = "#" WhiteSpace "include" WhiteSpace FileName WhiteSpace */
+ if (*P == '#')
+ {
+ P++;
+ while (*P)
+ if (*P++ == '\n')
+ break;
+ return 1;
+ }
+ else
+ return 0;
+}
+
+static char
+parse_binding (p)
+ char **p;
+{
+ /* Binding = "." | "*" */
+ if (*P == '.' || *P == '*')
+ {
+ char binding = *P++;
+
+ while (*P == '.' || *P == '*')
+ if (*P++ == '*')
+ binding = '*';
+ return binding;
+ }
+ else
+ return '\0';
+}
+
+static Lisp_Object
+parse_component (p)
+ char **p;
+{
+ /* Component = "?" | ComponentName
+ ComponentName = NameChar {NameChar}
+ NameChar = "a"-"z" | "A"-"Z" | "0"-"9" | "_" | "-" */
+ if (*P == '?')
+ {
+ P++;
+ return SINGLE_COMPONENT;
+ }
+ else if (isalnum (*P) || *P == '_' || *P == '-')
+ {
+ char *start = P++;
+
+ while (isalnum (*P) || *P == '_' || *P == '-')
+ P++;
+
+ return make_unibyte_string (start, P - start);
+ }
+ else
+ return Qnil;
+}
+
+static Lisp_Object
+parse_resource_name (p)
+ char **p;
+{
+ Lisp_Object result = Qnil, component;
+ char binding;
+
+ /* ResourceName = [Binding] {Component Binding} ComponentName */
+ if (parse_binding (p) == '*')
+ result = Fcons (LOOSE_BINDING, result);
+
+ component = parse_component (p);
+ if (NILP (component))
+ return Qnil;
+
+ result = Fcons (component, result);
+ while ((binding = parse_binding (p)) != '\0')
+ {
+ if (binding == '*')
+ result = Fcons (LOOSE_BINDING, result);
+ component = parse_component (p);
+ if (NILP (component))
+ return Qnil;
+ else
+ result = Fcons (component, result);
+ }
+
+ /* The final component should not be '?'. */
+ if (EQ (component, SINGLE_COMPONENT))
+ return Qnil;
+
+ return Fnreverse (result);
+}
+
+static Lisp_Object
+parse_value (p)
+ char **p;
+{
+ char *q, *buf;
+ Lisp_Object seq = Qnil, result;
+ int buf_len, total_len = 0, len, continue_p;
+
+ q = strchr (P, '\n');
+ buf_len = q ? q - P : strlen (P);
+ buf = xmalloc (buf_len);
+
+ while (1)
+ {
+ q = buf;
+ continue_p = 0;
+ while (*P)
+ {
+ if (*P == '\n')
+ {
+ P++;
+ break;
+ }
+ else if (*P == '\\')
+ {
+ P++;
+ if (*P == '\0')
+ break;
+ else if (*P == '\n')
+ {
+ P++;
+ continue_p = 1;
+ break;
+ }
+ else if (*P == 'n')
+ {
+ *q++ = '\n';
+ P++;
+ }
+ else if ('0' <= P[0] && P[0] <= '7'
+ && '0' <= P[1] && P[1] <= '7'
+ && '0' <= P[2] && P[2] <= '7')
+ {
+ *q++ = (P[0] - '0' << 6) + (P[1] - '0' << 3) + (P[2] - '0');
+ P += 3;
+ }
+ else
+ *q++ = *P++;
+ }
+ else
+ *q++ = *P++;
+ }
+ len = q - buf;
+ seq = Fcons (make_unibyte_string (buf, len), seq);
+ total_len += len;
+
+ if (continue_p)
+ {
+ q = strchr (P, '\n');
+ len = q ? q - P : strlen (P);
+ if (len > buf_len)
+ {
+ xfree (buf);
+ buf_len = len;
+ buf = xmalloc (buf_len);
+ }
+ }
+ else
+ break;
+ }
+ xfree (buf);
+
+ if (SBYTES (XCAR (seq)) == total_len)
+ return make_string (SDATA (XCAR (seq)), total_len);
+ else
+ {
+ buf = xmalloc (total_len);
+ q = buf + total_len;
+ for (; CONSP (seq); seq = XCDR (seq))
+ {
+ len = SBYTES (XCAR (seq));
+ q -= len;
+ memcpy (q, SDATA (XCAR (seq)), len);
+ }
+ result = make_string (buf, total_len);
+ xfree (buf);
+ return result;
+ }
+}
+
+static Lisp_Object
+parse_resource_line (p)
+ char **p;
+{
+ Lisp_Object quarks, value;
+
+ /* ResourceLine = Comment | IncludeFile | ResourceSpec | <empty line> */
+ if (parse_comment (p) || parse_include_file (p))
+ return Qnil;
+
+ /* ResourceSpec = WhiteSpace ResourceName WhiteSpace ":" WhiteSpace Value */
+ skip_white_space (p);
+ quarks = parse_resource_name (p);
+ if (NILP (quarks))
+ goto cleanup;
+ skip_white_space (p);
+ if (*P != ':')
+ goto cleanup;
+ P++;
+ skip_white_space (p);
+ value = parse_value (p);
+ return Fcons (quarks, value);
+
+ cleanup:
+ /* Skip the remaining data as a dummy value. */
+ parse_value (p);
+ return Qnil;
+}
+
+#undef P
+
+/* Equivalents of X Resource Manager functions.
+
+ An X Resource Database acts as a collection of resource names and
+ associated values. It is implemented as a trie on quarks. Namely,
+ each edge is labeled by either a string, LOOSE_BINDING, or
+ SINGLE_COMPONENT. Each node has a node id, which is a unique
+ nonnegative integer, and the root node id is 0. A database is
+ implemented as a hash table that maps a pair (SRC-NODE-ID .
+ EDGE-LABEL) to DEST-NODE-ID. It also holds a maximum node id used
+ in the table as a value for HASHKEY_MAX_NID. A value associated to
+ a node is recorded as a value for the node id. */
+
+#define HASHKEY_MAX_NID (make_number (0))
+
+static XrmDatabase
+xrm_create_database ()
+{
+ XrmDatabase database;
+
+ database = make_hash_table (Qequal, make_number (DEFAULT_HASH_SIZE),
+ make_float (DEFAULT_REHASH_SIZE),
+ make_float (DEFAULT_REHASH_THRESHOLD),
+ Qnil, Qnil, Qnil);
+ Fputhash (HASHKEY_MAX_NID, make_number (0), database);
+
+ return database;
+}
+
+static void
+xrm_q_put_resource (database, quarks, value)
+ XrmDatabase database;
+ Lisp_Object quarks, value;
+{
+ struct Lisp_Hash_Table *h = XHASH_TABLE (database);
+ unsigned hash_code;
+ int max_nid, i;
+ Lisp_Object node_id, key;
+
+ max_nid = XINT (Fgethash (HASHKEY_MAX_NID, database, Qnil));
+
+ XSETINT (node_id, 0);
+ for (; CONSP (quarks); quarks = XCDR (quarks))
+ {
+ key = Fcons (node_id, XCAR (quarks));
+ i = hash_lookup (h, key, &hash_code);
+ if (i < 0)
+ {
+ max_nid++;
+ XSETINT (node_id, max_nid);
+ hash_put (h, key, node_id, hash_code);
+ }
+ else
+ node_id = HASH_VALUE (h, i);
+ }
+ Fputhash (node_id, value, database);
+
+ Fputhash (HASHKEY_MAX_NID, make_number (max_nid), database);
+}
+
+/* Merge multiple resource entries specified by DATA into a resource
+ database DATABASE. DATA points to the head of a null-terminated
+ string consisting of multiple resource lines. It's like a
+ combination of XrmGetStringDatabase and XrmMergeDatabases. */
+
+void
+xrm_merge_string_database (database, data)
+ XrmDatabase database;
+ char *data;
+{
+ Lisp_Object quarks_value;
+
+ while (*data)
+ {
+ quarks_value = parse_resource_line (&data);
+ if (!NILP (quarks_value))
+ xrm_q_put_resource (database,
+ XCAR (quarks_value), XCDR (quarks_value));
+ }
+}
+
+static Lisp_Object
+xrm_q_get_resource_1 (database, node_id, quark_name, quark_class)
+ XrmDatabase database;
+ Lisp_Object node_id, quark_name, quark_class;
+{
+ struct Lisp_Hash_Table *h = XHASH_TABLE (database);
+ Lisp_Object key, labels[3], value;
+ int i, k;
+
+ if (!CONSP (quark_name))
+ return Fgethash (node_id, database, Qnil);
+
+ /* First, try tight bindings */
+ labels[0] = XCAR (quark_name);
+ labels[1] = XCAR (quark_class);
+ labels[2] = SINGLE_COMPONENT;
+
+ key = Fcons (node_id, Qnil);
+ for (k = 0; k < sizeof (labels) / sizeof (*labels); k++)
+ {
+ XSETCDR (key, labels[k]);
+ i = hash_lookup (h, key, NULL);
+ if (i >= 0)
+ {
+ value = xrm_q_get_resource_1 (database, HASH_VALUE (h, i),
+ XCDR (quark_name), XCDR (quark_class));
+ if (!NILP (value))
+ return value;
+ }
+ }
+
+ /* Then, try loose bindings */
+ XSETCDR (key, LOOSE_BINDING);
+ i = hash_lookup (h, key, NULL);
+ if (i >= 0)
+ {
+ value = xrm_q_get_resource_1 (database, HASH_VALUE (h, i),
+ quark_name, quark_class);
+ if (!NILP (value))
+ return value;
+ else
+ return xrm_q_get_resource_1 (database, node_id,
+ XCDR (quark_name), XCDR (quark_class));
+ }
+ else
+ return Qnil;
+}
+
+static Lisp_Object
+xrm_q_get_resource (database, quark_name, quark_class)
+ XrmDatabase database;
+ Lisp_Object quark_name, quark_class;
+{
+ return xrm_q_get_resource_1 (database, make_number (0),
+ quark_name, quark_class);
+}
+
+/* Retrieve a resource value for the specified NAME and CLASS from the
+ resource database DATABASE. It corresponds to XrmGetResource. */
+
+Lisp_Object
+xrm_get_resource (database, name, class)
+ XrmDatabase database;
+ char *name, *class;
+{
+ Lisp_Object quark_name, quark_class, tmp;
+ int nn, nc;
+
+ quark_name = parse_resource_name (&name);
+ if (*name != '\0')
+ return Qnil;
+ for (tmp = quark_name, nn = 0; CONSP (tmp); tmp = XCDR (tmp), nn++)
+ if (!STRINGP (XCAR (tmp)))
+ return Qnil;
+
+ quark_class = parse_resource_name (&class);
+ if (*class != '\0')
+ return Qnil;
+ for (tmp = quark_class, nc = 0; CONSP (tmp); tmp = XCDR (tmp), nc++)
+ if (!STRINGP (XCAR (tmp)))
+ return Qnil;
+
+ if (nn != nc)
+ return Qnil;
+ else
+ return xrm_q_get_resource (database, quark_name, quark_class);
+}
+
+#if TARGET_API_MAC_CARBON
+static Lisp_Object
+xrm_cfproperty_list_to_value (plist)
+ CFPropertyListRef plist;
+{
+ CFTypeID type_id = CFGetTypeID (plist);
+
+ if (type_id == CFStringGetTypeID ())
+ return cfstring_to_lisp (plist);
+ else if (type_id == CFNumberGetTypeID ())
+ {
+ CFStringRef string;
+ Lisp_Object result = Qnil;
+
+ string = CFStringCreateWithFormat (NULL, NULL, CFSTR ("%@"), plist);
+ if (string)
+ {
+ result = cfstring_to_lisp (string);
+ CFRelease (string);
+ }
+ return result;
+ }
+ else if (type_id == CFBooleanGetTypeID ())
+ return build_string (CFBooleanGetValue (plist) ? "true" : "false");
+ else if (type_id == CFDataGetTypeID ())
+ return cfdata_to_lisp (plist);
+ else
+ return Qnil;
+}
+#endif
+
+/* Create a new resource database from the preferences for the
+ application APPLICATION. APPLICATION is either a string that
+ specifies an application ID, or NULL that represents the current
+ application. */
+
+XrmDatabase
+xrm_get_preference_database (application)
+ char *application;
+{
+#if TARGET_API_MAC_CARBON
+ CFStringRef app_id, *keys, user_doms[2], host_doms[2];
+ CFMutableSetRef key_set = NULL;
+ CFArrayRef key_array;
+ CFIndex index, count;
+ char *res_name;
+ XrmDatabase database;
+ Lisp_Object quarks = Qnil, value = Qnil;
+ CFPropertyListRef plist;
+ int iu, ih;
+ struct gcpro gcpro1, gcpro2, gcpro3;
+
+ user_doms[0] = kCFPreferencesCurrentUser;
+ user_doms[1] = kCFPreferencesAnyUser;
+ host_doms[0] = kCFPreferencesCurrentHost;
+ host_doms[1] = kCFPreferencesAnyHost;
+
+ database = xrm_create_database ();
+
+ GCPRO3 (database, quarks, value);
+
+ BLOCK_INPUT;
+
+ app_id = kCFPreferencesCurrentApplication;
+ if (application)
+ {
+ app_id = cfstring_create_with_utf8_cstring (application);
+ if (app_id == NULL)
+ goto out;
+ }
+
+ key_set = CFSetCreateMutable (NULL, 0, &kCFCopyStringSetCallBacks);
+ if (key_set == NULL)
+ goto out;
+ for (iu = 0; iu < sizeof (user_doms) / sizeof (*user_doms) ; iu++)
+ for (ih = 0; ih < sizeof (host_doms) / sizeof (*host_doms); ih++)
+ {
+ key_array = CFPreferencesCopyKeyList (app_id, user_doms[iu],
+ host_doms[ih]);
+ if (key_array)
+ {
+ count = CFArrayGetCount (key_array);
+ for (index = 0; index < count; index++)
+ CFSetAddValue (key_set,
+ CFArrayGetValueAtIndex (key_array, index));
+ CFRelease (key_array);
+ }
+ }
+
+ count = CFSetGetCount (key_set);
+ keys = xmalloc (sizeof (CFStringRef) * count);
+ if (keys == NULL)
+ goto out;
+ CFSetGetValues (key_set, (const void **)keys);
+ for (index = 0; index < count; index++)
+ {
+ res_name = SDATA (cfstring_to_lisp (keys[index]));
+ quarks = parse_resource_name (&res_name);
+ if (!(NILP (quarks) || *res_name))
+ {
+ plist = CFPreferencesCopyAppValue (keys[index], app_id);
+ value = xrm_cfproperty_list_to_value (plist);
+ CFRelease (plist);
+ if (!NILP (value))
+ xrm_q_put_resource (database, quarks, value);
+ }
+ }
+
+ xfree (keys);
+ out:
+ if (key_set)
+ CFRelease (key_set);
+ CFRelease (app_id);
+
+ UNBLOCK_INPUT;
+
+ UNGCPRO;
+
+ return database;
+#else
+ return xrm_create_database ();
+#endif
+}
+
+\f
+#ifndef MAC_OSX
+
+/* The following functions with "sys_" prefix are stubs to Unix
+ functions that have already been implemented by CW or MPW. The
+ calls to them in Emacs source course are #define'd to call the sys_
+ versions by the header files s-mac.h. In these stubs pathnames are
+ converted between their Unix and Mac forms. */
+
+
+/* Unix epoch is Jan 1, 1970 while Mac epoch is Jan 1, 1904: 66 years
+ + 17 leap days. These are for adjusting time values returned by
+ MacOS Toolbox functions. */
+
+#define MAC_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
+
+#ifdef __MWERKS__
+#if __MSL__ < 0x6000
+/* CW Pro 5 epoch is Jan 1, 1900 (aaarghhhhh!); remember, 1900 is not
+ a leap year! This is for adjusting time_t values returned by MSL
+ functions. */
+#define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 70 + 17) * 24 * 60 * 60)
+#else /* __MSL__ >= 0x6000 */
+/* CW changes Pro 6 to follow Unix! */
+#define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
+#endif /* __MSL__ >= 0x6000 */
+#elif __MRC__
+/* MPW library functions follow Unix (confused?). */
+#define CW_OR_MPW_UNIX_EPOCH_DIFF ((365L * 66 + 17) * 24 * 60 * 60)
+#else /* not __MRC__ */
+You lose!!!
+#endif /* not __MRC__ */
+
+
+/* Define our own stat function for both MrC and CW. The reason for
+ doing this: "stat" is both the name of a struct and function name:
+ can't use the same trick like that for sys_open, sys_close, etc. to
+ redirect Emacs's calls to our own version that converts Unix style
+ filenames to Mac style filename because all sorts of compilation
+ errors will be generated if stat is #define'd to be sys_stat. */
+
+int
+stat_noalias (const char *path, struct stat *buf)
+{
+ char mac_pathname[MAXPATHLEN+1];
+ CInfoPBRec cipb;
+
+ if (posix_to_mac_pathname (path, mac_pathname, MAXPATHLEN+1) == 0)
+ return -1;
+
+ c2pstr (mac_pathname);
+ cipb.hFileInfo.ioNamePtr = mac_pathname;
+ cipb.hFileInfo.ioVRefNum = 0;
+ cipb.hFileInfo.ioDirID = 0;
+ cipb.hFileInfo.ioFDirIndex = 0;
+ /* set to 0 to get information about specific dir or file */
+
+ errno = PBGetCatInfo (&cipb, false);
+ if (errno == -43) /* -43: fnfErr defined in Errors.h */
+ errno = ENOENT;
+ if (errno != noErr)
+ return -1;
+
+ if (cipb.hFileInfo.ioFlAttrib & 0x10) /* bit 4 = 1 for directories */
+ {
+ buf->st_mode = S_IFDIR | S_IREAD | S_IEXEC;
+
+ if (!(cipb.hFileInfo.ioFlAttrib & 0x1))
+ buf->st_mode |= S_IWRITE; /* bit 1 = 1 for locked files/directories */
+ buf->st_ino = cipb.dirInfo.ioDrDirID;
+ buf->st_dev = cipb.dirInfo.ioVRefNum;
+ buf->st_size = cipb.dirInfo.ioDrNmFls;
+ /* size of dir = number of files and dirs */
+ buf->st_atime
+ = buf->st_mtime
+ = cipb.dirInfo.ioDrMdDat - MAC_UNIX_EPOCH_DIFF;
+ buf->st_ctime = cipb.dirInfo.ioDrCrDat - MAC_UNIX_EPOCH_DIFF;
+ }
+ else
+ {
+ buf->st_mode = S_IFREG | S_IREAD;
+ if (!(cipb.hFileInfo.ioFlAttrib & 0x1))
+ buf->st_mode |= S_IWRITE; /* bit 1 = 1 for locked files/directories */
+ if (cipb.hFileInfo.ioFlFndrInfo.fdType == 'APPL')
+ buf->st_mode |= S_IEXEC;
+ buf->st_ino = cipb.hFileInfo.ioDirID;
+ buf->st_dev = cipb.hFileInfo.ioVRefNum;
+ buf->st_size = cipb.hFileInfo.ioFlLgLen;
+ buf->st_atime
+ = buf->st_mtime
+ = cipb.hFileInfo.ioFlMdDat - MAC_UNIX_EPOCH_DIFF;
+ buf->st_ctime = cipb.hFileInfo.ioFlCrDat - MAC_UNIX_EPOCH_DIFF;
+ }
+
+ if (cipb.hFileInfo.ioFlFndrInfo.fdFlags & 0x8000)
+ {
+ /* identify alias files as symlinks */
+ buf->st_mode &= ~S_IFREG;
+ buf->st_mode |= S_IFLNK;
+ }
+
+ buf->st_nlink = 1;
+ buf->st_uid = getuid ();
+ buf->st_gid = getgid ();
+ buf->st_rdev = 0;
+
+ return 0;
+}
+
+
+int
+lstat (const char *path, struct stat *buf)
+{
+ int result;
+ char true_pathname[MAXPATHLEN+1];
+
+ /* Try looking for the file without resolving aliases first. */
+ if ((result = stat_noalias (path, buf)) >= 0)
+ return result;
+
+ if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
+ return -1;
+
+ return stat_noalias (true_pathname, buf);
+}
+
+
+int
+stat (const char *path, struct stat *sb)
+{
+ int result;
+ char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
+ int len;
+
+ if ((result = stat_noalias (path, sb)) >= 0 &&
+ ! (sb->st_mode & S_IFLNK))
+ return result;
+
+ if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)
+ return -1;
+
+ len = readlink (true_pathname, fully_resolved_name, MAXPATHLEN);
+ if (len > -1)
+ {
+ fully_resolved_name[len] = '\0';
+ /* in fact our readlink terminates strings */
+ return lstat (fully_resolved_name, sb);
+ }
+ else
+ return lstat (true_pathname, sb);
+}
+
+
+#if __MRC__
+/* CW defines fstat in stat.mac.c while MPW does not provide this
+ function. Without the information of how to get from a file
+ descriptor in MPW StdCLib to a Mac OS file spec, it should be hard
+ to implement this function. Fortunately, there is only one place
+ where this function is called in our configuration: in fileio.c,
+ where only the st_dev and st_ino fields are used to determine
+ whether two fildes point to different i-nodes to prevent copying
+ a file onto itself equal. What we have here probably needs
+ improvement. */
+
+int
+fstat (int fildes, struct stat *buf)
+{
+ buf->st_dev = 0;
+ buf->st_ino = fildes;
+ buf->st_mode = S_IFREG; /* added by T.I. for the copy-file */
+ return 0; /* success */
+}
+#endif /* __MRC__ */
+
+
+int
+mkdir (const char *dirname, int mode)
+{
+#pragma unused(mode)
+
+ HFileParam hfpb;
+ char true_pathname[MAXPATHLEN+1], mac_pathname[MAXPATHLEN+1];
+
+ if (find_true_pathname (dirname, true_pathname, MAXPATHLEN+1) == -1)
+ return -1;
+
+ if (posix_to_mac_pathname (true_pathname, mac_pathname, MAXPATHLEN+1) == 0)
+ return -1;
+
+ c2pstr (mac_pathname);
+ hfpb.ioNamePtr = mac_pathname;
+ hfpb.ioVRefNum = 0; /* ignored unless name is invalid */
+ hfpb.ioDirID = 0; /* parent is the root */
+
+ errno = PBDirCreate ((HParmBlkPtr) &hfpb, false);
+ /* just return the Mac OSErr code for now */
+ return errno == noErr ? 0 : -1;
+}
+
+
+#undef rmdir
+sys_rmdir (const char *dirname)
+{
+ HFileParam hfpb;
+ char mac_pathname[MAXPATHLEN+1];
+
+ if (posix_to_mac_pathname (dirname, mac_pathname, MAXPATHLEN+1) == 0)
+ return -1;
+
+ c2pstr (mac_pathname);
+ hfpb.ioNamePtr = mac_pathname;
+ hfpb.ioVRefNum = 0; /* ignored unless name is invalid */
+ hfpb.ioDirID = 0; /* parent is the root */
+
+ errno = PBHDelete ((HParmBlkPtr) &hfpb, false);
+ return errno == noErr ? 0 : -1;
+}
+
+
+#ifdef __MRC__
+/* No implementation yet. */
+int
+execvp (const char *path, ...)
+{
+ return -1;
+}
+#endif /* __MRC__ */
+
+
+int
+utime (const char *path, const struct utimbuf *times)
+{
+ char true_pathname[MAXPATHLEN+1], fully_resolved_name[MAXPATHLEN+1];
+ int len;
+ char mac_pathname[MAXPATHLEN+1];
+ CInfoPBRec cipb;
+
+ if (find_true_pathname (path, true_pathname, MAXPATHLEN+1) == -1)