shimell@stc.UUCP (Dave Shimell) (04/27/85)
<eat me> Here is a batch system for BSD4.2 written by Bruce Ollie Munro. (I am posting it as he is currently away on a course - please address flames etc to ollie@stc.UUCP) The batch system uses the line printer queueing system to do the hard work. This has the advantages of using standard software to control queues. Thus batch queues may be manipulated by lpc. We have run this batch system for a number of months with no problems. Please note that the Makefile is an augmented Make so this may not work on your system. To install for the first time type: make new (as root) May I suggest that if your system is greatly loaded then you should think about running the load contol software distributed over the net recently from San Diego. We run it in addition to batch and are greatly impressed! Regards, Dave Shimell. <shimell@stc.UUCP> {root44, ukc, idec, stl, creed}!stc!shimell --=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # To unbundle, csh this file echo Makefile cat >Makefile <<'End of Makefile' # Makefile for batch # %W% # USER = root SPOOL = /usr/spool/lpd/batch SCCS = . BIN = /usr/local LIBDIR = /usr/local/lib MAN = /usr/man/manl PROGS = batch bback CFLAGS = -O LIBS = -llocal #------------------------------------------------------------------------------- all: $(PROGS) @echo programs are up to date $(PROGS): $$@.o $(CC) $(LDFLAGS) $@.o $(LIBS) mv a.out $@ install cp: $(BIN)/batch $(LIBDIR)/bback $(MAN)/batch.l @echo installed programs are up to date new: install printcap mkdir $(SPOOL) cat /dev/null > $(SPOOL)/LOGFILE cat printcap >> /etc/printcap $(BIN)/batch: batch -rm -f $@ cp $? $@ $(LIBDIR)/bback:bback -rm -f $@ cp $? $@ chown $(USER) $@ chmod 4755 $@ $(MAN)/batch.l: batch.n -rm -f $@ cp $? $@ .DEFAULT: $(GET) $(GFLAGS) $(SCCS)/s.$< rm clean tidy: -rm -f $(PROGS) batch.o bback.o batch.n 'End of Makefile' echo batch.c cat >batch.c <<'End of batch.c' #include <stdio.h> #include <assert.h> #ifndef lint #ifndef NSCCS static char sccsid[] = "%W%"; #endif #endif #define MAXLINE 512 #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif typedef int bool; extern char **environ; extern char *optarg; extern int optind; main(argc, argv) char **argv; { register char **envp; /* Environment pointer */ register char *s1; /* Pointers used in inserting qoute escape */ register char *s2; /* in environment variable names */ int c; /* Option character */ int n; /* Number of sscanf arguments */ char name[MAXLINE]; /* Environment variable name */ char value[MAXLINE]; /* Environment variable value */ char value2[MAXLINE]; /* Environment value after escape of quotes */ char pathname[MAXLINE]; /* Pathname of current working directory */ char *shell; /* Name of shell */ char *ofile = "batch.log"; /* Job output file, set to default */ char *efile = NULL; /* Job error file */ bool notsh; /* TRUE if shell is other than sh */ bool mail = FALSE; /* Mail required switch */ FILE *pfp; extern FILE *popen(); extern char *getenv(); /* Routine to access environment variables */ extern char *getwd(); /* Routine to get current working directory */ extern int getopt(); /* Routine to return command line options */ while ((c = getopt(argc, argv, "me:o:")) != EOF) { switch (c) { case 'o' : ofile = optarg; break; case 'e' : efile = optarg; break; case 'm' : mail = TRUE; break; case '?' : quit("Usage: batch [-m] [-o file] [-e file] [command [args .... ]]"); } } if (efile == NULL) efile = ofile; if ((pfp = popen("/usr/ucb/lpr -P batch", "w")) == NULL) quit("batch: can't pipe to lpr"); fprintf(pfp, "%s\n", ofile); fprintf(pfp, "%s\n", efile); fprintf(pfp, "%d\n", mail); fprintf(pfp, "%s\n", getwd(pathname)); fprintf(pfp, "umask %o\n", umask(0)); /* * Get environment variables for command file, * scanf used to seperate values from names in * order to place quotes around the values so * that those containing spaces will be handled * correctly by 'sh' */ for (envp = environ; *envp != NULL; envp++) { n = sscanf(*envp, "%[^=]=%[^\n]", name, value); assert(n == 2); /* * Check for occurence of qoute (') in environment values * and replace them with '\'' in order that sh does not * get confused when executing job. */ s1 = value; s2 = value2; while (*s2++ = *s1) { /* Assignment */ if (*s1++ == '\'') { *s2++ = '\\'; *s2++ = '\''; *s2++ = '\''; } } fprintf(pfp, "%s='%s'\n", name, value2); fprintf(pfp, "export %s\n", name); } /* * Checks if shell is not 'sh' and if not places * an invocation line for the correct shell in * the command file */ shell = getenv("SHELL"); if (notsh = strcmp(shell, "/bin/sh")) fprintf(pfp, "%s << 'xxAARRGGHHxx'\n", shell); /* Place user's commands in command file */ if (argv[optind] == NULL) while ((c = getchar()) != EOF) putc(c, pfp); else { for (; argv[optind]; ++optind) fprintf(pfp, "%s ", argv[optind]); putc('\n', pfp); } /* * Shell termination line if not 'sh' */ if (notsh) fprintf(pfp, "xxAARRGGHHxx\n"); pclose(pfp); exit(0); } 'End of batch.c' echo batch.n cat >batch.n <<'End of batch.n' .TH BATCH LOCAL .SH NAME batch \- submit a batch command .SH SYNTAX .B batch [\-o stdout] [\-e stderr] [\-m] [command [arg ...]] .SH DESCRIPTION .I Batch submits a job to the batch job queue, where it will be run at low priority. This is useful for large jobs in order that they do not load the system heavily at peak times. .B Default if no command is specified is to read command from the .I "standard input." A log of the commands' output is maintained by .I batch. .I Batch interprets only the options, which may be in any order, that are placed .I before the command. The options are: .TP 10 .B "\-o file" Appends the standard output of .I command to .I file. Default file is .I batch.log. .TP 10 .B "\-e file" Appends the standard error of .I command to .I file. Default is to merge standard error with standard output. .TP 10 .B \-m Sends mail to user to inform them of completion of batch job. .PP The state of the batch queue may be examined using lpq(1) invoked as: .IP .I "lpq -P batch." .PP Batch jobs may be dequeued using lprm(1) invoked as: .IP .I "lprm -P batch." (See RESTRICTIONS below) .SH EXAMPLES .sp 3 .IP .br batch nroff xyz.n .PP will nroff xyz.n placing the standard output in .I batch.log. .PP .IP batch .br nroff xyz.n >xyz.doc .br <EOF> .PP will nroff xyz.n and redirect the standard output to .I xyz.doc. .PP The same effect could be achieved by:\- .PP .IP batch \-o xyz.doc nroff xyz.n .PP The above examples will place any error reports from the command on the standard output. .PP However .PP .IP batch \-e error.log \-o xyz.doc nroff xyz.n .PP nroff's xyz.n appending standard output to xyz.doc and standard error to error.log. .s3 .ne 6 .IP batch \-m .br cd ~/mydocs .br foreach i (*.n) .br tbl \-TX $i | nroff \-mm | col | lpr .br end .br <EOF> .PP will format documents from the mydocs directory and print them. When the job is complete mail will be sent to the user. .SH FILES .PD 0 .TP 32 batch.log default standard output file .TP 32 /usr/spool/lpd/batch/LOGFILE record of all batch jobs and their time statistics .PD .SH "SEE ALSO" lpq(1), lprm(1), lpc(8), lpd(8) .SH RESTRICTIONS If the command to be batched is specified on the invocation line all shell metacharacters should be escaped in order that they are not interpreted by the current login shell. .PP Using lprm(1) on the active batch job will cause some error messages. If the job removed is the only one in the queue then no harm is done, however if there are jobs waiting removing the active job will cause the daemon to stop and the queue must be restarted by the system administrator. 'End of batch.n' echo bback.c cat >bback.c <<'End of bback.c' #include <stdio.h> #include <pwd.h> #include <assert.h> #include <sys/param.h> #include <sys/quota.h> #include <sys/time.h> #include <sys/resource.h> #include <sys/wait.h> #ifndef NSCCS #ifndef lint static char sccsid[] = "%W%"; #endif #endif typedef int bool; #define MAXLINE 512 #define DAEMON_UID 1 #define LOW_PRIORITY 10 #define NORM_EXIT 0 #define BAD_EXIT 2 /* * convert whole seconds to number of clock * cycles by multiplying by CYCLE_SECS */ #define CYCLE_SECS 100 /* * convert micro-seconds to number of clock * cycles by dividing by USEC_CYCLES */ #define USEC_CYCLES 10000 /* * reduce number in micro-secs to tenths * secs by dividing by RED_TO_TENTHS */ #define RED_TO_TENTHS 100000 #define SECS_IN_MIN 60 extern char *optarg; extern char *program; extern int fail_status; char *user; /* User's login name */ extern char *gets(); main(argc, argv) char **argv; { char ofile[MAXLINE]; /* STDOUT filename */ char efile[MAXLINE]; /* STDERR filename */ char dir_path[MAXLINE]; int c; union wait status; /* Exit status of child */ union wait execute(); int n; bool mail; /* Mail required switch */ struct passwd *pw; /* structure for user's passwd file entry */ program = "batch backend"; fail_status = BAD_EXIT; while ((c = getopt(argc, argv, "w:l:i:n:h:")) != EOF) { switch (c) { case 'n': user = optarg; break; case '?': quit("Bad option -%c from lpd", c); } } /* * Check for attempts to use backend as root * or daemon by bypassing queueing process */ if (getuid() != DAEMON_UID) { pw = getpwuid(geteuid()); quit("attempt by user %s to use status of %s in batch", pw->pw_name, user); } /* * Get user's uid and gid from passwd file. */ if ((pw = getpwnam(user)) == NULL) quit("Non-existent user name- %s from lpd", user); endpwent(); /* * Set unbuffered input so that parent doesn't * read any command lines intended for child's use */ setbuf(stdin, NULL); gets(ofile); gets(efile); n = scanf("%d\n", &mail); assert(n == 1); /* * Change to passed directory name */ if (chdir(gets(dir_path)) == -1) quit("Couldn't access directory (%s)", dir_path); status = execute(ofile, efile, pw->pw_uid, pw->pw_gid); if (status.w_T.w_Termsig || status.w_T.w_Retcode) badstat(status); if (mail) sendmail(user, status); exit(NORM_EXIT); } /* * forks, execs sh with output directed * to ofile and efile and does setuid and * setgid to specified values */ union wait execute(ofile, efile, uid, gid) char *ofile; char *efile; int uid; int gid; { int pid; /* Return value from fork system call */ union wait status; /* Return value from time_command function */ union wait time_job(); if ((pid = fork()) == -1) quit("couldn't fork"); if (pid == 0) { if (quota(Q_SETUID, uid, NULL, NULL) == -1) quit("Cannot set quotas for %s", user); setgid(gid); setuid(uid); if (freopen(ofile, "a", stdout) != stdout) quit("can't create %s", ofile); if (strcmp(ofile, efile) == 0) { if (dup2(1, 2) == -1) quit("can't duplicate standard error to standard output"); } else if (freopen(efile, "a", stderr) != stderr) quit("can't create %s", efile); setbuf(stdout, NULL); setpriority(PRIO_PROCESS, getpid(), LOW_PRIORITY); execl("/bin/sh", "sh", "-s", NULL); quit("execl failed"); } status = time_job(); return(status); } /* * Waits for job to finish, collects resource stats on job * from wait3 system call and outputs on stderr a summary * of these stats in similar format to the csh time command */ union wait time_job() { union wait status; /* Return value from wait3 system call */ struct rusage rusage; /* Resources used by process */ struct timeval inittime;/* Time returned by gettimeofday system call */ struct timeval endtime; /* ditto */ struct timezone dummy; /* time zone correction (not used) */ u_long elap_time; /* Elapsed time during execution of job */ long cpu_cycles; /* No. of cycles of cpu time */ long elap_cycles; /* Elapsed time in terms of cycles */ gettimeofday(&inittime, &dummy); wait3(&status, NULL, &rusage); gettimeofday(&endtime, &dummy); elap_time = endtime.tv_sec - inittime.tv_sec; elap_cycles = ((endtime.tv_sec * CYCLE_SECS) + (endtime.tv_usec/USEC_CYCLES)) - ((inittime.tv_sec * CYCLE_SECS) + (inittime.tv_usec/USEC_CYCLES)); cpu_cycles = (((rusage.ru_utime.tv_sec + rusage.ru_stime.tv_sec) * CYCLE_SECS) + ((rusage.ru_utime.tv_usec + rusage.ru_stime.tv_usec)/USEC_CYCLES)); fprintf(stderr, "Job: %-9s", user); fprintf(stderr, "%ld.%ldu %ld.%lds %ld:%02ld %02ld%% %ld+%ldk %ld+%ldio %ldpf+%ldw\n", rusage.ru_utime.tv_sec, rusage.ru_utime.tv_usec/RED_TO_TENTHS, rusage.ru_stime.tv_sec, rusage.ru_stime.tv_usec/RED_TO_TENTHS, elap_time/SECS_IN_MIN, elap_time % SECS_IN_MIN, cpu_cycles * CYCLE_SECS/elap_cycles, rusage.ru_ixrss/cpu_cycles, (rusage.ru_idrss + rusage.ru_isrss)/cpu_cycles, rusage.ru_inblock, rusage.ru_oublock, rusage.ru_majflt, rusage.ru_nswap ); return(status); } /* * Sends mail to the user on completion of the job, * indicating the exit status of the job */ sendmail(user, status) char *user; union wait status; { FILE *pp; char mail[MAXLINE]; extern FILE *popen(); sprintf(mail, "/usr/ucb/mail -s 'batch job' %s", user); if ((pp = popen(mail, "w")) == NULL) quit("can't pipe to /usr/ucb/mail"); fprintf(pp, "Your batch job "); if (!status.w_T.w_Termsig && !status.w_T.w_Retcode) fprintf(pp, "completed successfully.\n"); else { fprintf(pp, "terminated abnormally "); if (status.w_T.w_Termsig == 0) fprintf(pp, "with exit status %d.\n", status.w_T.w_Retcode); else { fprintf(pp, "due to signal %d.\n", status.w_T.w_Termsig); if (status.w_T.w_Coredump != 0) fprintf(pp, "Signal caused a core dump.\n"); } } pclose(pp); } badstat(status) union wait status; { if (status.w_T.w_Termsig == 0) error("job exited with status %u", status.w_T.w_Retcode); else { error("job terminated due to signal %u", status.w_T.w_Termsig); if (status.w_T.w_Coredump) error("Core dumped"); } } 'End of bback.c' echo printcap cat >printcap <<'End of printcap' batch|batch job queue:\ :lp=/dev/null:sd=/usr/spool/lpd/batch:\ :if=/usr/local/lib/bback:\ :lf=/usr/spool/lpd/batch/LOGFILE: 'End of printcap' -- Regards, Dave Shimell. <shimell@stc.UUCP> {root44, ukc, idec, stl, creed}!stc!shimell
shimell@stc.UUCP (Dave Shimell) (04/27/85)
<I'm hungry> Here are the routines required for the local library. These routines are required for the batch program in the previous posting. Note, these routines were originally built for portability + convenience. The Makefile will build a library for BSD4.2 by default. Regards, Dave Shimell. <shimell@stc.UUCP> {root44, ukc, idec, stl, creed}!stc!shimell -=-=-=-=-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # To unbundle, csh this file echo Makefile cat >Makefile <<'End of Makefile' # Makefile for local libraries # %W% # SCCS = . BIN = /usr/local/lib LIB = liblocal.a CFLAGS = -O #------------------------------------------------------------------------------- all: $(LIB) @echo programs are up to date .DEFAULT: $(GET) $(GFLAGS) $(SCCS)/s.$< rm clean tidy: cp install: $(LIB) -rm -f $(BIN)/$(LIB) cp $(LIB) $(BIN)/$(LIB) ranlib $(BIN)/$(LIB) # The following is a list of files for BDS4.2. $(LIB): \ $(LIB)(curdir.o)\ $(LIB)(dname.o)\ $(LIB)(fswitch.o)\ $(LIB)(getopt.o)\ $(LIB)(sname.o)\ $(LIB)(error.o)\ $(LIB)(quit.o)\ $(LIB)(warning.o)\ $(LIB)(salloc.o)\ $(LIB)(ename.o) # The following is a complete list of files. # This will need editing for your system and # moved above. #$(LIB): # # $(LIB)(curdir.o)\ # $(LIB)(dname.o)\ # $(LIB)(fswitch.o)\ # $(LIB)(getopt.o)\ # $(LIB)(rename.o)\ # $(LIB)(sname.o)\ # $(LIB)(umask.o)\ # $(LIB)(ndir.h)\ # $(LIB)(opendir.o)\ # $(LIB)(closedir.o)\ # $(LIB)(readdir.o)\ # $(LIB)(seekdir.o)\ # $(LIB)(telldir.o)\ # \ # $(LIB)(error.o)\ # $(LIB)(quit.o)\ # $(LIB)(warning.o)\ # $(LIB)(salloc.o)\ # $(LIB)(ename.o)\ 'End of Makefile' echo closedir.c cat >closedir.c <<'End of closedir.c' #ifndef lint #ifndef NSCCS static char sccsid[] = "%W%"; #endif #endif #include <sys/types.h> #include <ndir.h> /* * close a directory. */ void closedir(dirp) register DIR *dirp; { close(dirp->dd_fd); dirp->dd_fd = -1; dirp->dd_loc = 0; free(dirp); } 'End of closedir.c' echo curdir.c cat >curdir.c <<'End of curdir.c' #ifndef lint #ifndef NSCCS static char sccsid[] = "%W%"; #endif #endif #include <stdio.h> extern FILE *popen(); curdir(pwd) char *pwd; /* Fill this in with the current directory */ { register FILE *fp; int reply = -1; if ((fp = popen("pwd", "r")) != NULL) { reply = 0; fscanf(fp, " %s ", pwd); pclose(fp); } return(reply); } 'End of curdir.c' echo dname.c cat >dname.c <<'End of dname.c' #ifndef lint #ifndef NSCCS static char sccsid[] = "%W%"; #endif #endif char * dname(nam) char *nam; { register char *slash; register char *cp; /* * Find last slash in nam. */ for (slash = cp = nam; *cp != '\0'; cp++) if (*cp == '/') slash = cp; if (slash == nam) nam = (*slash == '/' ? "/" : "."); else *slash = '\0'; return(nam); } 'End of dname.c' echo ename.c cat >ename.c <<'End of ename.c' #include <stdio.h> extern char *sname(); #ifndef lint #ifndef NSCCS static char sccsid[] = "%W%"; #endif #endif /* * Returns a pointer to the file type (file * extension) or NULL if one cannot be found. * Eg. * /usr.xyz/file.plm returns ".plm" * /usr.xyz/file returns NULL */ char * ename(file) register char *file; { register char *dot = NULL; for (file = sname(file); *file; file++) if (*file == '.') dot = file; return(dot); } 'End of ename.c' echo error.c cat >error.c <<'End of error.c' #ifndef lint #ifndef NSCCS static char sccsid[] = "%W%"; #endif #endif #include <stdio.h> extern char *program; unsigned ecount; /* VARARGS1 */ error(s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) char *s; { ecount++; fflush(stdout); fprintf(stderr, "%s: error, ", program); fprintf(stderr, s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); fprintf(stderr, "\n"); } 'End of error.c' echo fswitch.c cat >fswitch.c <<'End of fswitch.c' #ifndef lint #ifndef NSCCS static char sccsid[] = "%W%"; #endif #endif #include <stdio.h> #include <assert.h> #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif fswitch() { assert(FALSE); } 'End of fswitch.c' echo getopt.c cat >getopt.c <<'End of getopt.c' #include <stdio.h> #define ERR(S, A) if (opterr) error((S), (A)); else int opterr = 1; int optind = 1; int optopt; char *optarg; char *program; extern char *sname(); extern char *index(); #ifndef lint #ifndef NSCCS static char sccsid[] = "%W%"; #endif #endif int getopt(argc, argv, opts) char **argv, *opts; { static int sp = 1; register c; register char *cp; if (program == NULL) program = sname(argv[0]); if (sp == 1) { if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') return(EOF); else if (strcmp(argv[optind], "--") == NULL) { optind++; return(EOF); } } optopt = c = argv[optind][sp]; if (c == ':' || (cp = index(opts, c)) == NULL) { ERR("illegal option -%c", c); if (argv[optind][++sp] == '\0') { optind++; sp = 1; } return('?'); } if (*++cp == ':') { if (argv[optind][sp + 1] != '\0') optarg = &argv[optind++][sp + 1]; else if (++optind >= argc) { ERR("option -%c requires an argument", c); sp = 1; return('?'); } else optarg = argv[optind++]; sp = 1; } else { if (argv[optind][++sp] == '\0') { sp = 1; optind++; } optarg = NULL; } return(c); } 'End of getopt.c' echo ndir.h cat >ndir.h <<'End of ndir.h' static char ndir[] = "%W%"; /* * Change notice (rti!trt): To be compatible with non-bsd systems: * #defines for u_short, u_long, and void added. */ #define u_short unsigned short #define u_long long #define void /* * A directory consists of some number of blocks of DIRBLKSIZ * bytes, where DIRBLKSIZ is chosen such that it can be transferred * to disk in a single atomic operation (e.g. 512 bytes on most machines). * * Each DIRBLKSIZ byte block contains some number of directory entry * structures, which are of variable length. Each directory entry has * a struct direct at the front of it, containing its inode number, * the length of the entry, and the length of the name contained in * the entry. These are followed by the name padded to a 4 byte boundary * with null bytes. All names are guaranteed null terminated. * The maximum length of a name in a directory is MAXNAMLEN. * * The macro DIRSIZ(dp) gives the amount of space required to represent * a directory entry. Free space in a directory is represented by * entries which have dp->d_reclen >= DIRSIZ(dp). All DIRBLKSIZ bytes * in a directory block are claimed by the directory entries. This * usually results in the last entry in a directory having a large * dp->d_reclen. When entries are deleted from a directory, the * space is returned to the previous entry in the same directory * block by increasing its dp->d_reclen. If the first entry of * a directory block is free, then its dp->d_ino is set to 0. * Entries other than the first in a directory do not normally have * dp->d_ino set to 0. */ #define DIRBLKSIZ 512 #define MAXNAMLEN 255 struct direct { u_long d_ino; /* inode number of entry */ u_short d_reclen; /* length of this record */ u_short d_namlen; /* length of string in d_name */ char d_name[MAXNAMLEN + 1]; /* name must be no longer than this */ }; /* * The DIRSIZ macro gives the minimum record length which will hold * the directory entry. This requires the amount of space in struct direct * without the d_name field, plus enough space for the name with a terminating * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary. */ #undef DIRSIZ #define DIRSIZ(dp) \ ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3)) #ifndef KERNEL /* * Definitions for library routines operating on directories. */ typedef struct _dirdesc { int dd_fd; long dd_loc; long dd_size; char dd_buf[DIRBLKSIZ]; } DIR; #ifndef NULL #define NULL 0 #endif extern DIR *opendir(); extern struct direct *readdir(); extern long telldir(); extern void seekdir(); #define rewinddir(dirp) seekdir((dirp), (long)0) extern void closedir(); #endif KERNEL 'End of ndir.h' echo opendir.c cat >opendir.c <<'End of opendir.c' #ifndef lint #ifndef NSCCS static char sccsid[] = "%W%"; #endif #endif #include <sys/types.h> #include <sys/stat.h> #include <ndir.h> /* * open a directory. */ DIR * opendir(name) char *name; { register DIR *dirp; register int fd; struct stat sbuf; if ((fd = open(name, 0)) == -1) return NULL; fstat(fd, &sbuf); if (((sbuf.st_mode & S_IFDIR) == 0) || ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL)) { close (fd); return NULL; } dirp->dd_fd = fd; dirp->dd_loc = 0; return dirp; } 'End of opendir.c' echo quit.c cat >quit.c <<'End of quit.c' #ifndef lint #ifndef NSCCS static char sccsid[] = "%W%"; #endif #endif int fail_status = 1; /* Exit status on error */ #include <stdio.h> extern char *program; /* VARARGS1 */ quit(s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) char *s; { fflush(stdout); fprintf(stderr, "%s: fatal, ", program); fprintf(stderr, s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); fprintf(stderr, "\n"); exit(fail_status); } 'End of quit.c' echo readdir.c cat >readdir.c <<'End of readdir.c' #ifndef lint #ifndef NSCCS static char sccsid[] = "%W%"; #endif #endif #include <sys/types.h> #include <ndir.h> /* * read an old stlye directory entry and present it as a new one */ #define ODIRSIZ 14 struct olddirect { ino_t od_ino; char od_name[ODIRSIZ]; }; /* * get next entry in a directory. */ struct direct * readdir(dirp) register DIR *dirp; { register struct olddirect *dp; static struct direct dir; for (;;) { if (dirp->dd_loc == 0) { dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); if (dirp->dd_size <= 0) return NULL; } if (dirp->dd_loc >= dirp->dd_size) { dirp->dd_loc = 0; continue; } dp = (struct olddirect *)(dirp->dd_buf + dirp->dd_loc); dirp->dd_loc += sizeof(struct olddirect); if (dp->od_ino == 0) continue; dir.d_ino = dp->od_ino; strncpy(dir.d_name, dp->od_name, ODIRSIZ); dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */ dir.d_namlen = strlen(dir.d_name); dir.d_reclen = DIRBLKSIZ; return (&dir); } } 'End of readdir.c' echo rename.c cat >rename.c <<'End of rename.c' #ifndef lint #ifndef NSCCS static char sccsid[] = "%W%"; #endif #endif rename(from, to) char *from; /* Old name */ char *to; /* New name */ { register status = -1; if (link(from, to) != -1 && unlink(from) != -1) status = 0; return(status); } 'End of rename.c' echo salloc.c cat >salloc.c <<'End of salloc.c' #include <stdio.h> #ifndef lint #ifndef NSCCS static char sccsid[] = "%W%"; #endif #endif extern char *malloc(); extern char *strcpy(); /* * Allocate and copy a string * into dynamic memory. */ char * salloc(nam) char *nam; { register char *p; if ((p = malloc((unsigned)strlen(nam) + 1)) == NULL) quit("dynamic memory exhausted"); return(strcpy(p, nam)); } 'End of salloc.c' echo seekdir.c cat >seekdir.c <<'End of seekdir.c' #ifndef lint #ifndef NSCCS static char sccsid[] = "%W%"; #endif #endif #include <sys/param.h> #include <ndir.h> /* * seek to an entry in a directory. * Only values returned by "telldir" should be passed to seekdir. */ void seekdir(dirp, loc) register DIR *dirp; long loc; { long base, offset; struct direct *dp; /* rti!trt: Always seek. Slower, but safer. This may even fix a bug. if (loc == telldir(dirp)) return; */ base = loc & ~(DIRBLKSIZ - 1); offset = loc & (DIRBLKSIZ - 1); lseek(dirp->dd_fd, base, 0); dirp->dd_loc = 0; while (dirp->dd_loc < offset) { dp = readdir(dirp); if (dp == NULL) return; } } 'End of seekdir.c' echo sname.c cat >sname.c <<'End of sname.c' #ifndef lint #ifndef NSCCS static char sccsid[] = "%W%"; #endif #endif char * sname(nam) register char *nam; { register char *slash; slash = nam; while (*nam != '\0') { if (*nam++ == '/') slash = nam; } return(slash); } 'End of sname.c' echo telldir.c cat >telldir.c <<'End of telldir.c' #ifndef lint #ifndef NSCCS static char sccsid[] = "%W%"; #endif #endif #include <sys/types.h> #include <ndir.h> extern long lseek(); /* needed for pdp 11s -- ikonas!mcm */ /* * return a pointer into a directory */ long telldir(dirp) DIR *dirp; { return (lseek(dirp->dd_fd, 0L, 1) - dirp->dd_size + dirp->dd_loc); } 'End of telldir.c' echo umask.c cat >umask.c <<'End of umask.c' /* * Some systems do not have this * system call. * * DB Shimell September 1984 */ #ifndef lint #ifndef NSCCS static char sccsid[] = "%W%"; #endif #endif umask(numask) { return(0); } 'End of umask.c' echo warning.c cat >warning.c <<'End of warning.c' #ifndef lint #ifndef NSCCS static char sccsid[] = "%W%"; #endif #endif #include <stdio.h> extern char *program; unsigned wcount; /* VARARGS1 */ warning(s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) char *s; { wcount++; fflush(stdout); fprintf(stderr, "%s: warning, ", program); fprintf(stderr, s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); fprintf(stderr, "\n"); } 'End of warning.c' exit 0 -- Regards, Dave Shimell. <shimell@stc.UUCP> {root44, ukc, idec, stl, creed}!stc!shimell