Subject: v22i019: Brian Berliner's concurrent RCS system, Part05/07 Newsgroups: comp.sources.unix Approved: rsalz@uunet.UU.NET X-Checksum-Snefru: 11b1db10 c67b1e76 f8b8c43e 063bd5be Submitted-by: Brian Berliner Posting-number: Volume 22, Issue 19 Archive-name: cvs-berliner/part05 #! /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 'examples/modules' <<'END_OF_FILE' X# X# CVS Modules file for Prisma sources X# $Id: modules,v 1.1 89/08/25 00:00:15 berliner Exp $ X# X# Three differnt line formats are valid: X# key -a aliases... X# key [options] directory X# key [options] directory files... X# X# Where "options" are composed of: X# -i prog Run "prog" on checkin of files X# -o prog Run "prog" on "checkout" of files X# -t prog Run "prog" on tagging of files X# X X# Convenient aliases Xworld -a . Xkernel -a sys lang/adb sparcsim X X# CVSROOT.adm support XCVSROOT -i /usr/local/bin/mkmodules CVSROOT.adm XCVSROOT.adm -i /usr/local/bin/mkmodules CVSROOT.adm Xmodules -i /usr/local/bin/mkmodules CVSROOT.adm modules Xloginfo -i /usr/local/bin/mkmodules CVSROOT.adm loginfo X X# The "sys" entry exists only to make symbolic links after checkout Xsys -o sys/tools/make_links sys X X# Sub-directories of "bin" Xawk bin/awk Xcsh bin/csh Xdiff bin/diff Xmake bin/make Xsed bin/sed Xsh bin/sh X X# Programs that live in "bin" Xcat bin Makefile cat.c Xchgrp bin Makefile chgrp.c Xchmod bin Makefile chmod.c Xcmp bin Makefile cmp.c Xcp bin Makefile cp.c Xdate bin Makefile date.c Xdd bin Makefile dd.c Xdf bin Makefile df.c Xdomainname bin Makefile domainname.c Xdu bin Makefile du.c Xecho bin Makefile echo.c Xed bin Makefile ed.c Xenv bin Makefile env.c Xexpr bin Makefile expr.c Xgrep bin Makefile grep.c Xhostid bin Makefile hostid.c Xhostname bin Makefile hostname.c Xkill bin Makefile kill.c Xldd bin Makefile ldd.c Xline bin Makefile line.c Xln bin Makefile ln.c Xlogin bin Makefile login.c Xls bin Makefile ls.c Xmail bin Makefile mail.c Xmkdir bin Makefile mkdir.c Xmt bin Makefile mt.c Xmv bin Makefile mv.c Xnewgrp bin Makefile newgrp.c Xnice bin Makefile nice.c Xod bin Makefile od.c Xpagesize bin Makefile pagesize.c Xpasswd bin Makefile passwd.c Xpr bin Makefile pr.c Xps bin Makefile ps.c Xpwd bin Makefile pwd.c Xrm bin Makefile rm.c Xrmail bin Makefile rmail.c Xrmdir bin Makefile rmdir.c Xstty bin Makefile stty.c Xsu bin Makefile su.c Xsync bin Makefile sync.c Xtar bin Makefile tar.c Xtee bin Makefile tee.c Xtest bin Makefile test.c Xtime bin Makefile time.c Xwall bin Makefile wall.c Xwho bin Makefile who.c Xwrite bin Makefile write.c X X# Sub-directories of "etc" Xdump etc/dump Xfiles etc/files Xfsck etc/fsck Xgetty etc/getty Xin.routed etc/in.routed Xrestore etc/restore Xrpc.lockd etc/rpc.lockd Xrpc.statd etc/rpc.statd X X# Programs that live in "etc" Xarp etc Makefile arp.c Xbiod etc Makefile biod.c Xchown etc Makefile chown.c Xclri etc Makefile clri.c Xdkinfo etc Makefile dkinfo.c Xdmesg etc Makefile dmesg.c Xfsirand etc Makefile fsirand.c Xhalt etc Makefile halt.c Xifconfig etc Makefile ifconfig.c Xin.rlogind etc Makefile in.rlogind.c Xin.rshd etc Makefile in.rshd.c Xinetd etc Makefile inetd.c Xinit etc Makefile init.c Xmkfs etc Makefile mkfs.c Xmknod etc Makefile mknod.c Xmount etc Makefile mount.c Xnewfs etc Makefile newfs.c Xnfsd etc Makefile nfsd.c Xportmap etc Makefile portmap.c Xpstat etc Makefile pstat.c Xreboot etc Makefile reboot.c Xrenice etc Makefile renice.c Xrmt etc Makefile rmt.c Xshutdown etc Makefile shutdown.c Xsyslogd etc Makefile syslogd.c Xumount etc Makefile umount.c Xupdate etc Makefile update.c Xvipw etc Makefile vipw.c Xypbind etc Makefile ypbind.c X X# Sub-directories of "games" Xadventure games/adventure Xbackgammon games/backgammon Xbattlestar games/battlestar Xboggle games/boggle Xchess games/chess Xching games/ching Xcribbage games/cribbage Xfortune games/fortune Xhack games/hack Xhangman games/hangman Xhunt games/hunt Xlife games/life Xmille games/mille Xmonop games/monop Xquiz games/quiz Xrobots games/robots Xsail games/sail Xsnake games/snake Xtrek games/trek X X# Programs that live in "games" Xarithmetic games Makefile arithmetic.c Xbanner games Makefile banner.c Xbcd games Makefile bcd.c Xbj games Makefile bj.c Xbtlgammon games Makefile btlgammon.c Xcanfield games Makefile canfield.c Xcfscores games Makefile cfscores.c Xcraps games Makefile craps.c Xfactor games Makefile factor.c Xfish games Makefile fish.c Xmoo games Makefile moo.c Xnumber games Makefile number.c Xprimes games Makefile primes.c Xrain games Makefile rain.c Xrandom games Makefile random.c Xworm games Makefile worm.c Xworms games Makefile worms.c Xwump games Makefile wump.c X X# Sub-directories of "lang" Xadb lang/adb Xas lang/as Xboot lang/boot Xc2 lang/c2 Xcgrdr lang/cgrdr Xcompile lang/compile Xcpp lang/cpp Xdbx lang/dbx Xf77 lang/f77 Xinline lang/inline Xiropt lang/iropt Xld lang/ld Xlint lang/lint Xm4 lang/m4 Xpascal lang/pascal Xpcc lang/pcc Xratfor lang/ratfor Xrtld lang/rtld Xtcov lang/tcov Xvroot lang/vroot X X# Programs that live in "lang" Xar lang Makefile ar.c Xnm lang Makefile nm.c Xranlib lang Makefile ranlib.c Xsize lang Makefile size.c Xstrip lang Makefile strip.c Xsymorder lang Makefile symorder.c X X# Sub-directories of "lib" Xcsu lib/csu Xlibc lib/libc X X# Programs that live in "lib" X# NONE X X# Sub-directories of "lib/libc" Xlibc_compat lib/libc/compat Xlibc_crt lib/libc/crt Xlibc_des lib/libc/des Xlibc_gen lib/libc/gen Xlibc_net lib/libc/net Xlibc_inet lib/libc/inet Xlibc_rpc lib/libc/rpc Xlibc_stdio lib/libc/stdio Xlibc_sun lib/libc/sun Xlibc_sys lib/libc/sys Xlibc_yp lib/libc/yp X X# Programs that live in "lib/libc" X# NONE X X#Sub-directories of "local" Xnotes local/notes X X# Sub-directories of "man" Xman1 man/man1 Xman2 man/man2 Xman3 man/man3 Xman4 man/man4 Xman5 man/man5 Xman6 man/man6 Xman7 man/man7 Xman8 man/man8 Xmanl man/manl X X# Programs that live in "man" X# NONE X X# Sub-directories of "old" Xold_compact old/compact Xold_eyacc old/eyacc Xold_filemerge old/filemerge Xold_make old/make X X# Programs that live in "old" Xold_analyze old Makefile analyze.c Xold_prmail old Makefile prmail.c Xold_pti old Makefile pti.c Xold_syslog old Makefile syslog.c X X# Sub-directories of "ucb" XMail ucb/Mail Xcompress ucb/compress Xerror ucb/error Xex ucb/ex Xftp ucb/ftp Xgprof ucb/gprof Xindent ucb/indent Xlpr ucb/lpr Xmore ucb/more Xmsgs ucb/msgs Xnetstat ucb/netstat Xrdist ucb/rdist Xtalk ucb/talk Xtftp ucb/tftp Xtset ucb/tset Xvgrind ucb/vgrind X X# Programs that live in "ucb" Xbiff ucb Makefile biff.c Xchecknr ucb Makefile checknr.c Xclear ucb Makefile clear.c Xcolcrt ucb Makefile colcrt.c Xcolrm ucb Makefile colrm.c Xctags ucb Makefile ctags.c Xexpand ucb Makefile expand.c Xfinger ucb Makefile finger.c Xfold ucb Makefile fold.c Xfrom ucb Makefile from.c Xfsplit ucb Makefile fsplit.c Xgcore ucb Makefile gcore.c Xgroups ucb Makefile groups.c Xhead ucb Makefile head.c Xlast ucb Makefile last.c Xlastcomm ucb Makefile lastcomm.c Xleave ucb Makefile leave.c Xlogger ucb Makefile logger.c Xman_prog ucb Makefile man.c Xmkstr ucb Makefile mkstr.c Xprintenv ucb Makefile printenv.c Xquota ucb Makefile quota.c Xrcp ucb Makefile rcp.c Xrdate ucb Makefile rdate.c Xrlogin ucb Makefile rlogin.c Xrsh ucb Makefile rsh.c Xrup ucb Makefile rup.c Xruptime ucb Makefile ruptime.c Xrusers ucb Makefile rusers.c Xrwho ucb Makefile rwho.c Xsccs ucb Makefile sccs.c Xscript ucb Makefile script.c Xsoelim ucb Makefile soelim.c Xstrings ucb Makefile strings.c Xtail ucb Makefile tail.c Xtcopy ucb Makefile tcopy.c Xtelnet ucb Makefile telnet.c Xul ucb Makefile ul.c Xunexpand ucb Makefile unexpand.c Xunifdef ucb Makefile unifdef.c Xusers ucb Makefile users.c Xvmstat ucb Makefile vmstat.c Xw ucb Makefile w.c Xwc ucb Makefile wc.c Xwhat ucb Makefile what.c Xwhatis ucb Makefile whatis.c Xwhereis ucb Makefile whereis.c Xwhoami ucb Makefile whoami.c Xwhois ucb Makefile whois.c Xxstr ucb Makefile xstr.c Xyes ucb Makefile yes.c X X# Sub-directories of "usr.bin" Xcalendar usr.bin/calendar Xcflow usr.bin/cflow Xctrace usr.bin/ctrace Xcxref usr.bin/cxref Xdc usr.bin/dc Xdes usr.bin/des Xdiff3 usr.bin/diff3 Xsun_eqn usr.bin/eqn Xfile usr.bin/file Xfind usr.bin/find Xgraph usr.bin/graph Xlex usr.bin/lex Xsun_neqn usr.bin/neqn Xsun_nroff usr.bin/nroff Xsun_plot usr.bin/plot Xprof usr.bin/prof Xrefer usr.bin/refer Xrpcgen usr.bin/rpcgen Xspell usr.bin/spell Xsun_tbl usr.bin/tbl Xtip usr.bin/tip Xtrace usr.bin/trace Xsun_troff usr.bin/troff Xuucp usr.bin/uucp Xxsend usr.bin/xsend Xyacc usr.bin/yacc X X# Programs that live in "usr.bin" Xbasename usr.bin Makefile basename.c Xbc usr.bin Makefile bc.c Xcal usr.bin Makefile cal.c Xcb usr.bin Makefile cb.c Xcheckeq usr.bin Makefile checkeq.c Xchkey usr.bin Makefile chkey.c Xclick usr.bin Makefile click.c Xcol usr.bin Makefile col.c Xcomm usr.bin Makefile comm.c Xcpio usr.bin Makefile cpio.c Xcrypt usr.bin Makefile crypt.c Xcsplit usr.bin Makefile csplit.c Xcut usr.bin Makefile cut.c Xderoff usr.bin Makefile deroff.c Xegrep usr.bin Makefile egrep.c Xfgrep usr.bin Makefile fgrep.c Xgetopt usr.bin Makefile getopt.c Xid usr.bin Makefile id.c Xinstallcmd usr.bin Makefile installcmd.c Xiostat usr.bin Makefile iostat.c Xipcrm usr.bin Makefile ipcrm.c Xipcs usr.bin Makefile ipcs.c Xjoin usr.bin Makefile join.c Xkeylogin usr.bin Makefile keylogin.c Xlogname usr.bin Makefile logname.c Xlook usr.bin Makefile look.c Xmesg usr.bin Makefile mesg.c Xnl usr.bin Makefile nl.c Xpack usr.bin Makefile pack.c Xpaste usr.bin Makefile paste.c Xptx usr.bin Makefile ptx.c Xrev usr.bin Makefile rev.c Xscreenblank usr.bin Makefile screenblank.c Xsdiff usr.bin Makefile sdiff.c Xsleep usr.bin Makefile sleep.c Xsort usr.bin Makefile sort.c Xspline usr.bin Makefile spline.c Xsplit usr.bin Makefile split.c Xsum usr.bin Makefile sum.c Xtouch usr.bin Makefile touch.c Xtr usr.bin Makefile tr.c Xtsort usr.bin Makefile tsort.c Xtty usr.bin Makefile tty.c Xuniq usr.bin Makefile uniq.c Xunits usr.bin Makefile units.c Xunpack usr.bin Makefile unpack.c Xxargs usr.bin Makefile xargs.c Xypcat usr.bin Makefile ypcat.c Xypmatch usr.bin Makefile ypmatch.c Xyppasswd usr.bin Makefile yppasswd.c Xypwhich usr.bin Makefile ypwhich.c X X# Sub-directories of "usr.etc" Xautomount usr.etc/automount Xc2convert usr.etc/c2convert Xconfig usr.etc/config Xcron usr.etc/cron Xeeprom usr.etc/eeprom Xetherfind usr.etc/etherfind Xformat usr.etc/format Xhtable usr.etc/htable Ximplog usr.etc/implog Xin.ftpd -a usr.etc/in.ftpd ucb/ftp Xin.named usr.etc/in.named Xin.rwhod usr.etc/in.rwhod Xkeyserv usr.etc/keyserv Xndbootd usr.etc/ndbootd Xpraudit usr.etc/praudit Xrexd usr.etc/rexd Xrpc.bootparamd usr.etc/rpc.bootparamd Xtermcap usr.etc/termcap Xupgrade usr.etc/upgrade Xyp usr.etc/yp Xzic usr.etc/zic X X# Programs that live in "usr.etc" Xac usr.etc Makefile ac.c Xaccton usr.etc Makefile accton.c Xaudit usr.etc Makefile audit.c Xauditd usr.etc Makefile auditd.c Xcatman usr.etc Makefile catman.c Xchroot usr.etc Makefile chroot.c Xdcheck usr.etc Makefile dcheck.c Xdevnm usr.etc Makefile devnm.c Xdumpfs usr.etc Makefile dumpfs.c Xedquota usr.etc Makefile edquota.c Xexportfs usr.etc Makefile exportfs.c Xfoption usr.etc Makefile foption.c Xgettable usr.etc Makefile gettable.c Xgrpck usr.etc Makefile grpck.c Xicheck usr.etc Makefile icheck.c Xin.comsat usr.etc Makefile in.comsat.c Xin.fingerd usr.etc Makefile in.fingerd.c Xin.rexecd usr.etc Makefile in.rexecd.c Xin.telnetd usr.etc Makefile in.telnetd.c Xin.tnamed usr.etc Makefile in.tnamed.c Xkgmon usr.etc Makefile kgmon.c Xlink usr.etc Makefile link.c Xmkfile usr.etc Makefile mkfile.c Xmkproto usr.etc Makefile mkproto.c Xmount_lo usr.etc Makefile mount_lo.c Xncheck usr.etc Makefile ncheck.c Xnfsstat usr.etc Makefile nfsstat.c Xping usr.etc Makefile ping.c Xpwck usr.etc Makefile pwck.c Xquot usr.etc Makefile quot.c Xquotacheck usr.etc Makefile quotacheck.c Xquotaon usr.etc Makefile quotaon.c Xrarpd usr.etc Makefile rarpd.c Xrepquota usr.etc Makefile repquota.c Xroute usr.etc Makefile route.c Xrpc.etherd usr.etc Makefile rpc.etherd.c Xrpc.mountd usr.etc Makefile rpc.mountd.c Xrpc.pwdauthd usr.etc Makefile rpc.pwdauthd.c Xrpc.rquotad usr.etc Makefile rpc.rquotad.c Xrpc.rstatd usr.etc Makefile rpc.rstatd.c Xrpc.rusersd usr.etc Makefile rpc.rusersd.c Xrpc.rwalld usr.etc Makefile rpc.rwalld.c Xrpc.sprayd usr.etc Makefile rpc.sprayd.c Xrpc.yppasswdd usr.etc Makefile rpc.yppasswdd.c Xrpc.ypupdated usr.etc Makefile rpc.ypupdated.c Xrpcinfo usr.etc Makefile rpcinfo.c Xrwall usr.etc Makefile rwall.c Xsa usr.etc Makefile sa.c Xsavecore usr.etc Makefile savecore.c Xshowmount usr.etc Makefile showmount.c Xspray usr.etc Makefile spray.c Xswapon usr.etc Makefile swapon.c Xtrpt usr.etc Makefile trpt.c Xtunefs usr.etc Makefile tunefs.c Xunlink usr.etc Makefile unlink.c X X# Sub-directories of "usr.lib" Xbb_count usr.lib/bb_count Xfixedwidthfonts usr.lib/fixedwidthfonts Xlibcurses usr.lib/libcurses Xlibdbm usr.lib/libdbm Xlibg usr.lib/libg Xlibkvm usr.lib/libkvm Xlibln usr.lib/libln Xliblwp usr.lib/liblwp Xlibm usr.lib/libm Xlibmp usr.lib/libmp Xlibpixrect usr.lib/libpixrect Xlibplot usr.lib/libplot Xlibresolv usr.lib/libresolv Xlibrpcsvc usr.lib/librpcsvc Xlibtermlib usr.lib/libtermlib Xliby usr.lib/liby Xme usr.lib/me Xms usr.lib/ms Xsendmail usr.lib/sendmail Xsun_tmac usr.lib/tmac Xvfont usr.lib/vfont X X# Programs that live in "usr.lib" XgetNAME usr.lib Makefile getNAME Xmakekey usr.lib Makefile makekey X X# Sub-directories of "5bin" X5diff3 5bin/diff3 X5m4 5bin/m4 X X# Sub-directories of "5bin", but use sources from other places X5cxref -a 5bin/cxref usr.bin/cxref X5sed -a 5bin/sed bin/sed X5lint -a 5bin/lint lang/pcc lang/lint X X# Programs that live in "5bin" X5banner 5bin Makefile banner.c X5cat 5bin Makefile cat.c X5du 5bin Makefile du.c X5echo 5bin Makefile echo.c X5expr 5bin Makefile expr.c X5ls 5bin Makefile ls.c X5nohup 5bin Makefile nohup.c X5od 5bin Makefile od.c X5pg 5bin Makefile pg.c X5pr 5bin Makefile pr.c X5sum 5bin Makefile sum.c X5tabs 5bin Makefile tabs.c X5time 5bin Makefile time.c X5tr 5bin Makefile tr.c X5uname 5bin Makefile uname.c X X# Programs that live in "5bin", but use sources from other places X5chmod -a 5bin/Makefile bin/chmod.c X5date -a 5bin/Makefile bin/date.c X5grep -a 5bin/Makefile bin/grep.c X5stty -a 5bin/Makefile bin/stty.c X5col -a 5bin/Makefile usr.bin/col.c X5sort -a 5bin/Makefile usr.bin/sort.c X5touch -a 5bin/Makefile usr.bin/touch.c X X# Sub-directories of "5lib" X5compile 5lib/compile X5libcurses 5lib/libcurses X5liby 5lib/liby X5terminfo 5lib/terminfo X X# Programs that live in "5lib" X# NONE X X# Programs that live in "prisma" Xcvs prisma/cvs Xmicrokernel prisma/microkernel Xtms prisma/tms X X# Programs that live in "doctools" Xdcan doctools/dcan Xditrev doctools/ditrev Xditsee doctools/ditsee Xeqn doctools/eqn Xgrap doctools/grap Xiprx doctools/iprx Xm3 doctools/m3 Xneqn doctools/neqn Xpic doctools/pic Xroff doctools/roff Xtroff doctools/roff Xtbl doctools/tbl Xtgraph doctools/tgraph Xtmac doctools/tmac END_OF_FILE if test 14454 -ne `wc -c <'examples/modules'`; then echo shar: \"'examples/modules'\" unpacked with wrong size! fi # end of 'examples/modules' fi if test -f 'src/commit.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'src/commit.c'\" else echo shar: Extracting \"'src/commit.c'\" \(21136 characters\) sed "s/^X//" >'src/commit.c' <<'END_OF_FILE' X#ifndef lint Xstatic char rcsid[] = "$Id: commit.c,v 1.28 89/11/19 23:40:32 berliner Exp $"; X#endif !lint X X/* X * Copyright (c) 1989, Brian Berliner X * X * You may distribute under the terms of the GNU General Public License X * as specified in the README file that comes with the CVS 1.0 kit. X * X * Commit Files X * X * "commit" commits the present version to the RCS repository, AFTER X * having done a test on conflicts. The call is: X * cvs commit [options] files... X * X * "commit" accepts the following options: X * -f Force a commit, even if the RCS $Id string X * is not found X * -n Causes "commit" to *not* run any commit prog X * -a Commits all files in the current directory X * that have been modified. X * -m 'message' Does not start up the editor for the X * log message; just gleans it from the X * 'message' argument. X * -r Revision Allows committing to a particular *numeric* X * revision number. X * X * Note that "commit" does not do a recursive commit. You must do X * "commit" in each directory where there are files that you'd X * like to commit. X */ X X#include X#include X#include X#include X#include "cvs.h" X Xstatic int force_commit_no_rcsid = 0; X Xextern int run_module_prog; X Xcommit(argc, argv) X int argc; X char *argv[]; X{ X int commit_all = 0, err = 0; X char *rev = ""; /* not NULL! */ X char line[MAXLINELEN], message[MAXMESGLEN]; X int c; X X if (argc == -1) X commit_usage(); X /* X * For log purposes, do not allow "root" to commit files X */ X if (geteuid() == 0) X error(0, "cannot commit files as 'root'"); X optind = 1; X while ((c = getopt(argc, argv, "fnam:r:")) != -1) { X switch (c) { X case 'f': X force_commit_no_rcsid = 1; X break; X case 'n': X run_module_prog = 0; X break; X case 'a': X commit_all = 1; X break; X case 'm': X use_editor = FALSE; X if (strlen(optarg) >= sizeof(message)) { X warn(0, "warning: message too long; truncated!"); X (void) strncpy(message, optarg, sizeof(message)); X message[sizeof(message) - 1] = '\0'; X } else X (void) strcpy(message, optarg); X break; X case 'r': X if (!isdigit(optarg[0])) X error(0, "specified revision %s must be numeric!", optarg); X rev = optarg; X break; X case '?': X default: X commit_usage(); X break; X } X } X argc -= optind; X argv += optind; X if (!commit_all && argc == 0) X error(0, "must specify the files you'd like to check-in"); X if (commit_all && argc != 0) X error(0, "cannot specify files with the -a option"); X Name_Repository(); X Writer_Lock(); X if (commit_all) { X Find_Names(&fileargc, fileargv, ALL); X argc = fileargc; X argv = fileargv; X } X if (rev[0] != '\0') { X register int i; X FILE *fptty; X X fptty = open_file("/dev/tty", "r"); X printf("WARNING:\n"); X printf("\tCommitting with a specific revision number\n"); X printf("\tbypasses all consistency checks. Are you abosulutely\n"); X printf("\tsure you want to continue (y/n) [n] ? "); X (void) fflush(stdout); X if (fgets(line, sizeof(line), fptty) == NULL || X (line[0] != 'y' && line[0] != 'Y')) { X error(0, "commit of revision %s aborted", rev); X } X (void) fclose(fptty); X /* X * When committing with a specific revision number, we simply X * fudge the lists that Collect_Sets() would have created for X * us. This is all so gross, but sometimes useful. X */ X Clist[0] = Glist[0] = Mlist[0] = Olist[0] = Dlist[0] = '\0'; X Alist[0] = Rlist[0] = Wlist[0] = Llist[0] = Blist[0] = '\0'; X for (i = 0; i < argc; i++) { X (void) strcat(Mlist, " "); X (void) strcat(Mlist, argv[i]); X } X } else { X err += Collect_Sets(argc, argv); X } X if (err == 0) { X err += commit_process_lists(message, rev); X if (err == 0 && run_module_prog) { X char *cp; X FILE *fp; X X /* X * It is not an error if Checkin.prog does not exist. X */ X if ((fp = fopen(CVSADM_CIPROG, "r")) != NULL) { X if (fgets(line, sizeof(line), fp) != NULL) { X if ((cp = rindex(line, '\n')) != NULL) X *cp = '\0'; X (void) sprintf(prog, "%s %s", line, Repository); X printf("%s %s: Executing '%s'\n", progname, command, prog); X (void) system(prog); X } X (void) fclose(fp); X } X } X Update_Logfile(Repository, message); X } X Lock_Cleanup(0); X exit(err); X} X X/* X * Process all the lists, returning the number of errors found. X */ Xstatic Xcommit_process_lists(message, rev) X char *message; X char *rev; X{ X char line[MAXLISTLEN], fname[MAXPATHLEN], revision[50]; X FILE *fp; X char *cp; X int first, err = 0; X X /* X * Doesn't make much sense to commit a directory... X */ X if (Dlist[0]) X warn(0, "committing directories ignored -%s", Dlist); X /* X * Is everything up-to-date? X * Only if Glist, Olist, and Wlist are all NULL! X */ X if (Glist[0] || Olist[0] || Wlist[0]) { X (void) fprintf(stderr, "%s: the following files are not ", progname); X (void) fprintf(stderr, X "up to date; use '%s update' first:\n", progname); X if (Glist[0] != '\0') X (void) fprintf(stderr, "\t%s\n", Glist); X if (Olist[0] != '\0') X (void) fprintf(stderr, "\t%s\n", Olist); X if (Wlist[0] != '\0') X (void) fprintf(stderr, "\t%s\n", Wlist); X Lock_Cleanup(0); X exit(1); X } X /* X * Is there anything to do in the first place? X */ X if (Mlist[0] == '\0' && Rlist[0] == '\0' && Alist[0] == '\0') X error(0, "there is nothing to commit!"); X /* X * First we make sure that the file has an RCS $Id string in it X * and if it does not, the user is prompted for verification to continue. X */ X if (force_commit_no_rcsid == 0) { X (void) strcpy(line, Mlist); X (void) strcat(line, Alist); X for (first = 1, cp = strtok(line, " \t"); cp; X cp = strtok((char *)NULL, " \t")) { X (void) sprintf(prog, "%s -s %s %s", GREP, RCSID_PAT, cp); X if (system(prog) != 0) { X if (first) { X printf("%s %s: WARNING!\n", progname, command); X printf("\tThe following file(s) do not contain an RCS $Id keyword:\n"); X first = 0; X } X printf("\t\t%s\n", cp); X } X } X if (first == 0) { X FILE *fptty = open_file("/dev/tty", "r"); X printf("\tAre you sure you want to continue (y/n) [n] ? "); X (void) fflush(stdout); X if (fgets(line, sizeof(line), fptty) == NULL || X (line[0] != 'y' && line[0] != 'Y')) { X error(0, "commit aborted"); X } X (void) fclose(fptty); X } X } X if (use_editor) X do_editor(message); X /* X * Mlist is the "modified, needs committing" list X */ X (void) strcpy(line, Mlist); X for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) { X (void) strcpy(User, cp); X (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT); X if (lock_RCS(rev) != 0) X err++; X } X /* X * Rlist is the "to be removed" list X */ X (void) strcpy(line, Rlist); X for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) { X (void) strcpy(User, cp); X (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT); X if (lock_RCS(rev) != 0) X err++; X } X /* X * Alist is the "to be added" list X */ X (void) strcpy(line, Alist); X for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) { X (void) strcpy(User, cp); X (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT); X (void) sprintf(prog, "%s/%s -i -t%s/%s%s", Rcsbin, RCS, CVSADM, X User, CVSEXT_LOG); X (void) sprintf(fname, "%s/%s%s", CVSADM, User, CVSEXT_OPT); X fp = open_file(fname, "r"); X while (fgets(fname, sizeof(fname), fp) != NULL) { X if ((cp = rindex(fname, '\n')) != NULL) X *cp = '\0'; X (void) strcat(prog, " "); X (void) strcat(prog, fname); X } X (void) fclose(fp); X (void) strcat(prog, " "); X (void) strcat(prog, Rcs); X if (system(prog) == 0) { X fix_rcs_modes(Rcs, User); X } else { X warn(0, "could not create %s", Rcs); X err++; X } X } X /* X * If something failed, release all locks and restore the default X * branches X */ X if (err) { X int didllist = 0; X char *branch; X X for (cp = strtok(Llist, " \t"); cp; cp = strtok((char *)NULL, " \t")) { X didllist = 1; X (void) strcpy(User, cp); X (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT); X (void) sprintf(prog, "%s/%s -q -u %s", Rcsbin, RCS, Rcs); X if (system(prog) != 0) X warn(0, "could not UNlock %s", Rcs); X } X if (didllist) { X for (cp=strtok(Blist, " \t"); cp; cp=strtok((char *)NULL, " \t")) { X if ((branch = rindex(cp, ':')) == NULL) X continue; X *branch++ = '\0'; X (void) strcpy(User, cp); X (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT); X (void) sprintf(prog, "%s/%s -q -b%s %s", Rcsbin, RCS, X branch, Rcs); X if (system(prog) != 0) X warn(0, "could not restore branch %s to %s", branch, Rcs); X } X } X for (cp = strtok(Alist, " \t"); cp; cp = strtok((char *)NULL, " \t")) { X (void) strcpy(User, cp); X (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT); X (void) unlink(Rcs); X } X Lock_Cleanup(0); X exit(1); X } X /* X * Got them all, now go ahead; X * First, add the files in the Alist X */ X if (Alist[0] != '\0') { X int maxrev, rev; X X /* scan the entries file looking for the max revision number */ X fp = open_file(CVSADM_ENT, "r"); X maxrev = 0; X while (fgets(line, sizeof(line), fp) != NULL) { X rev = atoi(line); X if (rev > maxrev) X maxrev = rev; X } X if (maxrev == 0) X maxrev = 1; X (void) fclose(fp); X (void) sprintf(revision, "-r%d", maxrev); X (void) strcpy(line, Alist); X for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) { X (void) strcpy(User, cp); X if (Checkin(revision, message) != 0) X err++; X (void) sprintf(fname, "%s/%s%s", CVSADM, User, CVSEXT_OPT); X (void) unlink(fname); X (void) sprintf(fname, "%s/%s%s", CVSADM, User, CVSEXT_LOG); X (void) unlink(fname); X } X } X /* X * Everyone else uses the head as it is set in the RCS file, X * or the revision that was specified on the command line. X */ X if (rev[0] != '\0') X (void) sprintf(revision, "-r%s", rev); X else X revision[0] = '\0'; X /* X * Commit the user modified files in Mlist X */ X (void) strcpy(line, Mlist); X for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) { X (void) strcpy(User, cp); X if (Checkin(revision, message) != 0) X err++; X } X /* X * And remove the RCS files in Rlist, by placing it in the Attic X */ X (void) strcpy(line, Rlist); X for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) { X int omask; X X (void) strcpy(User, cp); X (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT); X (void) sprintf(fname, "%s/%s", Repository, CVSATTIC); X omask = umask(2); X (void) mkdir(fname, 0777); X (void) umask(omask); X (void) sprintf(fname, "%s/%s/%s%s", Repository, CVSATTIC, X User, RCSEXT); X (void) sprintf(prog, "%s/%s -u -q %s", Rcsbin, RCS, Rcs); X if ((system(prog) == 0 && rename(Rcs, fname) != -1) || X (!isreadable(Rcs) && isreadable(fname))) X Scratch_Entry(User); X else X err++; X } X return (err); X} X X/* X * Attempt to place a lock on the RCS file; returns 0 if it could and X * 1 if it couldn't. If the RCS file currently has a branch as the head, X * we must move the head back to the trunk before locking the file, and X * be sure to put the branch back as the head if there are any errors. X */ Xstatic Xlock_RCS(rev) X char *rev; X{ X char branch[50]; X int err = 0; X X branch[0] = '\0'; X /* X * For a specified, numeric revision of the form "1" or "1.1", X * (or when no revision is specified ""), definitely move the X * branch to the trunk before locking the RCS file. X * X * The assumption is that if there is more than one revision X * on the trunk, the head points to the trunk, not a branch... X * and as such, it's not necessary to move the head in this case. X */ X if (numdots(rev) < 2) { X branch_number(Rcs, branch); X if (branch[0] != '\0') { X (void) sprintf(prog, "%s/%s -q -b %s", Rcsbin, RCS, Rcs); X if (system(prog) != 0) { X warn(0, "cannot change branch to default for %s", Rcs); X return (1); X } X } X (void) sprintf(prog, "%s/%s -q -l %s", Rcsbin, RCS, Rcs); X err = system(prog); X } else { X (void) sprintf(prog, "%s/%s -q -l%s %s 2>%s", X Rcsbin, RCS, rev, Rcs, DEVNULL); X (void) system(prog); X } X if (err == 0) { X (void) strcat(Llist, " "); X (void) strcat(Llist, User); X (void) strcat(Blist, " "); X (void) strcat(Blist, User); X if (branch[0] != '\0') { X (void) strcat(Blist, ":"); X (void) strcat(Blist, branch); X } X return (0); X } X if (branch[0] != '\0') { X (void) sprintf(prog, "%s/%s -q -b%s %s", Rcsbin, RCS, branch, Rcs); X if (system(prog) != 0) X warn(0, "cannot restore branch to %s for %s", branch, Rcs); X } X return (1); X} X X/* X * A special function used only by lock_RCS() to determine if the current X * head is pointed at a branch. Returns the result in "branch" as a null X * string if the trunk is the head, or as the branch number if the branch X * is the head. X */ Xstatic Xbranch_number(rcs, branch) X char *rcs; X char *branch; X{ X char line[MAXLINELEN]; X FILE *fp; X char *cp; X X branch[0] = '\0'; /* Assume trunk is head */ X fp = open_file(rcs, "r"); X if (fgets(line, sizeof(line), fp) == NULL) { X (void) fclose(fp); X return; X } X if (fgets(line, sizeof(line), fp) == NULL) { X (void) fclose(fp); X return; X } X (void) fclose(fp); X if (strncmp(line, RCSBRANCH, sizeof(RCSBRANCH) - 1) != 0 || X (cp = rindex(line, ';')) == NULL) X return; X *cp = '\0'; /* strip the ';' */ X if ((cp = rindex(line, ' ')) == NULL && X (cp = rindex(line, '\t')) == NULL) X return; X cp++; X if (*cp == NULL) X return; X (void) strcpy(branch, cp); X} X X/* X * Puts a standard header on the output which is either being prepared for X * an editor session, or being sent to a logfile program. The modified, added, X * and removed files are included (if any) and formatted to look pretty. X */ Xstatic Xsetup_tmpfile(fp, prefix) X FILE *fp; X char *prefix; X{ X if (Mlist[0] != '\0') { X (void) fprintf(fp, "%sModified Files:\n", prefix); X fmt(fp, Mlist, prefix); X } X if (Alist[0] != '\0') { X (void) fprintf(fp, "%sAdded Files:\n", prefix); X fmt(fp, Alist, prefix); X } X if (Rlist[0] != '\0') { X (void) fprintf(fp, "%sRemoved Files:\n", prefix); X fmt(fp, Rlist, prefix); X } X} X X/* X * Breaks the files list into reasonable sized lines to avoid line X * wrap... all in the name of pretty output. X */ Xstatic Xfmt(fp, instring, prefix) X FILE *fp; X char *instring; X char *prefix; X{ X char line[MAXLINELEN]; X char *cp; X int col; X X (void) strcpy(line, instring); /* since strtok() is destructive */ X (void) fprintf(fp, "%s\t", prefix); X col = 8; /* assumes that prefix is < 8 chars */ X for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) { X if ((col + strlen(cp)) > 70) { X (void) fprintf(fp, "\n%s\t", prefix); X col = 8; X } X (void) fprintf(fp, "%s ", cp); X col += strlen(cp) + 1; X } X (void) fprintf(fp, "\n%s\n", prefix); X} X X/* X * Builds a temporary file using setup_tmpfile() and invokes the user's X * editor on the file. The header garbage in the resultant file is then X * stripped and the log message is stored in the "message" argument. X */ Xstatic Xdo_editor(message) X char *message; X{ X FILE *fp; X char line[MAXLINELEN], fname[MAXPATHLEN]; X int fd; X X message[0] = '\0'; X (void) strcpy(fname, CVSTEMP); X if ((fd = mkstemp(fname)) < 0) X error(0, "cannot create temporary file %s", fname); X if ((fp = fdopen(fd, "w+")) == NULL) X error(0, "cannot create FILE * to %s", fname); X setup_tmpfile(fp, CVSEDITPREFIX); X (void) fprintf(fp, "%sEnter Log. Lines beginning with '%s' are removed automatically\n", X CVSEDITPREFIX, CVSEDITPREFIX); X (void) fprintf(fp, "%s----------------------------------------------------------------------\n"); X (void) fclose(fp); X (void) sprintf(prog, "%s %s", Editor, fname); X if (system(prog) != 0) { X (void) unlink(fname); X warn(0, "warning: editor session failed"); X } X fp = open_file(fname, "r"); X while (fgets(line, sizeof(line), fp) != NULL) { X if (strncmp(line, CVSEDITPREFIX, sizeof(CVSEDITPREFIX)-1) == 0) X continue; X if ((strlen(message) + strlen(line)) >= MAXMESGLEN) { X warn(0, "warning: log message truncated!"); X break; X } X (void) strcat(message, line); X } X (void) fclose(fp); X (void) unlink(fname); X} X X/* X * Uses setup_tmpfile() to pass the updated message on directly to X * any logfile programs that have a regular expression match for the X * checked in directory in the source repository. The log information X * is fed into the specified program as standard input. X */ XUpdate_Logfile(repository, message) X char *repository; X char *message; X{ X FILE *fp_info; X char logfile[MAXPATHLEN], title[MAXLISTLEN+MAXPATHLEN], line[MAXLINELEN]; X char path[MAXPATHLEN], default_filter[MAXLINELEN]; X char *exp, *filter, *cp, *short_repository; X int filter_run, line_number; X X if (CVSroot == NULL) { X warn(0, "CVSROOT variable not set; no log message will be sent"); X return; X } X (void) sprintf(logfile, "%s/%s", CVSroot, CVSROOTADM_LOGINFO); X if ((fp_info = fopen(logfile, "r")) == NULL) { X warn(0, "warning: cannot open %s", logfile); X return; X } X if (CVSroot != NULL) X (void) sprintf(path, "%s/", CVSroot); X else X (void) strcpy(path, REPOS_STRIP); X if (strncmp(repository, path, strlen(path)) == 0) X short_repository = repository + strlen(path); X else X short_repository = repository; X (void) sprintf(title, "'%s%s'", short_repository, Llist); X default_filter[0] = '\0'; X filter_run = line_number = 0; X while (fgets(line, sizeof(line), fp_info) != NULL) { X line_number++; X if (line[0] == '#') X continue; X for (cp = line; *cp && isspace(*cp); cp++) X ; X if (*cp == '\0') X continue; /* blank line */ X for (exp = cp; *cp && !isspace(*cp); cp++) X ; X if (*cp != '\0') X *cp++ = '\0'; X while (*cp && isspace(*cp)) X cp++; X if (*cp == '\0') { X warn(0, "syntax error at line %d file %s; ignored", X line_number, logfile); X continue; X } X filter = cp; X if ((cp = rindex(filter, '\n')) != NULL) X *cp = '\0'; /* strip the newline */ X /* X * At this point, exp points to the regular expression, and X * filter points to the program to exec. Evaluate the regular X * expression against short_repository and exec the filter X * if it matches. X */ X if (strcmp(exp, "DEFAULT") == 0) { X (void) strcpy(default_filter, filter); X continue; X } X /* X * For a regular expression of "ALL", send the log message X * to the requested filter *without* noting that a filter was run. X * This allows the "DEFAULT" regular expression to be more X * meaningful with all updates going to a master log file. X */ X if (strcmp(exp, "ALL") == 0) { X (void) logfile_write(repository, filter, title, message); X continue; X } X if ((cp = re_comp(exp)) != NULL) { X warn(0, "bad regular expression at line %d file %s: %s", X line_number, logfile, cp); X continue; X } X if (re_exec(short_repository) == 0) X continue; /* no match */ X if (logfile_write(repository, filter, title, message) == 0) X filter_run = 1; X } X if (filter_run == 0 && default_filter[0] != '\0') X (void) logfile_write(repository, default_filter, title, message); X} X X/* X * Since some systems don't define this... X */ X#ifndef MAXHOSTNAMELEN X#define MAXHOSTNAMELEN 64 X#endif !MAXHOSTNAMELEN X X/* X * Writes some stuff to the logfile "filter" and returns the status of the X * filter program. X */ Xstatic Xlogfile_write(repository, filter, title, message) X char *repository; X char *filter; X char *title; X char *message; X{ X char cwd[MAXPATHLEN], host[MAXHOSTNAMELEN]; X FILE *fp; X char *cp; X X /* X * A maximum of 6 %s arguments are supported in the filter X */ X (void) sprintf(prog, filter, title, title, title, title, title, title); X if ((fp = popen(prog, "w")) == NULL) { X warn(0, "cannot write entry to log filter: %s", prog); X return (1); X } X if (gethostname(host, sizeof(host)) < 0) X (void) strcpy(host, "(unknown)"); X (void) fprintf(fp, "Update of %s\n", repository); X (void) fprintf(fp, "In directory %s:%s\n\n", host, X (cp = getwd(cwd)) ? cp : cwd); X setup_tmpfile(fp, ""); X (void) fprintf(fp, "Log Message:\n%s\n", message); X return (pclose(fp)); X} X X/* X * Called when "add"ing files to the RCS respository, as it is necessary X * to preserve the file modes in the same fashion that RCS does. This would X * be automatic except that we are placing the RCS ,v file very far away from X * the user file, and I can't seem to convince RCS of the location of the X * user file. So we munge it here, after the ,v file has been successfully X * initialized with "rcs -i". X */ Xstatic Xfix_rcs_modes(rcs, user) X char *rcs; X char *user; X{ X struct stat sb; X X if (stat(user, &sb) != -1) { X (void) chmod(rcs, (int) sb.st_mode & ~0222); X } X} X Xstatic Xcommit_usage() X{ X (void) fprintf(stderr, X "%s %s [-fn] [-a] [-m 'message'] [-r revision] [files...]\n", X progname, command); X exit(1); X} END_OF_FILE if test 21136 -ne `wc -c <'src/commit.c'`; then echo shar: \"'src/commit.c'\" unpacked with wrong size! fi # end of 'src/commit.c' fi if test -f 'src/partime.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'src/partime.c'\" else echo shar: Extracting \"'src/partime.c'\" \(14739 characters\) sed "s/^X//" >'src/partime.c' <<'END_OF_FILE' X#ifndef lint Xstatic char rcsid[] = "$Id: partime.c,v 1.1 89/05/09 11:51:02 berliner Exp $"; X#endif X X/* X * PARTIME parse date/time string into a TM structure X * X * Usage: X * #include "time.h" -- expanded tm structure X * char *str; struct tm *tp; X * partime(str,tp); X * Returns: X * 0 if parsing failed X * else time values in specified TM structure (unspecified values X * set to TMNULL) X * Notes: X * This code is quasi-public; it may be used freely in like software. X * It is not to be sold, nor used in licensed software without X * permission of the author. X * For everyone's benefit, please report bugs and improvements! X * Copyright 1980 by Ken Harrenstien, SRI International. X * (ARPANET: KLH @ SRI) X */ X X/* Hacknotes: X * If parsing changed so that no backup needed, could perhaps modify X * to use a FILE input stream. Need terminator, though. X * Perhaps should return 0 on success, else a non-zero error val? X * Flush AMPM from TM structure and handle locally within PARTIME, X * like midnight/noon? X */ X X#include X#include X#include "rcstime.h" X X#ifndef lint Xstatic char timeid[] = TIMEID; X#endif X Xstruct tmwent { X char *went; X long wval; /* must be big enough to hold pointer or integer */ X char wflgs; X char wtype; X}; X /* wflgs */ X#define TWSPEC 01 /* Word wants special processing */ X#define TWTIME 02 /* Word is a time value (absence implies date) */ X#define TWDST 04 /* Word is a DST-type timezone */ X#define TW1200 010 /* Word is NOON or MIDNIGHT (sigh) */ X Xint pt12hack(); Xint ptnoise(); Xstruct tmwent tmwords [] = { X {"january", 0, 0, TM_MON}, X {"february", 1, 0, TM_MON}, X {"march", 2, 0, TM_MON}, X {"april", 3, 0, TM_MON}, X {"may", 4, 0, TM_MON}, X {"june", 5, 0, TM_MON}, X {"july", 6, 0, TM_MON}, X {"august", 7, 0, TM_MON}, X {"september", 8, 0, TM_MON}, X {"october", 9, 0, TM_MON}, X {"november", 10, 0, TM_MON}, X {"december", 11, 0, TM_MON}, X X {"sunday", 0, 0, TM_WDAY}, X {"monday", 1, 0, TM_WDAY}, X {"tuesday", 2, 0, TM_WDAY}, X {"wednesday", 3, 0, TM_WDAY}, X {"thursday", 4, 0, TM_WDAY}, X {"friday", 5, 0, TM_WDAY}, X {"saturday", 6, 0, TM_WDAY}, X X {"gmt", 0*60, TWTIME, TM_ZON}, /* Greenwich */ X {"gst", 0*60, TWTIME, TM_ZON}, X {"gdt", 0*60, TWTIME+TWDST, TM_ZON}, /* ?? */ X X {"ast", 4*60, TWTIME, TM_ZON}, /* Atlantic */ X {"est", 5*60, TWTIME, TM_ZON}, /* Eastern */ X {"cst", 6*60, TWTIME, TM_ZON}, /* Central */ X {"mst", 7*60, TWTIME, TM_ZON}, /* Mountain */ X {"pst", 8*60, TWTIME, TM_ZON}, /* Pacific */ X {"yst", 9*60, TWTIME, TM_ZON}, /* Yukon */ X {"hst", 10*60, TWTIME, TM_ZON}, /* Hawaii */ X {"bst", 11*60, TWTIME, TM_ZON}, /* Bering */ X X {"adt", 4*60, TWTIME+TWDST, TM_ZON}, /* Atlantic */ X {"edt", 5*60, TWTIME+TWDST, TM_ZON}, /* Eastern */ X {"cdt", 6*60, TWTIME+TWDST, TM_ZON}, /* Central */ X {"mdt", 7*60, TWTIME+TWDST, TM_ZON}, /* Mountain */ X {"pdt", 8*60, TWTIME+TWDST, TM_ZON}, /* Pacific */ X {"ydt", 9*60, TWTIME+TWDST, TM_ZON}, /* Yukon */ X {"hdt", 10*60, TWTIME+TWDST, TM_ZON}, /* Hawaii */ X {"bdt", 11*60, TWTIME+TWDST, TM_ZON}, /* Bering */ X X {"daylight", 1, TWTIME+TWDST, TM_ZON}, /* Local Daylight */ X {"standard", 1, TWTIME, TM_ZON}, /* Local Standard */ X {"std", 1, TWTIME, TM_ZON}, /* " " */ X X {"am", 1, TWTIME, TM_AMPM}, X {"pm", 2, TWTIME, TM_AMPM}, X {"noon", 12,TWTIME+TW1200, 0}, /* Special frobs */ X {"midnight", 0, TWTIME+TW1200, 0}, X {"at", (long)ptnoise, TWSPEC, 0}, /* Noise word */ X X {0, 0, 0, 0}, /* Zero entry to terminate searches */ X}; X X#define TMWILD (-2) /* Value meaning item specified as wild-card */ X /* (May use someday...) */ X Xstruct token { X char *tcp; /* pointer to string */ X int tcnt; /* # chars */ X char tbrk; /* "break" char */ X char tbrkl; /* last break char */ X char tflg; /* 0 = alpha, 1 = numeric */ X union { /* Resulting value; */ X int tnum;/* either a #, or */ X struct tmwent *ttmw;/* ptr to a tmwent. */ X } tval; X}; X Xpartime(astr, atm) Xchar *astr; Xstruct tm *atm; X{ register int *tp; X register struct tmwent *twp; X register int i; X struct token btoken, atoken; X char *cp, ch; X int ord, midnoon; X int (*aproc)(); X X tp = (int *)atm; X zaptime(tp); /* Initialize the TM structure */ X midnoon = TMNULL; /* and our own temp stuff */ X btoken.tcnt = btoken.tbrkl = 0; X btoken.tcp = astr; X Xdomore: X if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken)) /* Get a token */ X { if(btoken.tval.tnum) return(0); /* Read error? */ X if(midnoon != TMNULL) /* EOF, wrap up */ X return(pt12hack(tp, midnoon)); X return(1); /* Win return! */ X } X if(btoken.tflg == 0) /* Alpha? */ X { twp = btoken.tval.ttmw; /* Yes, get ptr to entry */ X if(twp->wflgs&TWSPEC) /* Special alpha crock */ X { aproc = (int (*) ()) (twp->wval); X if(!(*aproc)(tp, twp, &btoken)) X return(0); /* ERR: special word err */ X goto domore; X } X if(twp->wflgs&TW1200) X if(ptstash(&midnoon,(int)twp->wval)) X return(0); /* ERR: noon/midnite clash */ X else goto domore; X if(ptstash(&tp[twp->wtype],(int)twp->wval)) X return(0); /* ERR: val already set */ X if(twp->wtype == TM_ZON) /* If was zone, hack DST */ X if(ptstash(&tp[TM_ISDST],(twp->wflgs&TWDST))) X return(0); /* ERR: DST conflict */ X goto domore; X } X X /* Token is number. Lots of hairy heuristics. */ X if(btoken.tcnt >= 7) /* More than 6 digits in string? */ X return(0); /* ERR: number too big */ X if(btoken.tcnt == 6) /* 6 digits = HHMMSS. Needs special crock */ X { /* since 6 digits are too big for integer! */ X i = (btoken.tcp[0]-'0')*10 /* Gobble 1st 2 digits */ X + btoken.tcp[1]-'0'; X btoken.tcnt = 2; /* re-read last 4 chars */ X goto coltime; X } X X i = btoken.tval.tnum; /* Value now known to be valid; get it. */ X if( btoken.tcnt == 5 /* 5 digits = HMMSS */ X || btoken.tcnt == 3) /* 3 digits = HMM */ X { if(btoken.tcnt != 3) X if(ptstash(&tp[TM_SEC], i%100)) X return(0); /* ERR: sec conflict */ X else i /= 100; Xhhmm4: if(ptstash(&tp[TM_MIN], i%100)) X return(0); /* ERR: min conflict */ X i /= 100; Xhh2: if(ptstash(&tp[TM_HOUR], i)) X return(0); /* ERR: hour conflict */ X goto domore; X } X X if(btoken.tcnt == 4) /* 4 digits = YEAR or HHMM */ X { if(tp[TM_YEAR] != TMNULL) goto hhmm4; /* Already got yr? */ X if(tp[TM_HOUR] != TMNULL) goto year4; /* Already got hr? */ X if((i%100) > 59) goto year4; /* MM >= 60? */ X if(btoken.tbrk == ':') /* HHMM:SS ? */ X if( ptstash(&tp[TM_HOUR],i/100) X || ptstash(&tp[TM_MIN], i%100)) X return(0); /* ERR: hr/min clash */ X else goto coltm2; /* Go handle SS */ X if(btoken.tbrk != ',' && btoken.tbrk != '/' X && ptitoken(btoken.tcp+btoken.tcnt,&atoken) /* Peek */ X && atoken.tflg == 0 /* alpha */ X && (atoken.tval.ttmw->wflgs&TWTIME)) /* HHMM-ZON */ X goto hhmm4; X if(btoken.tbrkl == '-' /* DD-Mon-YYYY */ X || btoken.tbrkl == ',' /* Mon DD, YYYY */ X || btoken.tbrkl == '/' /* MM/DD/YYYY */ X || btoken.tbrkl == '.' /* DD.MM.YYYY */ X || btoken.tbrk == '-' /* YYYY-MM-DD */ X ) goto year4; X goto hhmm4; /* Give up, assume HHMM. */ X } X X /* From this point on, assume tcnt == 1 or 2 */ X /* 2 digits = YY, MM, DD, or HH (MM and SS caught at coltime) */ X if(btoken.tbrk == ':') /* HH:MM[:SS] */ X goto coltime; /* must be part of time. */ X if(i > 31) goto yy2; /* If >= 32, only YY poss. */ X X /* Check for numerical-format date */ X for (cp = "/-."; ch = *cp++;) X { ord = (ch == '.' ? 0 : 1); /* n/m = D/M or M/D */ X if(btoken.tbrk == ch) /* "NN-" */ X { if(btoken.tbrkl != ch) X { if(ptitoken(btoken.tcp+btoken.tcnt,&atoken) X && atoken.tflg == 0 X && atoken.tval.ttmw->wtype == TM_MON) X goto dd2; X if(ord)goto mm2; else goto dd2; /* "NN-" */ X } /* "-NN-" */ X if(tp[TM_DAY] == TMNULL X && tp[TM_YEAR] != TMNULL) /* If "YY-NN-" */ X goto mm2; /* then always MM */ X if(ord)goto dd2; else goto mm2; X } X if(btoken.tbrkl == ch /* "-NN" */ X && tp[ord ? TM_MON : TM_DAY] != TMNULL) X if(tp[ord ? TM_DAY : TM_MON] == TMNULL) /* MM/DD */ X if(ord)goto dd2; else goto mm2; X else goto yy2; /* "-YY" */ X } X X /* At this point only YY, DD, and HH are left. X * YY is very unlikely since value is <= 32 and there was X * no numerical format date. Make one last try at YY X * before dropping through to DD vs HH code. X */ X if(btoken.tcnt == 2 /* If 2 digits */ X && tp[TM_HOUR] != TMNULL /* and already have hour */ X && tp[TM_DAY] != TMNULL /* and day, but */ X && tp[TM_YEAR] == TMNULL) /* no year, then assume */ X goto yy2; /* that's what we have. */ X X /* Now reduced to choice between HH and DD */ X if(tp[TM_HOUR] != TMNULL) goto dd2; /* Have hour? Assume day. */ X if(tp[TM_DAY] != TMNULL) goto hh2; /* Have day? Assume hour. */ X if(i > 24) goto dd2; /* Impossible HH means DD */ X if(!ptitoken(btoken.tcp+btoken.tcnt, &atoken)) /* Read ahead! */ X if(atoken.tval.tnum) return(0); /* ERR: bad token */ X else goto dd2; /* EOF, assume day. */ X if( atoken.tflg == 0 /* If next token is an alpha */ X && atoken.tval.ttmw->wflgs&TWTIME) /* time-spec, assume hour */ X goto hh2; /* e.g. "3 PM", "11-EDT" */ X Xdd2: if(ptstash(&tp[TM_DAY],i)) /* Store day (1 based) */ X return(0); X goto domore; X Xmm2: if(ptstash(&tp[TM_MON], i-1)) /* Store month (make zero based) */ X return(0); X goto domore; X Xyy2: i += 1900; Xyear4: if(ptstash(&tp[TM_YEAR],i)) /* Store year (full number) */ X return(0); /* ERR: year conflict */ X goto domore; X X /* Hack HH:MM[[:]SS] */ Xcoltime: X if(ptstash(&tp[TM_HOUR],i)) return(0); X if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken)) X return(!btoken.tval.tnum); X if(!btoken.tflg) return(0); /* ERR: HH: */ X if(btoken.tcnt == 4) /* MMSS */ X if(ptstash(&tp[TM_MIN],btoken.tval.tnum/100) X || ptstash(&tp[TM_SEC],btoken.tval.tnum%100)) X return(0); X else goto domore; X if(btoken.tcnt != 2 X || ptstash(&tp[TM_MIN],btoken.tval.tnum)) X return(0); /* ERR: MM bad */ X if(btoken.tbrk != ':') goto domore; /* Seconds follow? */ Xcoltm2: if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken)) X return(!btoken.tval.tnum); X if(!btoken.tflg || btoken.tcnt != 2 /* Verify SS */ X || ptstash(&tp[TM_SEC], btoken.tval.tnum)) X return(0); /* ERR: SS bad */ X goto domore; X} X X/* Store date/time value, return 0 if successful. X * Fails if entry already set to a different value. X */ Xptstash(adr,val) Xint *adr; X{ register int *a; X if( *(a=adr) != TMNULL) X return(*a != val); X *a = val; X return(0); X} X X/* This subroutine is invoked for NOON or MIDNIGHT when wrapping up X * just prior to returning from partime. X */ Xpt12hack(atp, aval) Xint *atp, aval; X{ register int *tp, i, h; X tp = atp; X if (((i=tp[TM_MIN]) && i != TMNULL) /* Ensure mins, secs */ X || ((i=tp[TM_SEC]) && i != TMNULL)) /* are 0 or unspec'd */ X return(0); /* ERR: MM:SS not 00:00 */ X i = aval; /* Get 0 or 12 (midnite or noon) */ X if ((h = tp[TM_HOUR]) == TMNULL /* If hour unspec'd, win */ X || h == 12) /* or if 12:00 (matches either) */ X tp[TM_HOUR] = i; /* Then set time */ X else if(!(i == 0 /* Nope, but if midnight and */ X &&(h == 0 || h == 24))) /* time matches, can pass. */ X return(0); /* ERR: HH conflicts */ X tp[TM_AMPM] = TMNULL; /* Always reset this value if won */ X return(1); X} X X/* Null routine for no-op tokens */ X Xptnoise() { return(1); } X X/* Get a token and identify it to some degree. X * Returns 0 on failure; token.tval will be 0 for normal EOF, otherwise X * hit error of some sort X */ X Xptitoken(astr, tkp) Xregister struct token *tkp; Xchar *astr; X{ X register char *cp; X register int i; X X tkp->tval.tnum = 0; X if(pttoken(astr,tkp) == 0) X#ifdef DEBUG X VOID printf("EOF\n"); X#endif DEBUG X return(0); X cp = tkp->tcp; X X#ifdef DEBUG X i = cp[tkp->tcnt]; X cp[tkp->tcnt] = 0; X VOID printf("Token: \"%s\" ",cp); X cp[tkp->tcnt] = i; X#endif DEBUG X X if(tkp->tflg) X for(i = tkp->tcnt; i > 0; i--) X tkp->tval.tnum = (int)tkp->tval.tnum*10 + ((*cp++)-'0'); X else X { i = ptmatchstr(cp, tkp->tcnt, tmwords); X tkp->tval.tnum = i ? i : -1; /* Set -1 for error */ X X#ifdef DEBUG X if(!i) VOID printf("Not found!\n"); X#endif DEBUG X X if(!i) return(0); X } X X#ifdef DEBUG X if(tkp->tflg) X VOID printf("Val: %d.\n",tkp->tval.tnum); X else VOID printf("Found: \"%s\", val: %d., type %d\n", X tkp->tval.ttmw->went,tkp->tval.ttmw->wval,tkp->tval.ttmw->wtype); X#endif DEBUG X X return(1); X} X X/* Read token from input string into token structure */ Xpttoken(astr,tkp) Xregister struct token *tkp; Xchar *astr; X{ X register char *cp; X register int c; X X tkp->tcp = cp = astr; X tkp->tbrkl = tkp->tbrk; /* Set "last break" */ X tkp->tcnt = tkp->tbrk = tkp->tflg = 0; X X while(c = *cp++) X { switch(c) X { case ' ': case '\t': /* Flush all whitespace */ X while((c = *cp++) && isspace(c)); X cp--; /* Drop thru to handle brk */ X case '(': case ')': /* Perhaps any non-alphanum */ X case '-': case ',': /* shd qualify as break? */ X case '/': case ':': case '.': /* Break chars */ X if(tkp->tcnt == 0) /* If no token yet */ X { tkp->tcp = cp; /* ignore the brk */ X tkp->tbrkl = c; X continue; /* and go on. */ X } X tkp->tbrk = c; X return(tkp->tcnt); X } X if(tkp->tcnt == 0) /* If first char of token, */ X tkp->tflg = isdigit(c); /* determine type */ X if(( isdigit(c) && tkp->tflg) /* If not first, make sure */ X ||(!isdigit(c) && !tkp->tflg)) /* char matches type */ X tkp->tcnt++; /* Win, add to token. */ X else { X cp--; /* Wrong type, back up */ X tkp->tbrk = c; X return(tkp->tcnt); X } X } X return(tkp->tcnt); /* When hit EOF */ X} X X Xptmatchstr(astr,cnt,astruc) Xchar *astr; Xint cnt; Xstruct tmwent *astruc; X{ register char *cp, *mp; X register int c; X struct tmwent *lastptr; X struct integ { int word; }; /* For getting at array ptr */ X int i; X X lastptr = 0; X for(;mp = (char *)((struct integ *)astruc)->word; astruc += 1) X { cp = astr; X for(i = cnt; i > 0; i--) X { switch((c = *cp++) ^ *mp++) /* XOR the chars */ X { case 0: continue; /* Exact match */ X case 040: if(isalpha(c)) X continue; X } X break; X } X if(i==0) X if(*mp == 0) return((unsigned int)astruc); /* Exact match */ X else if(lastptr) return(0); /* Ambiguous */ X else lastptr = astruc; /* 1st ambig */ X } X return((unsigned int)lastptr); X} X X X Xzaptime(tp) Xregister int *tp; X/* clears tm structure pointed to by tp */ X{ register int i; X i = (sizeof (struct tm))/(sizeof (int)); X do *tp++ = TMNULL; /* Set entry to "unspecified" */ X while(--i); /* Faster than FOR */ X} END_OF_FILE if test 14739 -ne `wc -c <'src/partime.c'`; then echo shar: \"'src/partime.c'\" unpacked with wrong size! fi # end of 'src/partime.c' fi echo shar: End of archive 5 \(of 7\). cp /dev/null ark5isdone MISSING="" for I in 1 2 3 4 5 6 7 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 7 archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 exit 0 # Just in case...