diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2005-10-03 13:43:40 +0000 |
---|---|---|
committer | <> | 2014-09-25 11:25:48 +0000 |
commit | 10de491ef0bc43827ab8631a4c02860134e620a9 (patch) | |
tree | 22e734337cc9aa5d9b1d7c71261d160b6a60634d /os2/filesubr.c | |
download | cvs-tarball-master.tar.gz |
Imported from /home/lorry/working-area/delta_cvs-tarball/cvs-1.12.13.tar.bz2.HEADcvs-1.12.13master
Diffstat (limited to 'os2/filesubr.c')
-rw-r--r-- | os2/filesubr.c | 946 |
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); +} + + + |