dennis@rlgvax.UUCP (Dennis Bednar) (07/08/86)
A few weeks ago I posted a request for a uusnap for System 5 uucp's with a "flat" uucp spool directory. Prior to that request, I already had a private tool that I call "uustatd" (UUcp STATistics on spool Directory), but I was hoping to receive a better tool. By the way, seismo!rick responded that it would be simple to to change 4.2's uusnap.c to handle "flat" directories. I spent a short period of time attempting this, but found that a major rework is still needed because of the way the remote machine name is embedded in X.machine* files. Our version of uucp stores all execute files in the form "D.localX*", where local is our local site (rlgvax). However, the version of uusnap.c for 4.2 assumes that X files are stored in the form "X.remote*". To find the machine associated with the D.localX file requires that the C.remote* file be read. As a result of this, the hacked up uusnap reports 0 X.* files for all machines, and reports lots of D.* files destined to our local machine. Below is the uustatd program. -dennis bednar #--------------- CUT HERE --------------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # Makefile # README.uustatd # config.h # lookdir.c # machine.h # sample.out # uustatd.c # This archive created: Tue Jul 8 02:51:19 EDT 1986 # if test -f Makefile then echo shar: will not over-write existing file 'Makefile' else echo x - Makefile # ............ F I L E B E G .......... Makefile cat << '\SHAR_EOF' > Makefile OBJS = uustatd.o lookdir.o # CHANGE THIS FOR YOUR LOCAL SITE # YOU MUST ALSO CHANGE the include file config.h for your local site. # All that matters for lookdir.c is whether you have # DIRFIXLEN - USG 14 char fixed length file names # DIRVARLEN - 4.X BSD variable (up to 255 chars) per directory entry # INSTALLDIR = . uustatd: $(OBJS) cc $(LDFLAGS) $(OBJS) mv a.out $@ lookdir.o: machine.h config.h uustatd.o: # install uustat with set uid uucp so it has permission read # the uucp spool files. You must do the install as root. install: uustatd chown uucp uustatd chmod 4751 uustatd -mv uustatd $(INSTALLDIR) clean: rm -f $(OBJS) uustatd a.out core \SHAR_EOF # ............ F I L E E N D .......... Makefile fi # end of overwriting check if test -f README.uustatd then echo shar: will not over-write existing file 'README.uustatd' else echo x - README.uustatd # ............ F I L E B E G .......... README.uustatd cat << '\SHAR_EOF' > README.uustatd This is a uucp statistics program for uucp's that use the "flat" spool directory. To install it: Edit machine.h and config.h so that config.h properly defines either DIRFIXLEN or DIRVARLEN. See the comments in the Makefile for what these mean. Change INSTALLDIR in Makefile. make # make a local copy of uustatd su root make install # install it whereever it goes uustatd # try running the program LAST, Report any bugs or improvements to {seismo}!rlgvax!dennis. \SHAR_EOF # ............ F I L E E N D .......... README.uustatd fi # end of overwriting check if test -f config.h then echo shar: will not over-write existing file 'config.h' else echo x - config.h # ............ F I L E B E G .......... config.h cat << '\SHAR_EOF' > config.h /* * config.h * configure the local options * * Based on OS: * termio.h vs. sgtty.h * index/rindex vs strchr/strrchr * if lstat() symbolic link stat is supported * * You pick: * if fixed len or variable len directory entries are supported */ /* * bsd 4.1 and 4.2 uses sgtty for ioctl's * s3, s5 uses termio.h for ioctls * On output, defines WTERMIO==1 or undefined * On output, defines TERMIO to be either sgttyb or termio, for defining * variables. */ #if BSD4_1 == 1 || BSD4_2 == 1 #define TERMIO sgttyb /* don't want termio so leave WTERMIO undefined */ #else !BSD4_1 #if S3 == 1 || S5 == 1 || S5R2 == 1 || CPGCOS == 1 #define TERMIO termio #define WTERMIO 1 #else !S3 Unknown version of UNIX. Only BSD 4.[12], S3, S5, S5R2, and CPGCOS known. #endif S3 #endif BSD4_1 /* * on 4.1 and 4.2 BSD systems, the S3, S5, and S5R2 string(3) * names are not around */ #if BSD4_1 == 1 || BSD4_2 == 1 #define strchr index #define strrchr rindex #endif #if BSD4_2 == 1 || CPGCOS == 1 #define WSYMLINK 1 #endif #if BSD4_2 == 1 || CPGCOS == 1 # define DIRVARLEN 1 #else !BSD4_2 # if S3 == 1 || S5 == 1 || S5R2 == 1 # define DIRFIXLEN 1 # else I don't know the directory conventions on this machine bsd4.1 # endif #endif \SHAR_EOF # ............ F I L E E N D .......... config.h fi # end of overwriting check if test -f lookdir.c then echo shar: will not over-write existing file 'lookdir.c' else echo x - lookdir.c # ............ F I L E B E G .......... lookdir.c cat << '\SHAR_EOF' > lookdir.c /* * lookdir.c * author - dennis bednar 5 3 84 * reads directory entries one at a time for you * By recompiling with proper ifdef flag, it works with either fixed length * (S3) or variable length names up to 255 chars as in Berkeley. * * * to use: * * setdir (char *name); // to open directory "name" for reading * // returns -1 iff error * char *getdir (); // gets next directory name from this directory * // returns (char *) 0 if done * * you do NOT need to call enddir () to close the directory, this * is automatically done when the eof on the directory is encountered. * * TODO * handle multiple open directories, reading from any. * allow user to call enddir() when he feels like it. */ /* pass DDIRFIXLEN ==1 or DDIRVARLEN == 1 from config.h which is * included by machine.h */ #include <stdio.h> #include "machine.h" #ifdef DIRFIXLEN #include <sys/types.h> /* added for osg5 only */ #include <sys/dir.h> /* old directory format */ #else #include <sys/types.h> /* define u_long */ #include <sys/dir.h> /* new directory format */ #endif #ifdef DIRFIXLEN #ifdef DIRVARLEN both defined error #endif #endif #ifndef DIRFIXLEN #ifndef DIRVARLEN neither defined error #endif #endif #ifdef DIRVARLEN DIR *opendir (); /* open a directory, Berkley variable len format */ struct direct *readdir (); /* read a filename from a directory */ extern void closedir (); /* finally, close the directory opened by opendir() */ #endif #define MAXPATH 512 /* globals */ static char errbuf [MAXPATH]; /* build error message here */ #ifdef DIRVARLEN static DIR *headptr = (DIR *)0; /* pointer to directory structure returned by opendir */ static struct direct *dirptr; /* pointer to directory entry structure returned by readdir */ #else static int fd = -1; /* file descriptor associated with directory */ static struct direct dirbuf; /* holds directory entry read in from directory */ #endif /* global routines */ char *getdir (); /* * open the directory "name" * returns * 0 = good * -1 = can't open directory */ setdir (name) char *name; { /* open the full path name of this dir */ #ifdef DIRFIXLEN /* if already open, close it first */ if (fd != -1) enddir (); if ((fd = open (name, 0)) == -1) #else /* if already open, close it first */ if (headptr != (DIR *) 0) enddir (); if ((headptr = opendir (name)) == (DIR *) NULL) #endif { return (-1); } else return 0; /* headptr is a pointer to a memory block containing info about this dir */ } /* * return the name of the next directory entry * (char *) 0 means end of names */ char *getdir () { static char filename [MAXPATH]; /* read in file name from directory here */ #ifdef DIRFIXLEN /* check that directory is indeed open */ if (fd == -1) { fprintf (stderr, "dir: getdir: forget to call setdir () first\n"); exit (1); } while (read(fd, (char *)&dirbuf, sizeof(dirbuf))>0) { /* not err or eof */ if (dirbuf.d_ino == 0) /* i-node is zero in this slot */ continue; /* skip this slot */ if (strcmp (dirbuf.d_name, ".") == 0 /* then myself */ || strcmp (dirbuf.d_name, "..") == 0) /* or my parent */ continue; /* skip this dir entry */ strncpy (filename, dirbuf.d_name, 14); /* tail end of name */ #ifdef DEBUG printf ("debug: %s\n", filename); fflush (stdout); #endif return (filename); /* next call read next entry */ } #else /* check that directory is indeed open */ if (headptr == (DIR *) 0) { fprintf (stderr, "dir: getdir: forget to call setdir () first\n"); exit (1); } while ((dirptr = readdir (headptr)) != (struct direct *)NULL) { /* not err or eof */ /* readdir will automatically skip over inodes which are zero */ if (strcmp (dirptr->d_name, ".") == 0 /* then myself */ || strcmp (dirptr->d_name, "..") == 0) /* or my parent */ continue; /* skip this dir entry */ strcpy (filename, dirptr->d_name); /* tail end of name */ return (filename); /* next call read next entry */ } #endif enddir (); /* have come to eof */ return (char *) 0; } /* * called internally when eof is detected * sets file descriptor to indicate that file is closed */ static enddir () { #ifdef DIRFIXLEN close (fd); fd = -1; #else closedir (headptr); headptr = (DIR *) 0; #endif } #ifdef STAND /* * standalone test code * calling format: readdir // lists all files in the current directory readdir dir // lists files in "dir" */ main (argc, argv) int argc; char **argv; { int i; /* loop index into nargv [] */ int nargc; /* new arg count returned by getfnames */ char **nargv; /* new argv [] array returned by getfnames */ if (argc == 1) getfnames (".", &nargc, &nargv); else if (argc == 2) getfnames (argv[1], &nargc, &nargv); else { fprintf (stderr, "Wrong number of arguments: usage: %s [directory]\n", argv [0]); exit (1); } if (nargc == -1) { fprintf (stderr, "%s: getfnames returned error\n", argv[0]); exit (1); } /* sort file names */ sortargs (nargc-1, &nargv [1]); /* pretty print in columns */ pretty (nargc, nargv, 80); } #endif /* * print working directory * returns the name in curdir */ #if 0 pwd (curdir, dirlen) char *curdir; int dirlen; { FILE *Fd; /* file descriptor for popen */ int fd; /* file descriptor for open */ char *p; /* index within the dir */ static char tempfile [] = "/tmp/pwdXXXXXX"; /* temporary file */ static char pwdcmd [80] = "/bin/pwd >"; /* indirect to a file */ static char rmcmd [80] = "rm "; /* indirect to a file */ /* new way is 10 times cleaner than before !! */ Fd = popen ("/bin/pwd", "r"); /* open a pipe to pwd cmd */ fgets (curdir, dirlen, Fd); /* read in the answer */ pclose (Fd); curdir[strlen(curdir) - 1] = '\0'; /* change the newline to null */ } #endif /* * returns the file names of all files in "dir" in an argv array * just like the shell does, with count in argc. * *pargc = argc returned * (-1 = error and *pargv is garbage, * (1 = no files, and argv[0] = ""), * (2 = 1 file, pointed to by argv[1]) * *pargv = pointer to array of pointers to file names * ie. * argv is returned in *pargv. * argv[0] contains the "" string * argv[1], ..., argv [argc-1] are pointers to the file names returned */ getfnames (dirname, pargc, pargv) char *dirname; int *pargc; char ***pargv; { char *rp; /* return pointer to file name returned from getdir */ char **argv; /* argv [] pointer array dynamically growing */ char *fp; /* filename pointer malloc'ed, stuffed into argv[i] */ int argc; /* number of elements in the array so far */ int increase; /* number of bytes to expand argv[] array by */ int numentries; /* number of slots allocated in argv [] array */ int cursize; /* total number of bytes in argv[] currently */ char *malloc (), *realloc (); /* malloc (3) */ if (setdir (dirname) == -1) { sprintf (errbuf, "setdir: can't open %s", dirname); perror (errbuf); exit (1); } /* number of slots in argv array each time expanded by */ #define NUMWANTED 25 /* compute size in bytes for expanding argv[] by NUMWANTED slots */ increase = cursize = NUMWANTED * sizeof (*argv); /* allocate pointer array the first time */ if ((argv = (char **) malloc (cursize)) == (char **) 0) { *pargc = -1; return; } else { numentries = NUMWANTED; /* number of slots allocated */ argv [0] = ""; argc = 1; /* number of slots filled so far */ } while ((rp = getdir ()) != (char *) 0) { /* first expand the array if we need to */ if (++argc > numentries) { /* have to reallocate a bigger array */ argv = (char **) realloc ((char *)argv, cursize = cursize + increase); if (argv == (char **) 0) { *pargc = -1; /* lost the old array!!! */ return; } numentries += NUMWANTED; } /* get room to copy file name from static area returned * from getdir (), if can't, loose filename. */ fp = malloc ((strlen (rp) + 1)); /* NULL at end */ if (fp == (char *) 0) { --argc; break; } /* now this name will fit in the array, so put it there */ strcpy (fp, rp); argv [argc-1] = fp; } /* return with answers */ *pargc = argc; *pargv = argv; } \SHAR_EOF # ............ F I L E E N D .......... lookdir.c fi # end of overwriting check if test -f machine.h then echo shar: will not over-write existing file 'machine.h' else echo x - machine.h # ............ F I L E B E G .......... machine.h cat << '\SHAR_EOF' > machine.h /* * must define only one of S3, S5, S5R2, BSD4_1, or BSD4_2, CPGCOS to be 1 */ #define CPGCOS 1 #include "config.h" \SHAR_EOF # ............ F I L E E N D .......... machine.h fi # end of overwriting check if test -f sample.out then echo shar: will not over-write existing file 'sample.out' else echo x - sample.out # ............ F I L E B E G .......... sample.out cat << '\SHAR_EOF' > sample.out Outgoing Traffic Machine C Bytes D Bytes X Bytes Files eli 129 252 60 1 ccicpg 386 25617 195 3 cci632 252 2194 134 2 men1 544 122334 224 4 vrdxhq 710 288291 315 5 dsi1 680 179694 280 5 \SHAR_EOF # ............ F I L E E N D .......... sample.out fi # end of overwriting check if test -f uustatd.c then echo shar: will not over-write existing file 'uustatd.c' else echo x - uustatd.c # ............ F I L E B E G .......... uustatd.c cat << '\SHAR_EOF' > uustatd.c /* * uustatd.c * dennis bednar 05 23 86 * Computer Consoles Inc, 11490 Commerce Park Drive, Reston VA 22091 * seismo!rlgvax!dennis (UUCP address) * * uucp statistics program. "d" stands for dennis to avoid confusion * with the standard "uustat" program. * * Read C. "work" files in the current directory, and * print statistics of what we have for outgoing traffic. * * Read X. "local execute" files in the current directory, and * print statistics of what we have for incoming traffic. * * -x99 = debug flag * -s<sys> = machine name or system name * -dir<dir> = process directory other that /usr/spool/uucp * * * * NOTE: This command ONLY works on a version of uucp in which * there are *no* subdirectories in the UUCPSPOOLDIR. It will * *NEED* modification to work on versions of uucp which * have sub-directories. If you decide to modify this code, * then I suggest you #ifdef it with SUBDIR so that it can easily * be compiled to work with either kind of machine. Send modifications * back to the original author (dennis@rlgvax.uucp). * * The external directory reading routines can be #ifdef'ed * for V7/S3/S5 14 character fixed length names or can be * #ifdefed for 4.2BSD variable (up to 255 chars) file names. * */ #include <stdio.h> #include <ctype.h> /* isdigit() */ #include <sys/types.h> #include <sys/stat.h> #include <dir.h> #define TRUE 1 #define FALSE 0 #define STR_SAME !strcmp #define STR_DIFF strcmp #define STRN_SAME !strncmp #define STRN_DIFF strncmp #define MAXFILELEN 100 #define MAXLINE 256 /* routines that read directory entries */ extern int setdir(); extern char *getdir(); extern char *malloc(); #define MAXMCHNAME 20 struct statblock { /* s_next *must* be first */ struct statblock *s_next; /* next one in queue */ char s_mchname[MAXMCHNAME+1]; /* store machine name here */ long s_csize; /* number of chars in all C. command files */ long s_dsize; /* number of chars in all D. data files */ long s_xsize; /* number of chars in all X. xqt files */ int s_numq; /* number queued up */ }; struct statblock *getstatp(); /* get statistics block pointer */ long filesize(); char *u_errmesg(); char *getmchname(); char *getl_file(); char *cmd; int debug = 0; /* >=1 prints Warning msgs. >=10 prints all */ char *flag_mch; /* only interested in this machine */ char *flag_dir; /* special case to work in this directory * instead of default UUCPSPOOLDIR. */ /* shared by getstatb(), dump_stat(), and clean_stat() */ static struct statblock *mchlist = NULL; #define UUCPSPOOLDIR "/usr/spool/uucp" main(argc, argv) int argc; char *argv[]; { char *wdir; /* name of directory to work in */ cmd = argv[0]; getargs(argc, argv); if (flag_dir) wdir = flag_dir; /* user overridden directory */ else wdir = UUCPSPOOLDIR; /* default directory */ if (chdir( wdir ) == -1) { fprintf(stderr, "%s: cannot chdir(%s)\n", cmd, wdir); exit(1); } pass1(); pass2(); exit(0); } /* * Outgoing traffic is processed * Also each D. and X. file "attached" to a C. file is recorded now. * Later in pass 2, when the entire directory is rescanned, we * will report those "detached" D. and X. files. */ pass1() { char *fname; /* file name */ /* open the current directory */ if ( setdir(".") == -1) { fprintf(stderr, "%s: cannot open directory\n", cmd); exit(1); } /* read until eof on directory */ while ( (fname = getdir()) != (char *)NULL) { if (debug > 10) printf("%s\n", fname); process("C.", fname); } if (mchlist) /* have statistics for at least one machine */ { printf(" Outgoing Traffic\n"); dump_stat(); clean_stat(); } } /* * Incoming traffic is processed */ pass2() { char *fname; /* file name */ /* open the current directory */ if ( setdir(".") == -1) { fprintf(stderr, "%s: cannot open directory\n", cmd); exit(1); } /* read file names */ while ( (fname = getdir()) != (char *)NULL) { if (debug > 10) printf("%s\n", fname); if (STRN_SAME(fname, "X.", 2) || STRN_SAME(fname, "D.", 2)) chk_detach(fname); /* see if detached */ process("X.", fname); } if (mchlist) { printf("\n Incoming Traffic (Unprocessed XQT files)\n"); dump_stat(); /* don't worry about clearing statistics 2nd and last time */ } } /* * only process the C. files, because they contain text info * that points to the attached D. datafile, and attached X. * execute file. * * For example, for outgoing traffic: * the file C.dsi1AD0060 contains: S D.dsi1BC0060 D.dsi1BC0060 network - D.dsi1BC0060 0666 network S D.rlgvaxXA0060 X.rlgvaxXA0060 network - D.rlgvaxXA0060 0666 network * the file D.dsi1BC0060 contains a news batch created by 'batch', * and the fiile D.rlgvaxXA0060 contains the commands: U network rlgvax F D.dsi1BC0060 I D.dsi1BC0060 C rnews * * For incoming traffic, there is an X.<remotesite>... file that * contains an "F" or "I" line which contains the name of the * local file which will be processed by /usr/lib/uuxqt. */ process(prefix, cfile) char *cfile; /* name of C. command file */ { FILE *fp; char *rtn1, *rtn2; char line1[MAXLINE+2]; char line2[MAXLINE+2]; /* room for newline, null */ char *mch; /* name of machine */ char *dfile; /* name of datafile */ char *xfile; /* name of execute file */ static char ddfile[MAXFILELEN+1]; /* room for null */ if (debug > 10) printf("process(%s) called\n", cfile); if (!isregfile(cfile)) { if (debug >= 1) fprintf(stderr, "%s: Warning, ignoring irregular file %s\n", cmd, cfile); return; } /* skip files that don't begin with file prefix */ /* in pass1, (Outgoing files), skip files that don't begin with C. */ /* in pass2, (Incoming files), skip files that don't begin with X. */ if (STRN_DIFF(cfile, prefix, strlen(prefix))) return; /* skip files that don't have the desired machine embedded in them */ mch = getmchname(cfile); if (debug > 10) printf("process: machine name was %s\n", mch); if (flag_mch && STR_DIFF(mch, flag_mch)) return; fp = fopen(cfile, "r"); if (fp == NULL) { fprintf(stderr, "%s: cannot open %s: %s\n", cmd, cfile, u_errmesg()); exit(1); } /* if premature EOF on either line, ignore this file */ if (STR_SAME(prefix, "C.")) { /* process outgoing C. "work" file */ rtn1 = fgets(line1, sizeof(line1), fp); rtn2 = fgets(line2, sizeof(line2), fp); if ( (rtn1 == NULL) || (rtn2 == NULL) ) { if (debug >= 1) fprintf(stderr, "%s: Warning ignorning file w/o 2 lines: %s\n", cmd, cfile); return; } } else { /* process incoming X. file */ /* in the X. file, search for an F file directive */ /* the file name after it refers to a file on the local mch */ while (fgets(line1, sizeof(line1), fp) != NULL) { if (line1[0] == 'F') break; } if (debug > 2) printf("read F line <%s>\n", line1); } fclose(fp); /* Warning *must* copy static dfile to ddfile because getl_file * reuses the static buffer within. */ if (STR_SAME(prefix, "C.")) { /* process outgoing C. file */ dfile = getl_file("S ", line1); /* get name of D. file from line */ if (dfile && debug > 10) printf("process: Data File = %s\n", dfile); if (dfile) strcpy(ddfile, dfile); xfile = getl_file("S ", line2, cfile); if (xfile && debug > 10) printf("process: XQT File = %s\n", xfile); if (dfile && xfile) add_stat(mch, cfile, ddfile, xfile); else fprintf(stderr, "%s: Warning, possible bad format in file '%s'\n", cmd, cfile); } else { /* process incoming X. file */ xfile = cfile; /* file name passed */ dfile = getl_file("F ", line1, cfile); if (dfile) add_stat(mch, NULL, dfile, xfile); else fprintf(stderr, "%s: Warning, skipping file '%s'\n", cmd, cfile); } } /* * return UNIX error message associated with errno * more flexible than perror(3) */ char * u_errmesg() { extern int errno; extern int sys_nerr; extern char *sys_errlist[]; static char buffer[50]; if (errno < 0 || errno >= sys_nerr) { sprintf( buffer, "errno %d undefined (%d=max)", errno, sys_nerr); return(buffer); } return( sys_errlist[errno] ); } /* * return true if the file is a regular file (ie not directory, pipe, etc.) */ isregfile(fname) char *fname; /* file name terminated by null */ { int fd; /* file descriptor */ struct stat statbuf; /* read info about a file into here */ int rtn; /* return code from system call */ fd = open(fname, 0); if (fd == -1) { fprintf(stderr, "%s: isregfile: cannot open %s: %s\n", cmd, fname, u_errmesg()); return FALSE; } rtn = fstat(fd, &statbuf); if (rtn == -1) { fprintf(stderr, "%s: cannot stat %s: %s\n", cmd, fname, u_errmesg()); exit(1); } close(fd); /* ignore error */ if ( (statbuf.st_mode & S_IFMT) == S_IFREG) /* is a reg file */ return TRUE; else return FALSE; } /* * return number of characters in a file */ long filesize(fname) char *fname; /* file name terminated by null */ { int fd; /* file descriptor */ struct stat statbuf; /* read info about a file into here */ int rtn; /* return code from system call */ /* no file name was passed */ if (fname == NULL) return 0L; fd = open(fname, 0); if (fd == -1) { if (debug >= 1) fprintf(stderr, "%s: Warning, cannot open %s to get size: %s\n", cmd, fname, u_errmesg()); return 0L; } rtn = fstat(fd, &statbuf); if (rtn == -1) { fprintf(stderr, "%s: cannot stat %s: %s\n", cmd, fname, u_errmesg()); exit(1); } close(fd); /* ignore error */ return (long) statbuf.st_size; } /* * get machine name from a UUCP file name * format is D.machineXY0000 * C.machineAY0000 * "machine" could end with a digit such as "dsi1" * */ char * getmchname(fname) char *fname; /* CANNOT have slash in file name */ { register char *cp; #define MCHNAMESIZE 20 static char mchname[MCHNAMESIZE+1]; /* room for null */ int len; cp = fname; cp += strlen(fname); /* points to null terminator */ --cp; /* last char of fname */ /* skip backwards over digits until a letter is seen */ /* this works even if the number of digits is not 4 */ while ( (cp > fname) && isdigit(*cp)) --cp; /* have come to 2nd letter of pair */ cp--; /* cp now points to first letter of pair, or one * past the machine name. */ len = (cp - (fname+2) ); /* compute length of machine name */ if ( (len <= 0) || (len > MCHNAMESIZE) ) { fprintf(stderr, "%s: getmchname(%s) failed\n", cmd, fname); exit(1); } strncpy(mchname, fname+2, len); mchname[len] = '\0'; /* strncpy doesn't copy a NULL */ return mchname; } /* * get name of file from the line that looks like: S file ... more stuff */ char * getl_file(prefix, line, rdfile) char *prefix; /* either "S " or "F " */ char *line; /* line of data from 'rdfile' */ char *rdfile; /* name of file in case of error */ { static char filename[MAXFILELEN+1]; /* room for null */ char *cp; char *dp; int len; /* skip over the "S " or "F " part of the line */ cp = line; if (STRN_DIFF(line, prefix, strlen(prefix))) { badformat: fprintf(stderr, "%s: prefix '%s' expected in file '%s', line '%s'\n", cmd, prefix, rdfile, line); return NULL; } cp += 2; #define iswhite(c) ( (c == ' ') || (c == '\t') || (c == '\n') || (c == '\0')) if ( iswhite(*cp) ) { fprintf(stderr, "%s: iswhite char (%c) found at line+2\n", cmd, *cp); goto badformat; } /* copy the file name from 'cp' to 'filename' */ dp = filename; len = 0; while ( !iswhite(*cp)) { *dp++ = *cp++; if (++len > MAXFILELEN) { fprintf(stderr, "%s: filename in line %s was too long\n", cmd, line); exit(1); } } *dp = '\0'; return filename; } /* * add statistics for this machine name. */ add_stat(mch, cfile, dfile, xfile) char *mch, *cfile, *dfile, *xfile; { struct statblock *sp; if (debug > 10) printf("addstat(mch=%s, cfile=%s, dfile=%s, xfile=%s) called\n", mch, cfile, dfile, xfile); /* get an existing or brand-new statistics block for this machine */ sp = getstatp(mch); if (cfile) sp->s_csize += filesize(cfile); if (dfile) { sp->s_dsize += filesize(dfile); addtolist(dfile); /* D. file is attached */ } if (xfile) { sp->s_xsize += filesize(xfile); addtolist(xfile); /* X. file is attached */ } sp->s_numq++; if (debug > 10) printf("add_stat: %s %ld %ld %ld %d\n", sp->s_mchname, sp->s_csize, sp->s_dsize, sp->s_xsize, sp->s_numq); } /* * get a pointer to a statistics block. * Search the existing list, if found, return it. * Otherwise allocate a new block, hook onto end of list, zero out * statistics counters, and return it. */ struct statblock * getstatp(mch) char *mch; { struct statblock **p; /* previous item.ptr in list */ struct statblock *n; /* next item in list */ struct statblock *new; /* new malloced statistics block */ /* search existing list */ for (p = &mchlist; n = *p; p = (struct statblock **)n) { if (STR_SAME(mch, n->s_mchname)) return n; } /* not found, p points to ptr where new stat block ptr will be stored */ /* note how this code is cleverly written to work even if the * list is empty the first time, or when the list is non-empty * the 2nd, 3rd, etc. times. */ new = (struct statblock *)malloc(sizeof(struct statblock)); if (new == NULL) { fprintf(stderr, "%s: malloc failed\n", cmd); exit(1); } /* hook onto end of existing list if any */ *p = new; new->s_next = NULL; /* ground end of linked list */ strcpy(new->s_mchname, mch); /* store new machine name */ new->s_csize = new->s_dsize = new->s_xsize = 0L; new->s_numq = 0; return new; } dump_stat() { struct statblock *p; p = mchlist; printf("Machine C Bytes D Bytes X Bytes Files\n"); while (p) { printf("%s %ld %ld %ld %d\n", p->s_mchname, p->s_csize, p->s_dsize, p->s_xsize, p->s_numq); p = p->s_next; } } /* * discard old statistics by freeing statistics blocks in linked list */ clean_stat() { struct statblock *p, /* current statblock item */ *n; /* next one after this one */ for (p = mchlist; p; p = n) { n = p->s_next; /* save next item ptr ... */ free(p); /* before discarding current item */ } mchlist = NULL; /* list is empty again */ } /* * get arguments from command line, assign global flag variables */ getargs(argc, argv) int argc; char **argv; { register int i; if (argc != 1) for (i = 1; i < argc; ++i) { if ( STRN_SAME(argv[i], "-x", 2) ) debug = atoi(&argv[1][2]); else if (STRN_SAME( argv[i], "-s", 2) ) flag_mch = argv[i]+2; else if (STRN_SAME( argv[i], "-dir", 4) ) flag_dir = argv[i]+4; } } struct node { struct list *l_next; /* MUST be first, next item in list */ char l_name[1]; /* domain name + 1 for null */ }; struct node *head = NULL; /* * standard FIFO implemented by linked list. * NO sorting. */ addtolist(name) char *name; { register struct node **p, /* previous item */ *n; /* next item */ struct node *np; /* node pointer */ /* find node pointed to by p which is the end of the list to tack onto */ for (p = &head; n = *p; p = (struct node **)n) if (STR_SAME(n->l_name, name)) return; /* already in list */ /* use same trick that cpio uses */ np = (struct node *) malloc( sizeof(struct node) + strlen(name) ); if (np == NULL) { perror("out of memory"); exit(1); } /* stuff node with domain name */ np->l_next = NULL; /* ground end of list */ strcpy(np->l_name, name); /* hook onto end of list */ *p = np; /* printf("dbg: %s\n", name); /* print domains as we hit them */ /* done */ } /* * search the linked list, returning TRUE iff found. * The arg passed will be a D. or X. file name saved during * pass 1, hopefully. If not, its detached. */ inlist(fname) char *fname; { register struct node **p, /* previous item */ *n; /* next item */ for (p = &head; n = *p; p = (struct node **)n) if (STR_SAME(n->l_name, fname)) return 1; return 0; } chk_detach(fname) char *fname; { if (!inlist(fname)) fprintf(stderr, "%s: Warning: %s is detached\n", cmd, fname); } \SHAR_EOF # ............ F I L E E N D .......... uustatd.c fi # end of overwriting check # end of shell archive exit 0 -- -Dennis Bednar {decvax,ihnp4,harpo,allegra}!seismo!rlgvax!dennis UUCP