summaryrefslogtreecommitdiff
path: root/os2/filesubr.c
diff options
context:
space:
mode:
Diffstat (limited to 'os2/filesubr.c')
-rw-r--r--os2/filesubr.c946
1 files changed, 946 insertions, 0 deletions
diff --git a/os2/filesubr.c b/os2/filesubr.c
new file mode 100644
index 0000000..e70f4ec
--- /dev/null
+++ b/os2/filesubr.c
@@ -0,0 +1,946 @@
+/* filesubr.c --- subroutines for dealing with files under OS/2
+ Jim Blandy <jimb@cyclic.com> and Karl Fogel <kfogel@cyclic.com>
+
+ This file is part of GNU CVS.
+
+ GNU CVS is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details. */
+
+/* These functions were moved out of subr.c because they need different
+ definitions under operating systems (like, say, Windows NT) with different
+ file system semantics. */
+
+#include <io.h>
+
+#include "os2inc.h"
+#include "cvs.h"
+
+static int deep_remove_dir( const char *path );
+
+/*
+ * Copies "from" to "to".
+ */
+void
+copy_file (from, to)
+ const char *from;
+ const char *to;
+{
+ struct stat sb;
+ struct utimbuf t;
+ int fdin, fdout;
+
+ if (trace)
+#ifdef SERVER_SUPPORT
+ (void) fprintf (stderr, "%c-> copy(%s,%s)\n",
+ (server_active) ? 'S' : ' ', from, to);
+#else
+ (void) fprintf (stderr, "-> copy(%s,%s)\n", from, to);
+#endif
+ if (noexec)
+ return;
+
+ if ((fdin = open (from, O_RDONLY | O_BINARY)) < 0)
+ error (1, errno, "cannot open %s for copying", from);
+ if (fstat (fdin, &sb) < 0)
+ error (1, errno, "cannot fstat %s", from);
+ if ((fdout = open (to, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY,
+ (int) sb.st_mode & 07777)) < 0)
+ error (1, errno, "cannot create %s for copying", to);
+ if (sb.st_size > 0)
+ {
+ char buf[BUFSIZ];
+ int n;
+
+ for (;;)
+ {
+ n = read (fdin, buf, sizeof(buf));
+ if (n == -1)
+ {
+#ifdef EINTR
+ if (errno == EINTR)
+ continue;
+#endif
+ error (1, errno, "cannot read file %s for copying", from);
+ }
+ else if (n == 0)
+ break;
+
+ if (write(fdout, buf, n) != n) {
+ error (1, errno, "cannot write file %s for copying", to);
+ }
+ }
+
+#ifdef HAVE_FSYNC
+ if (fsync (fdout))
+ error (1, errno, "cannot fsync file %s after copying", to);
+#endif
+ }
+
+ if (close (fdin) < 0)
+ error (0, errno, "cannot close %s", from);
+ if (close (fdout) < 0)
+ error (1, errno, "cannot close %s", to);
+
+ /* now, set the times for the copied file to match those of the original */
+ memset ((char *) &t, 0, sizeof (t));
+ t.actime = sb.st_atime;
+ t.modtime = sb.st_mtime;
+ (void) utime ((char *)to, &t);
+}
+
+/* FIXME-krp: these functions would benefit from caching the char * &
+ stat buf. */
+
+/*
+ * Returns non-zero if the argument file is a directory, or is a symbolic
+ * link which points to a directory.
+ */
+int
+isdir (file)
+ const char *file;
+{
+ struct stat sb;
+
+ if (stat (file, &sb) < 0)
+ return (0);
+ return (S_ISDIR (sb.st_mode));
+}
+
+/*
+ * Returns non-zero if the argument file is a symbolic link.
+ */
+int
+islink (file)
+ const char *file;
+{
+#ifdef S_ISLNK
+ struct stat sb;
+
+ if (lstat (file, &sb) < 0)
+ return (0);
+ return (S_ISLNK (sb.st_mode));
+#else
+ return (0);
+#endif
+}
+
+/*
+ * Returns non-zero if the argument file exists.
+ */
+int
+isfile (file)
+ const char *file;
+{
+ struct stat sb;
+
+ if (stat (file, &sb) < 0)
+ return (0);
+ return (1);
+}
+
+/*
+ * Returns non-zero if the argument file is readable.
+ * XXX - must be careful if "cvs" is ever made setuid!
+ */
+int
+isreadable (file)
+ const char *file;
+{
+ return (access (file, R_OK) != -1);
+}
+
+/*
+ * Returns non-zero if the argument file is writable
+ * XXX - muct be careful if "cvs" is ever made setuid!
+ */
+int
+iswritable (file)
+ const char *file;
+{
+ return (access (file, W_OK) != -1);
+}
+
+/*
+ * Returns non-zero if the argument file is accessable according to
+ * mode. If compiled with SETXID_SUPPORT also works if cvs has setxid
+ * bits set.
+ */
+int
+isaccessible (file, mode)
+ const char *file;
+ const int mode;
+{
+ return access(file, mode) == 0;
+}
+
+
+
+/*
+ * Make a directory and die if it fails
+ */
+void
+make_directory (name)
+ const char *name;
+{
+ struct stat buf;
+
+ if (stat (name, &buf) == 0 && (!S_ISDIR (buf.st_mode)))
+ error (0, 0, "%s already exists but is not a directory", name);
+ if (!noexec && mkdir ((char *)name) < 0)
+ error (1, errno, "cannot make directory %s", name);
+}
+
+/*
+ * Make a path to the argument directory, printing a message if something
+ * goes wrong.
+ */
+void
+make_directories (name)
+ const char *name;
+{
+ char *cp;
+
+ if (noexec)
+ return;
+
+ if (mkdir ((char *)name) == 0 || errno == EACCES)
+ return;
+ if (! existence_error (errno))
+ {
+ error (0, errno, "cannot make path to %s", name);
+ return;
+ }
+ if ((cp = strrchr (name, '/')) == NULL)
+ return;
+ *cp = '\0';
+ make_directories (name);
+ *cp++ = '/';
+ if (*cp == '\0')
+ return;
+ (void) mkdir ((char *)name);
+}
+
+/* Create directory NAME if it does not already exist; fatal error for
+ other errors. Returns 0 if directory was created; 1 if it already
+ existed. */
+int
+mkdir_if_needed (name)
+ char *name;
+{
+ if (mkdir (name) < 0)
+ {
+ /* Now, let me get this straight. In IBM C/C++
+ under OS/2, the error string for EEXIST is:
+
+ "The file already exists",
+
+ and the error string for EACCES is:
+
+ "The file or directory specified is read-only".
+
+ Nonetheless, mkdir() will set EACCES if the
+ directory *exists*, according both to the
+ documentation and its actual behavior.
+
+ I'm sure that this made sense, to someone,
+ somewhere, sometime. Just not me, here, now. */
+ if (errno != EEXIST
+#ifdef EACCES
+ && errno != EACCES
+#endif
+ )
+ error (1, errno, "cannot make directory %s", name);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Change the mode of a file, either adding write permissions, or removing
+ * all write permissions. Adding write permissions honors the current umask
+ * setting.
+ */
+void
+xchmod (fname, writable)
+ char *fname;
+ int writable;
+{
+ char *attrib_cmd;
+ char *attrib_option;
+ char *whole_cmd;
+ char *p;
+ char *q;
+
+ if (!isfile (fname))
+ {
+ error (0, 0, "cannot change mode of file %s; it does not exist",
+ fname);
+ return;
+ }
+
+ attrib_cmd = "attrib "; /* No, really? */
+
+ if (writable)
+ attrib_option = "-r "; /* make writeable */
+ else
+ attrib_option = "+r "; /* make read-only */
+
+ whole_cmd = xmalloc (strlen (attrib_cmd)
+ + strlen (attrib_option)
+ + strlen (fname)
+ + 1);
+
+ strcpy (whole_cmd, attrib_cmd);
+ strcat (whole_cmd, attrib_option);
+
+ /* Copy fname to the end of whole_cmd, translating / to \.
+ Attrib doesn't take / but many parts of CVS rely
+ on being able to use it. */
+ p = whole_cmd + strlen (whole_cmd);
+ q = fname;
+ while (*q)
+ {
+ if (*q == '/')
+ *p++ = '\\';
+ else
+ *p++ = *q;
+ ++q;
+ }
+ *p = '\0';
+
+ system (whole_cmd);
+ free (whole_cmd);
+}
+
+
+/* Read the value of a symbolic link.
+ Under OS/2, this function always returns EINVAL. */
+int
+readlink (char *path, char *buf, int buf_size)
+{
+ errno = EINVAL;
+ return -1;
+}
+
+/*
+ * unlink a file, if possible.
+ */
+int
+unlink_file (f)
+ const char *f;
+{
+ if (trace)
+#ifdef SERVER_SUPPORT
+ (void) fprintf (stderr, "%c-> unlink(%s)\n",
+ (server_active) ? 'S' : ' ', f);
+#else
+ (void) fprintf (stderr, "-> unlink(%s)\n", f);
+#endif
+ if (noexec)
+ return (0);
+
+ /* Win32 unlink is stupid - it fails if the file is read-only.
+ * OS/2 is similarly stupid. It does have a remove() function,
+ * but the documentation does not make clear why remove() is or
+ * isn't preferable to unlink(). I'll use unlink() because the
+ * name is closer to our interface, what the heck. Also, we know
+ * unlink()'s error code when trying to remove a directory.
+ */
+ if (isfile (f))
+ xchmod ((char *)f, 1);
+ return (unlink (f));
+}
+
+/*
+ * Unlink a file or dir, if possible. If it is a directory do a deep
+ * removal of all of the files in the directory. Return -1 on error
+ * (in which case errno is set).
+ */
+int
+unlink_file_dir (f)
+ const char *f;
+{
+ if (trace)
+#ifdef SERVER_SUPPORT
+ (void) fprintf (stderr, "%c-> unlink_file_dir(%s)\n",
+ (server_active) ? 'S' : ' ', f);
+#else
+ (void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f);
+#endif
+ if (noexec)
+ return (0);
+
+ if (unlink_file (f) != 0)
+ {
+ /* under OS/2, unlink returns EACCES if the path
+ is a directory. */
+ if (errno == EACCES)
+ return deep_remove_dir (f);
+ else
+ /* The file wasn't a directory and some other
+ * error occured
+ */
+ return -1;
+ }
+ /* We were able to remove the file from the disk */
+ return 0;
+}
+
+/* Remove a directory and everything it contains. Returns 0 for
+ * success, -1 for failure (in which case errno is set).
+ */
+
+static int
+deep_remove_dir (path)
+ const char *path;
+{
+ DIR *dirp;
+ struct dirent *dp;
+ char buf[PATH_MAX];
+
+ if (rmdir ((char *)path) != 0 && errno == EACCES)
+ {
+ if ((dirp = opendir ((char *)path)) == NULL)
+ /* If unable to open the directory return
+ * an error
+ */
+ return -1;
+
+ while ((dp = readdir (dirp)) != NULL)
+ {
+ if (strcmp (dp->d_name, ".") == 0 ||
+ strcmp (dp->d_name, "..") == 0)
+ continue;
+
+ sprintf (buf, "%s/%s", path, dp->d_name);
+
+ if (unlink_file (buf) != 0 )
+ {
+ if (errno == EACCES)
+ {
+ if (deep_remove_dir (buf))
+ {
+ closedir (dirp);
+ return -1;
+ }
+ }
+ else
+ {
+ /* buf isn't a directory, or there are
+ * some sort of permision problems
+ */
+ closedir (dirp);
+ return -1;
+ }
+ }
+ }
+ closedir (dirp);
+ return rmdir ((char *)path);
+ }
+ /* Was able to remove the directory return 0 */
+ return 0;
+}
+
+
+/*
+ * Rename a file and die if it fails
+ */
+void
+rename_file (from, to)
+ const char *from;
+ const char *to;
+{
+ if (trace)
+#ifdef SERVER_SUPPORT
+ (void) fprintf (stderr, "%c-> rename(%s,%s)\n",
+ (server_active) ? 'S' : ' ', from, to);
+#else
+ (void) fprintf (stderr, "-> rename(%s,%s)\n", from, to);
+#endif
+ if (noexec)
+ return;
+
+ unlink_file (to);
+ if (rename (from, to) != 0)
+ error (1, errno, "cannot rename file %s to %s", from, to);
+}
+
+
+/* Read NCHARS bytes from descriptor FD into BUF.
+ Return the number of characters successfully read.
+ The number returned is always NCHARS unless end-of-file or error. */
+static size_t
+block_read (fd, buf, nchars)
+ int fd;
+ char *buf;
+ size_t nchars;
+{
+ char *bp = buf;
+ size_t nread;
+
+ do
+ {
+ nread = read (fd, bp, nchars);
+ if (nread == (size_t)-1)
+ {
+#ifdef EINTR
+ if (errno == EINTR)
+ continue;
+#endif
+ return (size_t)-1;
+ }
+
+ if (nread == 0)
+ break;
+
+ bp += nread;
+ nchars -= nread;
+ } while (nchars != 0);
+
+ return bp - buf;
+}
+
+
+/*
+ * Compare "file1" to "file2". Return non-zero if they don't compare exactly.
+ */
+int
+xcmp (file1, file2)
+ const char *file1;
+ const char *file2;
+{
+ char *buf1, *buf2;
+ struct stat sb1, sb2;
+ int fd1, fd2;
+ int ret;
+
+ if ((fd1 = open (file1, O_RDONLY | O_BINARY)) < 0)
+ error (1, errno, "cannot open file %s for comparing", file1);
+ if ((fd2 = open (file2, O_RDONLY | O_BINARY)) < 0)
+ error (1, errno, "cannot open file %s for comparing", file2);
+ if (fstat (fd1, &sb1) < 0)
+ error (1, errno, "cannot fstat %s", file1);
+ if (fstat (fd2, &sb2) < 0)
+ error (1, errno, "cannot fstat %s", file2);
+
+ /* A generic file compare routine might compare st_dev & st_ino here
+ to see if the two files being compared are actually the same file.
+ But that won't happen in CVS, so we won't bother. */
+
+ if (sb1.st_size != sb2.st_size)
+ ret = 1;
+ else if (sb1.st_size == 0)
+ ret = 0;
+ else
+ {
+ /* FIXME: compute the optimal buffer size by computing the least
+ common multiple of the files st_blocks field */
+ size_t buf_size = 8 * 1024;
+ size_t read1;
+ size_t read2;
+
+ buf1 = xmalloc (buf_size);
+ buf2 = xmalloc (buf_size);
+
+ do
+ {
+ read1 = block_read (fd1, buf1, buf_size);
+ if (read1 == (size_t)-1)
+ error (1, errno, "cannot read file %s for comparing", file1);
+
+ read2 = block_read (fd2, buf2, buf_size);
+ if (read2 == (size_t)-1)
+ error (1, errno, "cannot read file %s for comparing", file2);
+
+ /* assert (read1 == read2); */
+
+ ret = memcmp(buf1, buf2, read1);
+ } while (ret == 0 && read1 == buf_size);
+
+ free (buf1);
+ free (buf2);
+ }
+
+ (void) close (fd1);
+ (void) close (fd2);
+ return (ret);
+}
+
+
+/* The equivalence class mapping for filenames.
+ OS/2 filenames are case-insensitive, but case-preserving. Both /
+ and \ are path element separators.
+ Thus, this table maps both upper and lower case to lower case, and
+ both / and \ to /.
+
+ Much thanks to Jim Blandy, who already invented this wheel in the
+ Windows NT port. */
+
+#if 0
+main ()
+{
+ int c;
+
+ for (c = 0; c < 256; c++)
+ {
+ int t;
+
+ if (c == '\\')
+ t = '/';
+ else
+ t = tolower (c);
+
+ if ((c & 0x7) == 0x0)
+ printf (" ");
+ printf ("0x%02x,", t);
+ if ((c & 0x7) == 0x7)
+ putchar ('\n');
+ else if ((c & 0x7) == 0x3)
+ putchar (' ');
+ }
+}
+#endif
+
+
+unsigned char
+OS2_filename_classes[] =
+{
+ 0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07,
+ 0x08,0x09,0x0a,0x0b, 0x0c,0x0d,0x0e,0x0f,
+ 0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17,
+ 0x18,0x19,0x1a,0x1b, 0x1c,0x1d,0x1e,0x1f,
+ 0x20,0x21,0x22,0x23, 0x24,0x25,0x26,0x27,
+ 0x28,0x29,0x2a,0x2b, 0x2c,0x2d,0x2e,0x2f,
+ 0x30,0x31,0x32,0x33, 0x34,0x35,0x36,0x37,
+ 0x38,0x39,0x3a,0x3b, 0x3c,0x3d,0x3e,0x3f,
+ 0x40,0x61,0x62,0x63, 0x64,0x65,0x66,0x67,
+ 0x68,0x69,0x6a,0x6b, 0x6c,0x6d,0x6e,0x6f,
+ 0x70,0x71,0x72,0x73, 0x74,0x75,0x76,0x77,
+ 0x78,0x79,0x7a,0x5b, 0x2f,0x5d,0x5e,0x5f,
+ 0x60,0x61,0x62,0x63, 0x64,0x65,0x66,0x67,
+ 0x68,0x69,0x6a,0x6b, 0x6c,0x6d,0x6e,0x6f,
+ 0x70,0x71,0x72,0x73, 0x74,0x75,0x76,0x77,
+ 0x78,0x79,0x7a,0x7b, 0x7c,0x7d,0x7e,0x7f,
+ 0x80,0x81,0x82,0x83, 0x84,0x85,0x86,0x87,
+ 0x88,0x89,0x8a,0x8b, 0x8c,0x8d,0x8e,0x8f,
+ 0x90,0x91,0x92,0x93, 0x94,0x95,0x96,0x97,
+ 0x98,0x99,0x9a,0x9b, 0x9c,0x9d,0x9e,0x9f,
+ 0xa0,0xa1,0xa2,0xa3, 0xa4,0xa5,0xa6,0xa7,
+ 0xa8,0xa9,0xaa,0xab, 0xac,0xad,0xae,0xaf,
+ 0xb0,0xb1,0xb2,0xb3, 0xb4,0xb5,0xb6,0xb7,
+ 0xb8,0xb9,0xba,0xbb, 0xbc,0xbd,0xbe,0xbf,
+ 0xc0,0xc1,0xc2,0xc3, 0xc4,0xc5,0xc6,0xc7,
+ 0xc8,0xc9,0xca,0xcb, 0xcc,0xcd,0xce,0xcf,
+ 0xd0,0xd1,0xd2,0xd3, 0xd4,0xd5,0xd6,0xd7,
+ 0xd8,0xd9,0xda,0xdb, 0xdc,0xdd,0xde,0xdf,
+ 0xe0,0xe1,0xe2,0xe3, 0xe4,0xe5,0xe6,0xe7,
+ 0xe8,0xe9,0xea,0xeb, 0xec,0xed,0xee,0xef,
+ 0xf0,0xf1,0xf2,0xf3, 0xf4,0xf5,0xf6,0xf7,
+ 0xf8,0xf9,0xfa,0xfb, 0xfc,0xfd,0xfe,0xff,
+};
+
+/* Like strcmp, but with the appropriate tweaks for file names.
+ Under OS/2, filenames are case-insensitive but case-preserving, and
+ both \ and / are path element separators. */
+int
+fncmp (const char *n1, const char *n2)
+{
+ while (*n1 && *n2
+ && (OS2_filename_classes[(unsigned char) *n1]
+ == OS2_filename_classes[(unsigned char) *n2]))
+ n1++, n2++;
+ return (OS2_filename_classes[(unsigned char) *n1]
+ - OS2_filename_classes[(unsigned char) *n2]);
+}
+
+/* Fold characters in FILENAME to their canonical forms.
+ If FOLD_FN_CHAR is not #defined, the system provides a default
+ definition for this. */
+void
+fnfold (char *filename)
+{
+ while (*filename)
+ {
+ *filename = FOLD_FN_CHAR (*filename);
+ filename++;
+ }
+}
+
+
+/* Generate a unique temporary filename. Returns a pointer to a newly
+ malloc'd string containing the name. Returns successfully or not at
+ all. */
+char *
+cvs_temp_name ()
+{
+ char value[L_tmpnam + 1];
+ char *retval;
+
+ /* FIXME: Does OS/2 have some equivalent to TMPDIR? */
+ retval = tmpnam (value);
+ if (retval == NULL)
+ error (1, errno, "cannot generate temporary filename");
+ return xstrdup (retval);
+}
+
+
+
+/* char *
+ * xresolvepath ( const char *path )
+ *
+ * Like xreadlink(), but resolve all links in a path.
+ *
+ * INPUTS
+ * path The original path.
+ *
+ * RETURNS
+ * The path with any symbolic links expanded.
+ *
+ * ERRORS
+ * This function exits with a fatal error if it fails to read the link for
+ * any reason.
+ */
+char *
+xresolvepath ( path )
+ const char *path;
+{
+ char *hardpath;
+ char *owd;
+
+ /* assert ( isdir ( path ) ); */
+
+ /* FIXME - If HAVE_READLINK is defined, we should probably walk the path
+ * bit by bit calling xreadlink().
+ */
+
+ owd = xgetwd();
+ if ( CVS_CHDIR ( path ) < 0)
+ error ( 1, errno, "cannot chdir to %s", path );
+ if ( ( hardpath = xgetwd() ) == NULL )
+ error (1, errno, "cannot readlink %s", hardpath);
+ if ( CVS_CHDIR ( owd ) < 0)
+ error ( 1, errno, "cannot chdir to %s", owd );
+ free (owd);
+ return hardpath;
+}
+
+/* Return a pointer into PATH's last component. */
+char *
+last_component (char *path)
+{
+ char *scan;
+ char *last = 0;
+
+ for (scan = path; *scan; scan++)
+ if (ISDIRSEP (*scan))
+ last = scan;
+
+ if (last && (last != path))
+ return last + 1;
+ else
+ return path;
+}
+
+
+/* Return the home directory. Returns a pointer to storage
+ managed by this function or its callees (currently getenv). */
+char *
+get_homedir ()
+{
+ return getenv ("HOME");
+}
+
+/* See cvs.h for description. */
+void
+expand_wild (argc, argv, pargc, pargv)
+ int argc;
+ char **argv;
+ int *pargc;
+ char ***pargv;
+{
+ int i;
+ int new_argc;
+ char **new_argv;
+ /* Allocated size of new_argv. We arrange it so there is always room for
+ one more element. */
+ int max_new_argc;
+
+ new_argc = 0;
+ /* Add one so this is never zero. */
+ max_new_argc = argc + 1;
+ new_argv = (char **) xmalloc (max_new_argc * sizeof (char *));
+ for (i = 0; i < argc; ++i)
+ {
+ HDIR FindHandle = 0x0001;
+ FILEFINDBUF3 FindBuffer;
+ ULONG FindCount = 1;
+ APIRET rc; /* Return code */
+#define ALL_FILES (FILE_ARCHIVED|FILE_DIRECTORY|FILE_SYSTEM|FILE_HIDDEN|FILE_READONLY)
+
+ /* DosFindFirst, called with a string like 'dir/file' will return
+ * *only* the file part. So what we have to do here is to save the
+ * directory part, and add it later to the returned filename.
+ */
+
+ /* Path + name */
+ char *PathName = argv [i];
+
+ /* Path only, including slash */
+ char *Path = NULL;
+
+ /* Name without path */
+ char *Name = last_component (PathName);
+
+ if (Name > PathName)
+ {
+ /* We have a path component, save it */
+ Path = xmalloc (Name - PathName + 1);
+ memcpy (Path, PathName, Name - PathName);
+ Path [Name - PathName] = '\0';
+ }
+
+ rc = DosFindFirst(PathName, /* File pattern */
+ &FindHandle, /* Directory search handle */
+ ALL_FILES, /* Search attribute */
+ (PVOID) &FindBuffer, /* Result buffer */
+ sizeof(FindBuffer), /* Result buffer length */
+ &FindCount, /* Number of entries to find */
+ FIL_STANDARD); /* Return level 1 file info */
+
+ if (rc != 0)
+ {
+ if (rc == ERROR_NO_MORE_FILES)
+ {
+ /* No match. The file specified didn't contain a wildcard (in which case
+ we clearly should return it unchanged), or it contained a wildcard which
+ didn't match (in which case it might be better for it to be an error,
+ but we don't try to do that). */
+ new_argv [new_argc++] = xstrdup (argv[i]);
+ if (new_argc == max_new_argc)
+ {
+ max_new_argc *= 2;
+ new_argv = xrealloc (new_argv, max_new_argc * sizeof (char *));
+ }
+ }
+ else
+ {
+ error (1, rc, "cannot find %s", PathName);
+ }
+ }
+ else
+ {
+ while (1)
+ {
+ /*
+ * Don't match ".", "..", and files starting with '.'
+ * (unless pattern also starts with '.'). This is
+ * (more or less) what standard Unix globbing does.
+ */
+ if ((strcmp(FindBuffer.achName, ".") != 0) &&
+ (strcmp(FindBuffer.achName, "..") != 0) &&
+ ((argv[i][0] == '.') || (FindBuffer.achName[0] != '.')))
+ {
+ /* Be sure to add the path if needed */
+ char *NewArg;
+ if (Path)
+ {
+ unsigned Len =
+ strlen (Path) + strlen (FindBuffer.achName) + 1;
+ NewArg = xmalloc (Len);
+ strcpy (NewArg, Path);
+ strcat (NewArg, FindBuffer.achName);
+ }
+ else
+ {
+ NewArg = xstrdup (FindBuffer.achName);
+ }
+ new_argv [new_argc++] = NewArg;
+ if (new_argc == max_new_argc)
+ {
+ max_new_argc *= 2;
+ new_argv = xrealloc (new_argv, max_new_argc * sizeof (char *));
+ }
+ }
+
+ rc = DosFindNext (FindHandle,
+ (PVOID) &FindBuffer,
+ sizeof(FindBuffer),
+ &FindCount);
+ if (rc == ERROR_NO_MORE_FILES)
+ break;
+ else if (rc != NO_ERROR)
+ error (1, rc, "cannot find %s", argv[i]);
+ }
+ rc = DosFindClose(FindHandle);
+ if (rc != 0)
+ error (1, rc, "cannot close %s", argv[i]);
+ }
+ if (Path != NULL)
+ free (Path);
+ }
+ *pargc = new_argc;
+ *pargv = new_argv;
+}
+
+/* Change drive and directory to path DIR. */
+
+int
+os2_chdir (const char *Dir)
+{
+ /* If the path includes a drive, change the current drive to the one
+ given. */
+ if (strlen (Dir) >= 2 && Dir [1] == ':')
+ {
+ /* A drive is given in Dir. Extract the drive from the string, then
+ * remove the drive from Dir by incrementing it.
+ */
+ int Drive = Dir [0];
+ Dir += 2;
+
+ /* Check if the given drive is valid, convert to a drive number
+ * (A: == 1, B: == 2, etc.). The compare below assumes ascii, but
+ * that is not a problem with OS/2.
+ */
+ if (Drive >= 'a' && Drive <= 'z')
+ {
+ Drive -= 'a' - 1;
+ }
+ else if (Drive >= 'A' && Drive <= 'Z')
+ {
+ Drive -= 'A' - 1;
+ }
+ else
+ {
+ /* An invalid drive letter. Set errno and return an error */
+ errno = EACCES;
+ return -1;
+ }
+
+ /* We have a valid drive given, so change the drive now */
+ if (DosSetDefaultDisk (Drive) != 0)
+ {
+ /* We had an error. Assume that the drive does not exist */
+#ifdef ENODEV
+ errno = ENODEV;
+#else
+ /* IBM C/C++ Tools 2.01 seems to lack ENODEV. */
+ errno = ENOENT;
+#endif
+ return -1;
+ }
+
+ }
+
+ /* Now we have a path without a drive left. Make it the current dir */
+ return chdir (Dir);
+}
+
+
+