Path: utzoo!attcan!utgpu!watmath!uunet!cs.utexas.edu!csd4.milw.wisc.edu!srcsip!tcnet!nis!sialis!icus!lenny From: lenny@icus.islp.ny.us (Lenny Tropiano) Newsgroups: unix-pc.sources Subject: POSIX/SVID/X3J11 standard routines (libposix.a) (Part 3 of 5) Keywords: POSIX, SVID, X3J11, dirent, News 3.0, good stuff! Message-ID: <938@icus.islp.ny.us> Date: 3 Aug 89 01:52:23 GMT Distribution: unix-pc Organization: ICUS Software Systems, Islip, New York Lines: 1151 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh DIRENT.INSTALL <<'END_OF_DIRENT.INSTALL' X X X INSTALLATION INSTRUCTIONS X X XThe following instructions are for systems resembling Ninth Edition UNIX, with Xhints about dealing with variations you may encounter for your specific system. XInstallation should be done only by someone who is comfortable with modifying Xthe standard C library and header files. X XIf your system already includes directory access routines, you should replace Xthem with this package. We're trying to get this standardized; see the Xdiscussion in the NOTES file. X XI have tried to make the source code as generic as possible, but if your system Xpredates Seventh Edition UNIX you will have problems. X XDISCLAIMER: Although I believe the code and procedures described here to be Xcorrect, I make no warranty of any kind, and you are advised to perform your Xown careful testing before making any substantial change like this to your Xprogramming environment. X X X0) For antique systems that do not support C's "void" data type, edit the file X sys.dirent.h to add the following: X X typedef int void; /* good enough for govt work */ X X If for some reason your doesn't define them, add the X following to sys.dirent.h: X X typedef unsigned short ino_t; /* (assuming original UFS) */ X typedef long off_t; /* long is forced by lseek() */ X X None of this should be necessary for any modern UNIX system. X X1) Copy the file dirent.h to /usr/include/dirent.h and copy the file X sys.dirent.h to /usr/include/sys/dirent.h. (The file sys._dir.h is also X provided for the BRL UNIX System V emulation for 4.nBSD. That environment X uses different directory names for everything.) X X2) Copy the file directory.3c to /usr/man/man3/directory.3 and copy the file X dirent.4 to /usr/man/man5/dirent.5; edit the new file X /usr/man/man3/directory.3 to change the "SEE ALSO" reference from dirent(4) X to dirent(5) and to change the 3C on the first line to 3; edit the new file X /usr/man/man5/dirent.5 to change the 4 on the first line to 5; then print X the manual pages via the command X X man directory dirent X X to see what the new routines are like. (If you have a "catman" style of X on-line manual, adapt these instructions accordingly. Manual entries are X kept in directories with other names on some systems such as UNIX System V. X On systems that already had a directory library documented in some other X manual entry, remove the superseded manual entry; if the description of the X native filesystem directory format found by "man dir" refers to a directory X library, modify it to simply refer to the entry for "dirent".) X X3) Copy the files closedir.c, opendir.c, readdir.c, rewinddir.c, seekdir.c, X and telldir.c to the "gen" or "port/gen" subdirectory of your C library X source directory. If you do not have a getdents() system call, copy the X file getdents.c to the "sys" or "port/sys" subdirectory and copy the file X getdents.2 to /usr/man/man2/getdents.2 (actually you may prefer to put this X file in section 3 and adjust the references in the other manual entries X accordingly; also adjust the references to dirent(4) to be to dirent(5) if X that's where the entry is). Edit the C library makefile(s) to include the X new object modules in the C library. (See the comments at the beginning of X getdents.c for symbols that must be defined to configure getdents.c.) Then X remake and reinstall the C library. Alternatively, you can just compile X the new sources and insert their objects near the front of the C library X /lib/libc.a using the "ar" utility (seekdir.o should precede readdir.o, X which in turn should precede getdents.o). On some systems you then need to X use the "ranlib" utility to update the archive symbol table. X X4) After the C library has been updated, delete /usr/include/ndir.h or any X other header used with a previous directory library to prevent inadvertent X use of the superseded directory access interface. Also delete any X corresponding library such as /usr/lib/libndir.a. X X5) To verify installation, try compiling, linking, and running the program X testdir.c. This program searches the current directory "." for each file X named as a program argument and prints `"FOO" found.' or `"FOO" not found.' X where FOO is of course replaced by the name being sought in the directory. X Try something like X X cd /usr/bin # a multi-block directory X WHEREVER/testdir FOO lint BAR f77 XYZZY X X which should produce the output X X "FOO" not found. X "lint" found. X "BAR" not found. X "f77" found. X "XYZZY" not found. X X A more thorough test would be X X cd /usr/bin # a multi-block directory X WHEREVER/testdir `ls -a` | grep 'not found' X X This program does not test the seekdir() and telldir() functions. X X6) Notify your programmers that all directory access must be made through the X new interface, and that documentation is available via X X man directory dirent X X Make the NOTES file available to those programmers who might want to X understand what this is all about. X X7) Change all system sources that were accessing directories to use the new X routines. Nearly all such sources contain the line X X #include X or X #include X X so they should be easy to find. (If you earlier removed some other header X file, that is, if this package superseded an earlier version of the X directory access library, look for its name too. See the conversion X instructions in the NOTES file.) END_OF_DIRENT.INSTALL if test 5490 -ne `wc -c DIRENT.NOTES <<'END_OF_DIRENT.NOTES' X X XNOTES FOR NEARLY-POSIX-COMPATIBLE C LIBRARY DIRECTORY-ACCESS ROUTINES X X XOlder UNIX C libraries lacked support for reading directories, so historically Xprograms had knowledge of UNIX directory structure hard-coded into them. When XBerkeley changed the format of directories for 4.2BSD, it became necessary to Xchange programs to work with the new structure. Fortunately, Berkeley designed Xa small set of directory access routines to encapsulate knowledge of the new Xdirectory format so that user programs could deal with directory entries as an Xabstract data type. (Unfortunately, they didn't get it quite right.) The Xinterface to these routines was nearly independent of the particular Ximplementation of directories on any given UNIX system; this has become a Xparticularly important requirement with the advent of heterogeneous network Xfilesystems such as NFS. X XIt has consequently become possible to write portable applications that search Xdirectories by restricting all directory access to use these new interface Xroutines. The sources supplied here are a total rewrite of Berkeley's code, Xincorporating ideas from a variety of sources and conforming as closely to Xpublished standards as possible, and are in the PUBLIC DOMAIN to encourage Xtheir widespread adoption. They support four methods of access to system Xdirectories: the original UNIX filesystem via read(), the 4.2BSD filesystem via Xread(), NFS and native filesystems via getdirentries(), and SVR3 getdents(). XThe other three types are accomplished by appropriate emulation of the SVR3 Xgetdents() system call, which attains portability at the cost of slightly more Xdata movement than absolutely necessary for some systems. These routines Xshould be added to the standard C library on all UNIX systems, and all existing Xand future applications should be changed to use this interface. Once this is Xdone, there should be no portability problems due to differences in underlying Xdirectory structures among UNIX systems. (When porting your applications to Xother UNIX systems, you can always carry this package around with you.) X XAn additional benefit of these routines is that they buffer directory input, Xwhich provides improved access speed over raw read()s of one entry at a time. X XOne annoying compatibility problem has arisen along the way, namely that the Xoriginal Berkeley interface used the same name, struct direct, for the new data Xstructure as had been used for the original UNIX filesystem directory record Xstructure. This name was changed by the IEEE 1003.1 (POSIX) Working Group to X"struct dirent" and was picked up for SVR3 under the new name; it is also the Xname used in this portable package. I believe it is necessary to bite the Xbullet and adopt the new non-conflicting name. Code using a 4.2BSD-compatible Xpackage needs to be slightly revised to work with this new package, as follows: X Change X #include /* Ninth Edition UNIX */ X or X #include /* 4.2BSD */ X or X #include /* BRL System V emulation */ X to X #include /* if not already #included */ X #include X X Change X struct direct X to X struct dirent X X Change X (anything)->d_namlen X to X strlen( (anything)->d_name ) X XThere is a minor compatibility problem in that the closedir() function was Xoriginally defined to have type void, but IEEE 1003.1 changed this to type int, Xwhich is what this implementation supports (even though I disagree with the Xchange). However, the difference does not affect most applications. X XAnother minor problem is that IEEE 1003.1 defined the d_name member of a struct Xdirent to be an array of maximum length; this does not permit use of compact Xvariable-length entries directly from a directory block buffer. This part of Xthe specification is incompatible with efficient use of the getdents() system Xcall, and I have therefore chosen to follow the SVID specification instead of XIEEE 1003.1 (which I hope is changed for the final-use standard). This Xdeviation should have little or no impact on sensibly-coded applications, since Xthe relevant d_name length is that given by strlen(), not the declared array Xsize. X XError handling is not completely satisfactory, due to the variety of possible Xfailure modes in a general setting. For example, the rewinddir() function Xmight fail, but there is no good way to indicate this. I have tried to Xfollow the specifications in IEEE 1003.1 and the SVID as closely as possible, Xbut there are minor deviations in this regard. Applications should not rely Xtoo heavily on exact failure mode semantics. X XPlease do not change the new standard interface in any way, as that would Xdefeat the major purpose of this package! (It's okay to alter the internal Ximplementation if you really have to, although I tried to make this unnecessary Xfor the vast majority of UNIX variants.) X XInstallation instructions can be found in the file named INSTALL. X XThis implementation is provided by: X X Douglas A. Gwyn X U.S. Army Ballistic Research Laboratory X SLCBR-VL-V X Aberdeen Proving Ground, MD 21005-5066 X X (301)278-6647 X X Gwyn@BRL.MIL or seismo!brl!gwyn X XThis is UNSUPPORTED, use-at-your-own-risk, free software in the public domain. XHowever, I would appreciate hearing of any actual bugs you find in this Ximplementation and/or any improvements you come up with. END_OF_DIRENT.NOTES if test 5314 -ne `wc -c bsearch.3c <<'END_OF_bsearch.3c' X.TH BSEARCH 3C "Standard Extension" X.SH NAME Xbsearch \- binary search a sorted table X.SH SYNOPSIS X.B #include X.PP X.B "char \(**bsearch ((char \(**) key, (char \(**) base, nel, sizeof (\(**key), compar)" X.br X.B unsigned nel; X.br X.B int (\(**compar)( ); X.SH DESCRIPTION X.I Bsearch Xis a binary search routine generalized from Knuth (6.2.1) Algorithm B. XIt returns a pointer into a table indicating where a datum may be found. XThe table must be previously sorted in increasing order according to a Xprovided comparison function. X.I Key Xpoints to a datum instance to be sought in the table. X.I Base Xpoints to the element at the base of the table. X.I Nel Xis the number of elements in the table. X.I Compar Xis the name of the comparison function, which is called with two arguments Xthat point to the elements being compared. The function must return Xan integer less than, equal to, or greater than zero as accordinly the first Xargument is to be considered less than, equal to, or greater than the second. X.SH EXAMPLE XThe example below searches a table containing pointers to nodes consisting of Xa string and its length. The table is ordered alphabetically on the string in Xthe node pointed to by each entry. X.PP XThis code fragment reads in strings and either finds the corresponding node Xand prints out the string and its length, or prints an error message. X.PP X.RS X.nf X.ss 18 X#include X#include X X#define \s-1TABSIZE\s+1 1000 X Xstruct node { /\(** these are stored in the table \(**/ X char \(**string; X int length; X}; Xstruct node table[\s-1TABSIZE\s+1]; /\(** table to be searched \(**/ X . X . X . X{ X struct node \(**node_ptr, node; X int node_compare( ); /\(** routine to compare 2 nodes \(**/ X char str_space[20]; /\(** space to read string into \(**/ X . X . X . X node.string = str_space; X while (scanf("%s", node.string) != \s-1EOF\s+1) { X node_ptr = (struct node \(**)bsearch((char \(**)(&node), X (char \(**)table, \s-1TABSIZE\s+1, X sizeof(struct node), node_compare); X if (node_ptr != \s-1NULL\s+1) { X (void)printf("string = %20s, length = %d\en", X node_ptr\(mi>string, node_ptr\(mi>length); X } else { X (void)printf("not found: %s\en", node.string); X } X } X} X/\(** X This routine compares two nodes based on an X alphabetical ordering of the string field. X\(**/ Xint Xnode_compare(node1, node2) Xstruct node \(**node1, \(**node2; X{ X return strcmp(node1\(mi>string, node2\(mi>string); X} X.fe X.SH NOTES XThe pointers to the key and the element at the base of Xthe table should be of type pointer-to-element, Xand cast to type pointer-to-character. X.br XThe comparison function need not compare every byte, Xso arbitrary data may be contained in the elements in addition to the values Xbeing compared. X.br XAlthough declared as type pointer-to-character, Xthe value returned should be cast into type pointer-to-element. X.SH SEE ALSO Xhsearch(3C), lsearch(3C), qsort(3C), tsearch(3C). X.SH DIAGNOSTICS XA X\s-1NULL\s+1 Xpointer is returned if the key cannot be found in the table. END_OF_bsearch.3c if test 3005 -ne `wc -c directory.3c <<'END_OF_directory.3c' X.TH DIRECTORY 3C "Standard Extension" X.SH NAME Xopendir, readdir, telldir, seekdir, rewinddir, closedir \- directory operations X.SH SYNOPSIS X.B "#include " X.br X.B "#include " X.P X.B "DIR \(**opendir (dirname)" X.br X.B "char \(**dirname;" X.P X.B "struct dirent \(**readdir (dirp)" X.br X.B "DIR \(**dirp;" X.P X.B "off_t telldir (dirp)" X.br X.B "DIR \(**dirp;" X.P X.B "void seekdir (dirp, loc)" X.br X.B "DIR \(**dirp;" X.br X.B "off_t loc;" X.P X.B "void rewinddir (dirp)" X.br X.B "DIR \(**dirp;" X.P X.B "int closedir (dirp)" X.br X.B "DIR \(**dirp;" X.SH DESCRIPTION X.I Opendir Xestablishes a connection between Xthe directory named by X.I dirname Xand a unique object of type X.SM DIR Xknown as a X.I "directory stream" Xthat it creates. X.I Opendir Xreturns a pointer to be used to identify the Xdirectory stream Xin subsequent operations. XA X.SM NULL Xpointer is returned if X.I dirname Xcannot be accessed or is not a directory, Xor if X.I opendir Xis unable to create the X.SM DIR Xobject X(perhaps due to insufficient memory). X.P X.I Readdir Xreturns a pointer to an internal structure Xcontaining information about the next active directory entry. XNo inactive entries are reported. XThe internal structure may be overwritten by Xanother operation on the same Xdirectory stream; Xthe amount of storage needed to hold a copy Xof the internal structure is given by the value of a macro, X.IR DIRENTSIZ(strlen(direntp\->d_name)) , Xnot by X.I "sizeof(struct\ dirent)" Xas one might expect. XA X.SM NULL Xpointer is returned Xupon reaching the end of the directory, Xupon detecting an invalid location in the directory, Xor upon occurrence of an error while reading the directory. X.P X.I Telldir Xreturns the current position associated with the named Xdirectory stream Xfor later use as an argument to X.IR seekdir . X.P X.I Seekdir Xsets the position of the next X.I readdir Xoperation on the named Xdirectory stream. XThe new position reverts to the one associated with the Xdirectory stream Xwhen the X.I telldir Xoperation from which X.I loc Xwas obtained was performed. X.P X.I Rewinddir Xresets the position of the named Xdirectory stream Xto the beginning of the directory. XAll buffered data for the directory stream is discarded, Xthereby guaranteeing that the actual Xfile system directory will be referred to for the next X.I readdir Xon the Xdirectory stream. X.P X.I Closedir Xcloses the named Xdirectory stream; Xinternal resources used for the Xdirectory stream are liberated, Xand subsequent use of the associated X.SM DIR Xobject is no longer valid. X.I Closedir Xreturns a value of zero if no error occurs, X\-1 otherwise. X.P XThere are several possible errors that can occur Xas a result of these operations; Xthe external integer variable X.I errno Xis set to indicate the specific error. X.RI ( Readdir 's Xdetection of the normal end of a directory Xis not considered to be an error.) X.SH EXAMPLE XSample code which searches the current working directory for entry X.IR name : X.P X.ft B X dirp = opendir( "." ); X.br X while ( (dp = readdir( dirp )) != NULL ) X.br X if ( strcmp( dp\->d_name, name ) == 0 ) X.br X { X.br X (void) closedir( dirp ); X.br X return FOUND; X.br X } X.br X (void) closedir( dirp ); X.br X return NOT_FOUND; X.ft P X.SH "SEE ALSO" Xgetdents(2), dirent(4). X.SH WARNINGS XEntries for "." and ".." Xmay not be reported for some file system types. X.P XThe value returned by X.I telldir Xneed not have any simple interpretation Xand should only be used as an argument to X.IR seekdir . XSimilarly, Xthe X.I loc Xargument to X.I seekdir Xmust be obtained from a previous X.I telldir Xoperation on the same Xdirectory stream. X.P X.I Telldir Xand X.I seekdir Xare unreliable when used in conjunction with Xfile systems that perform directory compaction or expansion Xor when the directory stream has been closed and reopened. XIt is best to avoid using X.I telldir Xand X.I seekdir Xaltogether. X.P XThe exact set of X.I errno Xvalues and meanings may vary among implementations. X.P XBecause directory entries can dynamically Xappear and disappear, Xand because directory contents are buffered Xby these routines, Xan application may need to continually rescan Xa directory to maintain an accurate picture Xof its active entries. END_OF_directory.3c if test 4136 -ne `wc -c getcwd.c <<'END_OF_getcwd.c' X/* X getcwd -- get current working directory name (POSIX and SVID compatible) X X last edit: 21-Sep-1987 D A Gwyn X X This public-domain getcwd() routine can be used to replace the UNIX X System V library routine (which uses popen() to capture the output of X the "pwd" command). Once that is done, "pwd" can be reimplemented as X just puts(getcwd()). X X This implementation depends on every directory having entries for X "." and "..". It also depends on the internals of the X data structures to some degree. X X I considered using chdir() to ascend the hierarchy, followed by a X final chdir() to the path being returned by getcwd() to restore the X location, but decided that error recovery was too difficult that way. X The algorithm I settled on was inspired by my rewrite of the "pwd" X utility, combined with the dotdots[] array trick from the SVR2 shell. X*/ X X#include X#include X#include X#include X#include X Xtypedef char *pointer; /* (void *) if you have it */ X Xextern void free(); Xextern pointer malloc(); Xextern int fstat(), stat(); X Xextern int errno; /* normally done by */ X X#ifndef NULL X#define NULL 0 /* amorphous null pointer constant */ X#endif X X#ifndef NAME_MAX X#define NAME_MAX 255 /* maximum directory entry size */ X#endif X Xchar * Xgetcwd( buf, size ) /* returns pointer to CWD pathname */ X char *buf; /* where to put name (NULL to malloc) */ X int size; /* size of buf[] or malloc()ed memory */ X { X static char dotdots[] = X"../../../../../../../../../../../../../../../../../../../../../../../../../.."; X char *dotdot; /* -> dotdots[.], right to left */ X DIR *dirp; /* -> parent directory stream */ X struct dirent *dir; /* -> directory entry */ X struct stat stat1, stat2; /* info from stat() */ X struct stat *d = &stat1; /* -> info about "." */ X struct stat *dd = &stat2; /* -> info about ".." */ X register char *buffer; /* local copy of buf, or malloc()ed */ X char *bufend; /* -> buffer[size] */ X register char *endp; /* -> end of reversed string */ X register char *dname; /* entry name ("" for root) */ X int serrno = errno; /* save entry errno */ X X if ( size == 0 ) X { X errno = EINVAL; /* invalid argument */ X return NULL; X } X X if ( (buffer = buf) == NULL /* wants us to malloc() the string */ X && (buffer = (char *)malloc( (unsigned)size )) == NULL X ) { X errno = ENOMEM; /* cannot malloc() specified size */ X return NULL; X } X X if ( stat( ".", dd ) != 0 ) /* prime the pump */ X goto error; /* errno already set */ X X endp = buffer; /* initially, empty string */ X bufend = &buffer[size]; X X for ( dotdot = &dotdots[sizeof(dotdots)]; dotdot != dotdots; ) X { X dotdot -= 3; /* include one more "/.." section */ X /* (first time is actually "..") */ X X /* swap stat() info buffers */ X { X register struct stat *temp = d; X X d = dd; /* new current dir is old parent dir */ X dd = temp; X } X X if ( (dirp = opendir( dotdot )) == NULL ) /* new parent */ X goto error; /* errno already set */ X X if ( fstat( dirp->dd_fd, dd ) != 0 ) X { X serrno = errno; /* set by fstat() */ X (void)closedir( dirp ); X errno = serrno; /* in case closedir() clobbered it */ X goto error; X } X X if ( d->st_dev == dd->st_dev ) X { /* not crossing a mount point */ X if ( d->st_ino == dd->st_ino ) X { /* root directory */ X dname = ""; X goto append; X } X X do X if ( (dir = readdir( dirp )) == NULL ) X { X (void)closedir( dirp ); X errno = ENOENT; /* missing entry */ X goto error; X } X while ( dir->d_ino != d->st_ino ); X } X else { /* crossing a mount point */ X struct stat t; /* info re. test entry */ X char name[sizeof(dotdots) + 1 + NAME_MAX]; X X (void)strcpy( name, dotdot ); X dname = &name[strlen( name )]; X *dname++ = '/'; X X do { X if ( (dir = readdir( dirp )) == NULL ) X { X (void)closedir( dirp ); X errno = ENOENT; /* missing entry */ X goto error; X } X X (void)strcpy( dname, dir->d_name ); X /* must fit if NAME_MAX is not a lie */ X } X while ( stat( name, &t ) != 0 X || t.st_ino != d->st_ino X || t.st_dev != d->st_dev X ); X } X X dname = dir->d_name; X X /* append "/" and reversed dname string onto buffer */ X append: X if ( endp != buffer /* avoid trailing / in final name */ X || dname[0] == '\0' /* but allow "/" when CWD is root */ X ) X *endp++ = '/'; X X { X register char *app; /* traverses dname string */ X X for ( app = dname; *app != '\0'; ++app ) X ; X X if ( app - dname >= bufend - endp ) X { X (void)closedir( dirp ); X errno = ERANGE; /* won't fit allotted space */ X goto error; X } X X while ( app != dname ) X *endp++ = *--app; X } X X (void)closedir( dirp ); X X if ( dname[0] == '\0' ) /* reached root; wrap it up */ X { X register char *startp; /* -> buffer[.] */ X X *endp = '\0'; /* plant null terminator */ X X /* straighten out reversed pathname string */ X for ( startp = buffer; --endp > startp; ++startp ) X { X char temp = *endp; X X *endp = *startp; X *startp = temp; X } X X errno = serrno; /* restore entry errno */ X return buffer; X } X } X X errno = ENOMEM; /* actually, algorithm failure */ X X error: X if ( buf == NULL ) X free( (pointer)buffer ); X X return NULL; X } X X END_OF_getcwd.c if test 5249 -ne `wc -c mkdir.c <<'END_OF_mkdir.c' X/**************************************************************************** X XNAME X mkdir.c -- emulation of BSD/SVr3-style mkdir(2) system call X XSYNOPSIS X int mkdir(path, perm) -- make a directory with given permissions X char *path; int perm; X XDESCRIPTION X The mkdir() function is used for making directories on systems that Xdon't have a mkdir(2) call (that is, V7 and USG systems before V.3). It tries Xto act as much like mkdir(2) as possible. Due to various bogosities (why, oh Xwhy wasn't mknod(2) for a directory made a non-privileged call?) it cannot Xcompletely succeed. It returns 0 on success and -1 on failure but Xonly detect the following ERRNO conditions; ENOENT, EEXIST, EACCESS, ENOTDIR. X X Note: this function emulates the SVr3 behavior (group ID of the directory Xis the effective group ID of the calling process) rather than the BSD Xbehavior (group ID of the directory is the group ID of the parent directory). X X On USG it will succeed if the real or apparent uid of the calling process Xhas write privileges in the current directory (the latter case is implemented Xby a kluge that tries to set the parent directory's permissions to 0777, 'ware Xsecurity holes!). The new directory will be owned by the effective uid. X X On V7 (because it restricts the chown(2) call and mkdir(1) makes Xdirectories owned by the real ID of its caller) the call will only succeed Xif the real ID matches, and the new directory will be owned by the real Xuid. X XREVISED BY X Eric S. Raymond X X****************************************************************************/ X/* LINTLIBRARY */ X#include X#include X#include X#include X Xextern int errno; X Xstatic int fwait(pid) X/* wait on a child process, shielding it from SIGINT and SIGHUP */ Xregister int pid; X{ X register int w; X int status; X X while ((w = wait(&status)) != pid && w != -1) X continue; X if (w == -1) X status = -1; X X return(status); X} X Xint mkdir(path, perm) Xchar *path; Xint perm; X{ X int status, pid; X int uid = getuid(), gid = getgid(); X#ifndef lint X int euid = geteuid(); X int egid = getegid(); X#endif /* lint */ X struct stat pstat, sbuf; X register char *p; X char parent[200]; X extern char *strrchr(); X X#ifdef MAIN X (void) fprintf(stderr, "Attempting to mkdir %s\n", path); X#endif /* MAIN */ X X errno = 0; X X /* X * check that the directory doesn't already exist, so that the X * do loop below must run at least once X */ X if (stat(path, &sbuf) == 0) X { X errno = EEXIST; X#ifdef MAIN X (void) fprintf(stderr, "Directory %s exists\n", path); X#endif /* MAIN */ X return(-1); X } X X /* construct the parent's name */ X if (p = strrchr(path, '/')) X { X *p = '\0'; X (void) strcpy(parent, path); X *p = '/'; X } X else X (void) strcpy(parent, "."); X X if (stat(parent, &pstat) == -1) /* check that the parent exists */ X { X errno = ENOENT; X return(-1); X } X else if (!(pstat.st_mode & S_IFDIR)) /* and that it's a directory */ X { X errno = ENOTDIR; X return(-1); X } X X#ifdef USG X /* X * If the parent directory is 755 (rwxr-xr-x) the mkdir(1) below X * will probably fail because it will get suid'd to our real uid, which X * is random (and thus probably won't match the parent owner's). X * So we have to temporarily chmod the parent to 777 (rwxrwxrwx). X */ X if (sbuf.st_uid != uid) X { X if (chmod(parent, 0777) == -1) X { X errno = EACCES; X return(-1); X } X } X#endif /* USG */ X X /* now we can make the new directory */ X if (pid = fork()) /* parent side */ X { X if (pid == -1) X return(-1); X X status = fwait(pid); /* wait till mkdir child is done */ X X#ifdef USG X /* X * Spawn another child to set ownership correctly -- we do this so X * that it gets set to effective ID even if we're running su. This X * only works where chown(2) can be called to give files away by a X * non-superuser. X */ X if (pid = fork()) X (void) fwait(pid); X else { X#ifndef lint X int oldumask = umask(0777); X#endif /* lint */ X X (void) stat(".", &pstat); X (void) setuid(uid); X (void) setgid(gid); X#ifndef lint /* USG lints disagree about 2nd argument types */ X (void) chmod(path, (unsigned)(perm & ~oldumask)); X (void) chown(path, euid, egid); X (void) umask(oldumask); X#endif /* lint */ X _exit(0); X } X#endif /* USG */ X } else { /* child side */ X (void) close(1); /* stdout */ X (void) close(2); /* stderr */ X (void) execlp("mkdir", "mkdir", path, (char *)0); X perror(path); X _exit(1); X } X X#ifndef lint /* USG lints disagree about the type of arg 2 */ X#ifdef USG X if (sbuf.st_uid != uid) X (void) chmod(parent, (unsigned) pstat.st_mode); /* put it back */ X#endif /* USG */ X#endif /* lint */ X X return(status); X} X X/* mkdir.c ends here */ END_OF_mkdir.c if test 4832 -ne `wc -c tsearch.c <<'END_OF_tsearch.c' X/* X * Tree search generalized from Knuth (6.2.2) Algorithm T just like X * the AT&T man page says. X * X * The node_t structure is for internal use only, lint doesn't grok it. X * X * Written by reading the System V Interface Definition, not the code. X * X * Totally public domain. X */ X/*LINTLIBRARY*/ X X#include X Xtypedef struct node_t X{ X char *key; X struct node_t *left, *right; X} Xnode; X Xnode *tsearch(key, rootp, compar) X/* find or insert datum into search tree */ Xchar *key; /* key to be located */ Xregister node **rootp; /* address of tree root */ Xint (*compar)(); /* ordering function */ X{ X register node *q; X X if (rootp == (struct node_t **)0) X return ((struct node_t *)0); X while (*rootp != (struct node_t *)0) /* Knuth's T1: */ X { X int r; X X if ((r = (*compar)(key, (*rootp)->key)) == 0) /* T2: */ X return (*rootp); /* we found it! */ X rootp = (r < 0) ? X &(*rootp)->left : /* T3: follow left branch */ X &(*rootp)->right; /* T4: follow right branch */ X } X q = (node *) malloc(sizeof(node)); /* T5: key not found */ X if (q != (struct node_t *)0) /* make new node */ X { X *rootp = q; /* link new node to old */ X q->key = key; /* initialize new node */ X q->left = q->right = (struct node_t *)0; X } X return (q); X} X Xnode *tdelete(key, rootp, compar) X/* delete node with given key */ Xchar *key; /* key to be deleted */ Xregister node **rootp; /* address of the root of tree */ Xint (*compar)(); /* comparison function */ X{ X node *p; X register node *q; X register node *r; X int cmp; X X if (rootp == (struct node_t **)0 || (p = *rootp) == (struct node_t *)0) X return ((struct node_t *)0); X while ((cmp = (*compar)(key, (*rootp)->key)) != 0) X { X p = *rootp; X rootp = (cmp < 0) ? X &(*rootp)->left : /* follow left branch */ X &(*rootp)->right; /* follow right branch */ X if (*rootp == (struct node_t *)0) X return ((struct node_t *)0); /* key not found */ X } X r = (*rootp)->right; /* D1: */ X if ((q = (*rootp)->left) == (struct node_t *)0) /* Left (struct node_t *)0? */ X q = r; X else if (r != (struct node_t *)0) /* Right link is null? */ X { X if (r->left == (struct node_t *)0) /* D2: Find successor */ X { X r->left = q; X q = r; X } X else X { /* D3: Find (struct node_t *)0 link */ X for (q = r->left; q->left != (struct node_t *)0; q = r->left) X r = q; X r->left = q->right; X q->left = (*rootp)->left; X q->right = (*rootp)->right; X } X } X free((struct node_t *) *rootp); /* D4: Free node */ X *rootp = q; /* link parent to new node */ X return(p); X} X Xstatic void trecurse(root, action, level) X/* Walk the nodes of a tree */ Xregister node *root; /* Root of the tree to be walked */ Xregister void (*action)(); /* Function to be called at each node */ Xregister int level; X{ X if (root->left == (struct node_t *)0 && root->right == (struct node_t *)0) X (*action)(root, leaf, level); X else X { X (*action)(root, preorder, level); X if (root->left != (struct node_t *)0) X trecurse(root->left, action, level + 1); X (*action)(root, postorder, level); X if (root->right != (struct node_t *)0) X trecurse(root->right, action, level + 1); X (*action)(root, endorder, level); X } X} X Xvoid twalk(root, action) /* Walk the nodes of a tree */ Xnode *root; /* Root of the tree to be walked */ Xvoid (*action)(); /* Function to be called at each node */ X{ X if (root != (node *)0 && action != (void(*)())0) X trecurse(root, action, 0); X} X X/* tsearch.c ends here */ END_OF_tsearch.c if test 3509 -ne `wc -c