Date: Sun, 13 Dec 1998 10:04:03 +0200 (IST) From: Eli Zaretskii X-Sender: eliz AT is To: DJ Delorie , djgpp-workers AT delorie DOT com Subject: Re: djgpp 2.02 and the /dev/x/foo feature In-Reply-To: Message-ID: MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII Reply-To: djgpp-workers AT delorie DOT com Here's the source of the test program I used. I thought it would be a good addition to djtst distribution, so I arranged this as context diffs to be applied by `patch' on top of unpacked djtst202.zip. *** /dev/null Sat Dec 12 19:05:34 1998 --- tests/libc/dos/io/filesys.c Sat Dec 12 15:54:04 1998 *************** *** 0 **** --- 1,462 ---- + /* This program runs its command-line arguments through all the + library functions which take file names as arguments, and reports + any abnormal results. All file-name oriented functions are tested, + except those which are mere wrappers around other functions (for + example, `utimes' is a trivial wrapper around `utime', and thus is + not tested here). + + The original purpose of this program is to test support for special + file names, like "/dev/x/foo" etc. But you can use this program + for any file names you like. + + Keep in mind that you are supposed to know what you are doing when + you choose the file names to pass to the program. The program + expects each command-line argument to be a name of a regular + existing file, which is deleted during the testing, and a directory + by the same name is created in its stead (the directory is also + deleted near the end of the test). You can pass any other types of + arguments to the program, like non-existing files, device name, etc., + but some of the tests might fail then. For example, if you start with + a directory, the test of `open' will fail even if there's nothing + wrong with the underlying library functions. */ + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include + + #define ALL_FILES (FA_RDONLY|FA_HIDDEN|FA_SYSTEM|FA_LABEL|FA_DIREC|FA_ARCH) + + /* The directory where it all started. */ + static char *orig_dir; + + /* + * + * Test functions. + * + */ + + /* Test functions which take zero to 2 arguments, besides the file + name, and return an int status. */ + int t_misc (const char *func, int narg, const char *fname, int arg1, int arg2) + { + int retval = -1; + + switch (narg) + { + case 0: + if (strcmp (func, "chdir") == 0) + { + retval = chdir (fname); + chdir (orig_dir); + } + else if (strcmp (func, "rmdir") == 0) + { + char d_root[4]; + + _put_path (fname); + dosmemget (__tb, 2, d_root); + + /* If FNAME is on another drive, it can be the CWD on that + drive, and DOS won't let us remove it. So we chdir to + the root of that drive, just in case. */ + if (d_root[0] && d_root[1] == ':') + { + d_root[2] = '/'; + d_root[3] = '\0'; + chdir (d_root); + } + retval = rmdir (fname); + chdir (orig_dir); + } + else if (strcmp (func, "remove") == 0) + { + if (!__file_exists (fname)) + mkdir (fname, 0666); + retval = remove (fname); + } + else if (strcmp (func, "_use_lfn") == 0) + retval = _use_lfn (fname); + else if (strcmp (func, "system") == 0) + retval = system (fname); + break; + case 1: + if (strcmp (func, "_open") == 0) + { + retval = _open (fname, arg1); + if (retval > 2) + _close (retval); + } + else if (strcmp (func, "chmod") == 0) + retval = chmod (fname, arg1); + else if (strcmp (func, "access") == 0) + retval = access (fname, arg1); + else if (strcmp (func, "mkdir") == 0) + retval = mkdir (fname, arg1); + else if (strcmp (func, "pathconf") == 0) + retval = pathconf (fname, arg1); + else if (strcmp (func, "truncate") == 0) + retval = truncate (fname, arg1); + break; + case 2: + if (strcmp (func, "open") == 0) + { + retval = open (fname, arg1, arg2); + if (retval > 2) + close (retval); + } + else if (strcmp (func, "_chmod") == 0) + retval = _chmod (fname, arg1, arg2); + else if (strcmp (func, "_is_executable") == 0) + retval = _is_executable (fname, arg1, (const char *)arg2); + else + retval = -1; + break; + default: + retval = -1; + break; + } + return retval; + } + + /* Test functions which copy/rename/remove the file, or transform its + file name. These usually take two char * arguments. */ + int t_path (const char *func, int narg, const char *fname, int d1, int d2) + { + static const char *report_fmt = "%s: `%s' -> `%s'\n"; + int retval; + char tpath[PATH_MAX]; + int (*testee)(const char *, const char *) = link; + + d2 = d1; /* pacify gcc -Wall */ + + if (strcmp (func, "_fixpath") == 0) + { + _fixpath (fname, tpath); + printf (report_fmt, func, fname, tpath); + retval = 0; + } + else if ((strcmp (func, "link") == 0 ? testee = link : 0) != 0 + || (strcmp (func, "symlink") == 0 ? testee = symlink : 0) != 0 + || (strcmp (func, "rename") == 0 ? testee = rename : 0) != 0 + || (strcmp (func, "_rename") == 0 ? testee = _rename : 0) != 0) + { + size_t fnlast = strlen (fname) - 1; + + /* Beginning with `!', look for a character which can be used to + generate a name of a non-existent file by replacing with it + the last character of FNAME. */ + strcpy (tpath, fname); + tpath[fnlast] = '!'; + while (__file_exists (tpath)) + { + tpath[fnlast]++; + + /* Skip characters disallowed by DOS. */ + if (strchr (".+,;=[]|<>/\\\":?*", tpath[fnlast])) + tpath[fnlast]++; + } + retval = testee (fname, tpath); + if (retval == 0) + { + if (testee == rename || testee == _rename) + rename (tpath, fname); + else + { + remove (tpath); + + /* `symlink' creates foo.exe */ + if (testee == symlink) + remove (strcat (tpath, ".exe")); + } + } + } + else if (strcmp (func, "mkstemp") == 0) + { + char *s, *p; + + strcpy (tpath, fname); + for (s = p = basename (tpath); p < s + 8; p++) + *p = 'X'; + s[0] = 'f'; + s[1] = 't'; + *p = '\0'; + retval = mkstemp (tpath); + printf (report_fmt, func, fname, tpath); + if (retval > 2) + { + close (retval); + remove (tpath); + } + } + else if (strcmp (func, "tmpnam") == 0) + { + char *p = dirname (fname); + + if (setenv ("TMPDIR", p, 1) == -1) + { + printf ("%s: setenv failed\n", func); + retval = -1; + } + else + { + printf (report_fmt, func, fname, tmpnam (NULL)); + retval = 0; + } + free (p); + } + else if (strcmp (func, "_truename") == 0) + { + char *p = _truename (fname, NULL); + + if (p) + { + printf (report_fmt, func, fname, p); + retval = 0; + free (p); + } + else + retval = -1; + } + else + retval = -1; + + return retval; + } + + /* Test findfirst. */ + int t_findfirst (const char *func, int narg, const char *fname, int attr, int dummy) + { + struct ffblk ff; + + dummy = dummy; /* shut up gcc -Wall */ + + return findfirst (fname, &ff, attr); + } + + /* Test fopen. */ + int t_fopen (const char *func, int narg, const char *fname, int d1, int d2) + { + int retval; + FILE *fp = fopen (fname, "r"); + + d2 = d1; /* keep gcc -Wall happy */ + + retval = fp == NULL ? -1 : 0; + + if (fp) + fclose (fp); + return retval; + } + + /* Test opendir. */ + int t_opendir (const char *func, int narg, const char *fname, int d1, int d2) + { + int retval; + char *fdir = dirname (fname); + char *base = basename (fname); + + d2 = d1; /* for gcc -Wall */ + + if (!fdir) + { + printf ("%s: dirname failed\n", func); + retval = -1; + } + else + { + DIR *dirp = opendir (fdir); + + free (fdir); + if (!dirp) + retval = -1; + else + { + struct dirent *de; + + retval = -1; + + /* See that we actually opened the right directory, by + reading it until we see the file FNAME. */ + while ((de = readdir (dirp)) != NULL) + { + if (strcasecmp (de->d_name, base) == 0) + { + retval = 0; + break; + } + } + closedir (dirp); + } + } + return retval; + } + + /* Test spawnl. */ + int t_spawn (const char *func, int narg, const char *fname, int arg, int dummy) + { + dummy = dummy; /* can't stand gcc -Wall whining */ + + if (!_is_executable (fname, -1, NULL)) + { + printf ("%s: %s is not an executable file, skipping\n", func, fname); + return 0; + } + return spawnl (arg, fname, fname, NULL); + } + + /* Test stat. */ + int t_stat (const char *func, int narg, const char *fname, int d1, int d2) + { + struct stat st_buf; + + d2 = d1; /* prevent gcc -Wall from complaining */ + return stat (fname, &st_buf); + } + + /* Test statfs. */ + int t_statfs (const char *func, int narg, const char *fname, int d1, int d2) + { + struct statfs sbuf; + int retval = statfs (fname, &sbuf); + + d2 = d1; /* avoid messages from gcc -Wall */ + + if (retval == 0) + { + char dos_path[FILENAME_MAX]; + int dev_drv; + + /* Make sure "/dev/x/foo" reports the correct drive, not the + current one. */ + _fixpath (fname, dos_path); + dev_drv = (dos_path[0] & 0x1f) - 1; + if (dev_drv != sbuf.f_fsid[0]) + retval = -1; + } + return retval; + } + + /* Test utime. */ + int t_utime (const char *func, int narg, const char *fname, int at, int mt) + { + struct utimbuf tb = {(time_t)at, (time_t)mt}; + return utime (fname, &tb); + } + + struct fs_test { + const char *func_name; + int nargs; + int (*tester)(const char *, int, const char *, int, int); + int arg1; + int arg2; + int result; + int negate_result; + } test_vec[] = { /* the order of the tests below is not entirely arbitrary! */ + {"_open", 1, t_misc, O_RDONLY, 0, -1, 1}, + {"open", 2, t_misc, O_RDONLY, 0666, -1, 1}, + {"_chmod", 2, t_misc, 0, 0, -1, 1}, + {"chmod", 1, t_misc, S_IWUSR, 0, 0, 0}, + {"access", 1, t_misc, R_OK, 0, 0, 0}, + {"findfirst", 2, t_findfirst, 0, ALL_FILES, 0, 0}, + {"_fixpath", 1, t_path, 0, 0, 0, 0}, + {"fopen", 1, t_fopen, 0, 0, 0, 0}, + {"_is_executable", 2, t_misc, -1, 0, 1, 0}, + {"link", 1, t_path, 0, 0, 0, 0}, + {"mkstemp", 0, t_path, 0, 0, -1, 1}, + {"opendir", 0, t_opendir, 0, 0, -1, 1}, + {"pathconf", 1, t_misc, _PC_NAME_MAX, 0, 256, 0}, + {"_rename", 1, t_path, 0, 0, 0, 0}, + {"rename", 1, t_path, 0, 0, 0, 0}, + {"spawnl", 2, t_spawn, P_WAIT, 0, -1, 1}, + {"stat", 1, t_stat, 0, 0, 0, 0}, + {"statfs", 0, t_statfs, 0, 0, 0, 0}, + {"symlink", 1, t_path, 0, 0, 0, 0}, + {"system", 0, t_misc, 0, 0, -1, 1}, + {"tmpnam", 0, t_path, 0, 0, 0, 0}, + {"_truename", 1, t_path, 0, 0, 0, 0}, + {"truncate", 1, t_misc, 0, 0, 0, 0}, + {"utime", 2, t_utime, 930000000, 925000000, 0, 0}, + {"remove", 0, t_misc, 0, 0, 0, 0}, + {"mkdir", 1, t_misc, S_IWUSR, 0, 0, 0}, + {"chdir", 0, t_misc, 0, 0, 0, 0}, + {"_use_lfn", 0, t_misc, 0, 0, 1, 0}, + {"rmdir", 0, t_misc, 0, 0, 0, 0} + }; + + /* Non-zero when verbose operation was requested. */ + static int verbose = 0; + + /* Run a single file FILE through all the tests. */ + int fstest (const char *file) + { + int i, retval = 0; + + for (i = 0; i < sizeof (test_vec) / sizeof (test_vec[0]); i++) + { + int result; + + if (verbose) + printf ("\nTesting %s(%s)...", test_vec[i].func_name, file); + errno = 0; + result = test_vec[i].tester(test_vec[i].func_name, test_vec[i].nargs, + file, test_vec[i].arg1, test_vec[i].arg2); + if (test_vec[i].negate_result + ? result == test_vec[i].result + : result != test_vec[i].result) + { + printf ("%s%s(%s): incorrect result: %d (expected: %s%d)\n", + verbose ? "\n" : "", + test_vec[i].func_name, file, result, + test_vec[i].negate_result ? "NOT " : "", + test_vec[i].result); + if (errno) + printf ("System error was: %s\n", strerror (errno)); + retval++; + } + } + return retval; + } + + int main (int argc, char **argv) + { + if (argc == 1) + { + printf ("Usage: %s [-v] file...\n", argv[0]); + return 0; + } + + orig_dir = getcwd (NULL, PATH_MAX); + + if (strcmp (argv[1], "-v") == 0) + { + verbose = 1; + --argc; + ++argv; + } + + while (--argc) + { + char *arg = *++argv; + + if (fstest (arg) == -1) + return 1; + } + + return 0; + } *** tests/libc/dos/io/makefile.~0 Thu Jan 1 19:03:16 1998 --- tests/libc/dos/io/makefile Sat Dec 12 19:03:56 1998 *************** *** 4,8 **** --- 4,9 ---- SRC += cbrk.c SRC += ctrlc.c SRC += putpath.c + SRC += filesys.c include $(TOP)/../makefile.inc