v22i019: Brian Berliner's concurrent RCS system, Part05/07
Rich Salz
rsalz at uunet.uu.net
Sat May 5 01:15:36 AEST 1990
Submitted-by: Brian Berliner <berliner at prisma.com>
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 <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of archive 5 (of 7)."
# Contents: examples/modules src/commit.c src/partime.c
# Wrapped by rsalz at litchi.bbn.com on Thu May 3 16:59:05 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'examples/modules' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'examples/modules'\"
else
echo shar: Extracting \"'examples/modules'\" \(14454 characters\)
sed "s/^X//" >'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 <sys/param.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <ctype.h>
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 <stdio.h>
X#include <ctype.h>
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:<alpha> */
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...
--
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.
More information about the Comp.sources.unix
mailing list