summaryrefslogtreecommitdiffstats
path: root/gnu/usr.bin/rcs/rcs/rcs.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/usr.bin/rcs/rcs/rcs.c')
-rw-r--r--gnu/usr.bin/rcs/rcs/rcs.c1629
1 files changed, 1629 insertions, 0 deletions
diff --git a/gnu/usr.bin/rcs/rcs/rcs.c b/gnu/usr.bin/rcs/rcs/rcs.c
new file mode 100644
index 0000000..dfb622a
--- /dev/null
+++ b/gnu/usr.bin/rcs/rcs/rcs.c
@@ -0,0 +1,1629 @@
+/* Change RCS file attributes. */
+
+/* Copyright 1982, 1988, 1989 Walter Tichy
+ Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
+ Distributed under license by the Free Software Foundation, Inc.
+
+This file is part of RCS.
+
+RCS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+RCS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with RCS; see the file COPYING.
+If not, write to the Free Software Foundation,
+59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+Report problems and direct all questions to:
+
+ rcs-bugs@cs.purdue.edu
+
+*/
+
+/*
+ * Revision 5.21 1995/06/16 06:19:24 eggert
+ * Update FSF address.
+ *
+ * Revision 5.20 1995/06/01 16:23:43 eggert
+ * (main): Warn if no options were given. Punctuate messages properly.
+ *
+ * (sendmail): Rewind mailmess before flushing it.
+ * Output another warning if mail should work but fails.
+ *
+ * (buildeltatext): Pass "--binary" if -kb and if --binary makes a difference.
+ *
+ * Revision 5.19 1994/03/17 14:05:48 eggert
+ * Use ORCSerror to clean up after a fatal error. Remove lint.
+ * Specify subprocess input via file descriptor, not file name. Remove lint.
+ * Flush stderr after prompt.
+ *
+ * Revision 5.18 1993/11/09 17:40:15 eggert
+ * -V now prints version on stdout and exits. Don't print usage twice.
+ *
+ * Revision 5.17 1993/11/03 17:42:27 eggert
+ * Add -z. Don't lose track of -m or -t when there are no other changes.
+ * Don't discard ignored phrases. Improve quality of diagnostics.
+ *
+ * Revision 5.16 1992/07/28 16:12:44 eggert
+ * rcs -l now asks whether you want to break the lock.
+ * Add -V. Set RCS file's mode and time at right moment.
+ *
+ * Revision 5.15 1992/02/17 23:02:20 eggert
+ * Add -T.
+ *
+ * Revision 5.14 1992/01/27 16:42:53 eggert
+ * Add -M. Avoid invoking umask(); it's one less thing to configure.
+ * Add support for bad_creat0. lint -> RCS_lint
+ *
+ * Revision 5.13 1992/01/06 02:42:34 eggert
+ * Avoid changing RCS file in common cases where no change can occur.
+ *
+ * Revision 5.12 1991/11/20 17:58:08 eggert
+ * Don't read the delta tree from a nonexistent RCS file.
+ *
+ * Revision 5.11 1991/10/07 17:32:46 eggert
+ * Remove lint.
+ *
+ * Revision 5.10 1991/08/19 23:17:54 eggert
+ * Add -m, -r$, piece tables. Revision separator is `:', not `-'. Tune.
+ *
+ * Revision 5.9 1991/04/21 11:58:18 eggert
+ * Add -x, RCSINIT, MS-DOS support.
+ *
+ * Revision 5.8 1991/02/25 07:12:38 eggert
+ * strsave -> str_save (DG/UX name clash)
+ * 0444 -> S_IRUSR|S_IRGRP|S_IROTH for portability
+ *
+ * Revision 5.7 1990/12/18 17:19:21 eggert
+ * Fix bug with multiple -n and -N options.
+ *
+ * Revision 5.6 1990/12/04 05:18:40 eggert
+ * Use -I for prompts and -q for diagnostics.
+ *
+ * Revision 5.5 1990/11/11 00:06:35 eggert
+ * Fix `rcs -e' core dump.
+ *
+ * Revision 5.4 1990/11/01 05:03:33 eggert
+ * Add -I and new -t behavior. Permit arbitrary data in logs.
+ *
+ * Revision 5.3 1990/10/04 06:30:16 eggert
+ * Accumulate exit status across files.
+ *
+ * Revision 5.2 1990/09/04 08:02:17 eggert
+ * Standardize yes-or-no procedure.
+ *
+ * Revision 5.1 1990/08/29 07:13:51 eggert
+ * Remove unused setuid support. Clean old log messages too.
+ *
+ * Revision 5.0 1990/08/22 08:12:42 eggert
+ * Don't lose names when applying -a option to multiple files.
+ * Remove compile-time limits; use malloc instead. Add setuid support.
+ * Permit dates past 1999/12/31. Make lock and temp files faster and safer.
+ * Ansify and Posixate. Add -V. Fix umask bug. Make linting easier. Tune.
+ * Yield proper exit status. Check diff's output.
+ *
+ * Revision 4.11 89/05/01 15:12:06 narten
+ * changed copyright header to reflect current distribution rules
+ *
+ * Revision 4.10 88/11/08 16:01:54 narten
+ * didn't install previous patch correctly
+ *
+ * Revision 4.9 88/11/08 13:56:01 narten
+ * removed include <sysexits.h> (not needed)
+ * minor fix for -A option
+ *
+ * Revision 4.8 88/08/09 19:12:27 eggert
+ * Don't access freed storage.
+ * Use execv(), not system(); yield proper exit status; remove lint.
+ *
+ * Revision 4.7 87/12/18 11:37:17 narten
+ * lint cleanups (Guy Harris)
+ *
+ * Revision 4.6 87/10/18 10:28:48 narten
+ * Updating verison numbers. Changes relative to 1.1 are actually
+ * relative to 4.3
+ *
+ * Revision 1.4 87/09/24 13:58:52 narten
+ * Sources now pass through lint (if you ignore printf/sprintf/fprintf
+ * warnings)
+ *
+ * Revision 1.3 87/03/27 14:21:55 jenkins
+ * Port to suns
+ *
+ * Revision 1.2 85/12/17 13:59:09 albitz
+ * Changed setstate to rcs_setstate because of conflict with random.o.
+ *
+ * Revision 4.3 83/12/15 12:27:33 wft
+ * rcs -u now breaks most recent lock if it can't find a lock by the caller.
+ *
+ * Revision 4.2 83/12/05 10:18:20 wft
+ * Added conditional compilation for sending mail.
+ * Alternatives: V4_2BSD, V6, USG, and other.
+ *
+ * Revision 4.1 83/05/10 16:43:02 wft
+ * Simplified breaklock(); added calls to findlock() and getcaller().
+ * Added option -b (default branch). Updated -s and -w for -b.
+ * Removed calls to stat(); now done by pairfilenames().
+ * Replaced most catchints() calls with restoreints().
+ * Removed check for exit status of delivermail().
+ * Directed all interactive output to stderr.
+ *
+ * Revision 3.9.1.1 83/12/02 22:08:51 wft
+ * Added conditional compilation for 4.2 sendmail and 4.1 delivermail.
+ *
+ * Revision 3.9 83/02/15 15:38:39 wft
+ * Added call to fastcopy() to copy remainder of RCS file.
+ *
+ * Revision 3.8 83/01/18 17:37:51 wft
+ * Changed sendmail(): now uses delivermail, and asks whether to break the lock.
+ *
+ * Revision 3.7 83/01/15 18:04:25 wft
+ * Removed putree(); replaced with puttree() in rcssyn.c.
+ * Combined putdellog() and scanlogtext(); deleted putdellog().
+ * Cleaned up diagnostics and error messages. Fixed problem with
+ * mutilated files in case of deletions in 2 files in a single command.
+ * Changed marking of selector from 'D' to DELETE.
+ *
+ * Revision 3.6 83/01/14 15:37:31 wft
+ * Added ignoring of interrupts while new RCS file is renamed;
+ * Avoids deletion of RCS files by interrupts.
+ *
+ * Revision 3.5 82/12/10 21:11:39 wft
+ * Removed unused variables, fixed checking of return code from diff,
+ * introduced variant COMPAT2 for skipping Suffix on -A files.
+ *
+ * Revision 3.4 82/12/04 13:18:20 wft
+ * Replaced getdelta() with gettree(), changed breaklock to update
+ * field lockedby, added some diagnostics.
+ *
+ * Revision 3.3 82/12/03 17:08:04 wft
+ * Replaced getlogin() with getpwuid(), flcose() with ffclose(),
+ * /usr/ucb/Mail with macro MAIL. Removed handling of Suffix (-x).
+ * fixed -u for missing revno. Disambiguated structure members.
+ *
+ * Revision 3.2 82/10/18 21:05:07 wft
+ * rcs -i now generates a file mode given by the umask minus write permission;
+ * otherwise, rcs keeps the mode, but removes write permission.
+ * I added a check for write error, fixed call to getlogin(), replaced
+ * curdir() with getfullRCSname(), cleaned up handling -U/L, and changed
+ * conflicting, long identifiers.
+ *
+ * Revision 3.1 82/10/13 16:11:07 wft
+ * fixed type of variables receiving from getc() (char -> int).
+ */
+
+
+#include "rcsbase.h"
+
+struct Lockrev {
+ char const *revno;
+ struct Lockrev * nextrev;
+};
+
+struct Symrev {
+ char const *revno;
+ char const *ssymbol;
+ int override;
+ struct Symrev * nextsym;
+};
+
+struct Message {
+ char const *revno;
+ struct cbuf message;
+ struct Message *nextmessage;
+};
+
+struct Status {
+ char const *revno;
+ char const *status;
+ struct Status * nextstatus;
+};
+
+enum changeaccess {append, erase};
+struct chaccess {
+ char const *login;
+ enum changeaccess command;
+ struct chaccess *nextchaccess;
+};
+
+struct delrevpair {
+ char const *strt;
+ char const *end;
+ int code;
+};
+
+static int branchpoint P((struct hshentry*,struct hshentry*));
+static int breaklock P((struct hshentry const*));
+static int buildeltatext P((struct hshentries const*));
+static int doaccess P((void));
+static int doassoc P((void));
+static int dolocks P((void));
+static int domessages P((void));
+static int rcs_setstate P((char const*,char const*));
+static int removerevs P((void));
+static int sendmail P((char const*,char const*));
+static int setlock P((char const*));
+static struct Lockrev **rmnewlocklst P((char const*));
+static struct hshentry *searchcutpt P((char const*,int,struct hshentries*));
+static void buildtree P((void));
+static void cleanup P((void));
+static void getaccessor P((char*,enum changeaccess));
+static void getassoclst P((int,char*));
+static void getchaccess P((char const*,enum changeaccess));
+static void getdelrev P((char*));
+static void getmessage P((char*));
+static void getstates P((char*));
+static void scanlogtext P((struct hshentry*,int));
+
+static struct buf numrev;
+static char const *headstate;
+static int chgheadstate, exitstatus, lockhead, unlockcaller;
+static int suppress_mail;
+static struct Lockrev *newlocklst, *rmvlocklst;
+static struct Message *messagelst, **nextmessage;
+static struct Status *statelst, **nextstate;
+static struct Symrev *assoclst, **nextassoc;
+static struct chaccess *chaccess, **nextchaccess;
+static struct delrevpair delrev;
+static struct hshentry *cuthead, *cuttail, *delstrt;
+static struct hshentries *gendeltas;
+
+mainProg(rcsId, "rcs", "$FreeBSD$")
+{
+ static char const cmdusage[] =
+ "\nrcs usage: rcs -{ae}logins -Afile -{blu}[rev] -cstring -{iILqTU} -ksubst -mrev:msg -{nN}name[:[rev]] -orange -sstate[:rev] -t[text] -Vn -xsuff -zzone file ...";
+
+ char *a, **newargv, *textfile;
+ char const *branchsym, *commsyml;
+ int branchflag, changed, expmode, initflag;
+ int strictlock, strict_selected, textflag;
+ int keepRCStime, Ttimeflag;
+ size_t commsymlen;
+ struct buf branchnum;
+ struct Lockrev *lockpt;
+ struct Lockrev **curlock, **rmvlock;
+ struct Status * curstate;
+
+ nosetid();
+
+ nextassoc = &assoclst;
+ nextchaccess = &chaccess;
+ nextmessage = &messagelst;
+ nextstate = &statelst;
+ branchsym = commsyml = textfile = 0;
+ branchflag = strictlock = false;
+ bufautobegin(&branchnum);
+ commsymlen = 0;
+ curlock = &newlocklst;
+ rmvlock = &rmvlocklst;
+ expmode = -1;
+ suffixes = X_DEFAULT;
+ initflag= textflag = false;
+ strict_selected = 0;
+ Ttimeflag = false;
+
+ /* preprocessing command options */
+ if (1 < argc && argv[1][0] != '-')
+ warn("No options were given; this usage is obsolescent.");
+
+ argc = getRCSINIT(argc, argv, &newargv);
+ argv = newargv;
+ while (a = *++argv, 0<--argc && *a++=='-') {
+ switch (*a++) {
+
+ case 'i': /* initial version */
+ initflag = true;
+ break;
+
+ case 'b': /* change default branch */
+ if (branchflag) redefined('b');
+ branchflag= true;
+ branchsym = a;
+ break;
+
+ case 'c': /* change comment symbol */
+ if (commsyml) redefined('c');
+ commsyml = a;
+ commsymlen = strlen(a);
+ break;
+
+ case 'a': /* add new accessor */
+ getaccessor(*argv+1, append);
+ break;
+
+ case 'A': /* append access list according to accessfile */
+ if (!*a) {
+ error("missing pathname after -A");
+ break;
+ }
+ *argv = a;
+ if (0 < pairnames(1,argv,rcsreadopen,true,false)) {
+ while (AccessList) {
+ getchaccess(str_save(AccessList->login),append);
+ AccessList = AccessList->nextaccess;
+ }
+ Izclose(&finptr);
+ }
+ break;
+
+ case 'e': /* remove accessors */
+ getaccessor(*argv+1, erase);
+ break;
+
+ case 'l': /* lock a revision if it is unlocked */
+ if (!*a) {
+ /* Lock head or default branch. */
+ lockhead = true;
+ break;
+ }
+ *curlock = lockpt = talloc(struct Lockrev);
+ lockpt->revno = a;
+ lockpt->nextrev = 0;
+ curlock = &lockpt->nextrev;
+ break;
+
+ case 'u': /* release lock of a locked revision */
+ if (!*a) {
+ unlockcaller=true;
+ break;
+ }
+ *rmvlock = lockpt = talloc(struct Lockrev);
+ lockpt->revno = a;
+ lockpt->nextrev = 0;
+ rmvlock = &lockpt->nextrev;
+ curlock = rmnewlocklst(lockpt->revno);
+ break;
+
+ case 'L': /* set strict locking */
+ if (strict_selected) {
+ if (!strictlock) /* Already selected -U? */
+ warn("-U overridden by -L");
+ }
+ strictlock = true;
+ strict_selected = true;
+ break;
+
+ case 'U': /* release strict locking */
+ if (strict_selected) {
+ if (strictlock) /* Already selected -L? */
+ warn("-L overridden by -U");
+ }
+ strict_selected = true;
+ break;
+
+ case 'n': /* add new association: error, if name exists */
+ if (!*a) {
+ error("missing symbolic name after -n");
+ break;
+ }
+ getassoclst(false, (*argv)+1);
+ break;
+
+ case 'N': /* add or change association */
+ if (!*a) {
+ error("missing symbolic name after -N");
+ break;
+ }
+ getassoclst(true, (*argv)+1);
+ break;
+
+ case 'm': /* change log message */
+ getmessage(a);
+ break;
+
+ case 'M': /* do not send mail */
+ suppress_mail = true;
+ break;
+
+ case 'o': /* delete revisions */
+ if (delrev.strt) redefined('o');
+ if (!*a) {
+ error("missing revision range after -o");
+ break;
+ }
+ getdelrev( (*argv)+1 );
+ break;
+
+ case 's': /* change state attribute of a revision */
+ if (!*a) {
+ error("state missing after -s");
+ break;
+ }
+ getstates( (*argv)+1);
+ break;
+
+ case 't': /* change descriptive text */
+ textflag=true;
+ if (*a) {
+ if (textfile) redefined('t');
+ textfile = a;
+ }
+ break;
+
+ case 'T': /* do not update last-mod time for minor changes */
+ if (*a)
+ goto unknown;
+ Ttimeflag = true;
+ break;
+
+ case 'I':
+ interactiveflag = true;
+ break;
+
+ case 'q':
+ quietflag = true;
+ break;
+
+ case 'x':
+ suffixes = a;
+ break;
+
+ case 'V':
+ setRCSversion(*argv);
+ break;
+
+ case 'z':
+ zone_set(a);
+ break;
+
+ case 'k': /* set keyword expand mode */
+ if (0 <= expmode) redefined('k');
+ if (0 <= (expmode = str2expmode(a)))
+ break;
+ /* fall into */
+ default:
+ unknown:
+ error("unknown option: %s%s", *argv, cmdusage);
+ };
+ } /* end processing of options */
+
+ /* Now handle all pathnames. */
+ if (nerror) cleanup();
+ else if (argc < 1) faterror("no input file%s", cmdusage);
+ else for (; 0 < argc; cleanup(), ++argv, --argc) {
+
+ ffree();
+
+ if ( initflag ) {
+ switch (pairnames(argc, argv, rcswriteopen, false, false)) {
+ case -1: break; /* not exist; ok */
+ case 0: continue; /* error */
+ case 1: rcserror("already exists");
+ continue;
+ }
+ }
+ else {
+ switch (pairnames(argc, argv, rcswriteopen, true, false)) {
+ case -1: continue; /* not exist */
+ case 0: continue; /* errors */
+ case 1: break; /* file exists; ok*/
+ }
+ }
+
+
+ /*
+ * RCSname contains the name of the RCS file, and
+ * workname contains the name of the working file.
+ * if !initflag, finptr contains the file descriptor for the
+ * RCS file. The admin node is initialized.
+ */
+
+ diagnose("RCS file: %s\n", RCSname);
+
+ changed = initflag | textflag;
+ keepRCStime = Ttimeflag;
+ if (!initflag) {
+ if (!checkaccesslist()) continue;
+ gettree(); /* Read the delta tree. */
+ }
+
+ /* update admin. node */
+ if (strict_selected) {
+ changed |= StrictLocks ^ strictlock;
+ StrictLocks = strictlock;
+ }
+ if (
+ commsyml &&
+ (
+ commsymlen != Comment.size ||
+ memcmp(commsyml, Comment.string, commsymlen) != 0
+ )
+ ) {
+ Comment.string = commsyml;
+ Comment.size = strlen(commsyml);
+ changed = true;
+ }
+ if (0 <= expmode && Expand != expmode) {
+ Expand = expmode;
+ changed = true;
+ }
+
+ /* update default branch */
+ if (branchflag && expandsym(branchsym, &branchnum)) {
+ if (countnumflds(branchnum.string)) {
+ if (cmpnum(Dbranch, branchnum.string) != 0) {
+ Dbranch = branchnum.string;
+ changed = true;
+ }
+ } else
+ if (Dbranch) {
+ Dbranch = 0;
+ changed = true;
+ }
+ }
+
+ changed |= doaccess(); /* Update access list. */
+
+ changed |= doassoc(); /* Update association list. */
+
+ changed |= dolocks(); /* Update locks. */
+
+ changed |= domessages(); /* Update log messages. */
+
+ /* update state attribution */
+ if (chgheadstate) {
+ /* change state of default branch or head */
+ if (!Dbranch) {
+ if (!Head)
+ rcswarn("can't change states in an empty tree");
+ else if (strcmp(Head->state, headstate) != 0) {
+ Head->state = headstate;
+ changed = true;
+ }
+ } else
+ changed |= rcs_setstate(Dbranch,headstate);
+ }
+ for (curstate = statelst; curstate; curstate = curstate->nextstatus)
+ changed |= rcs_setstate(curstate->revno,curstate->status);
+
+ cuthead = cuttail = 0;
+ if (delrev.strt && removerevs()) {
+ /* rebuild delta tree if some deltas are deleted */
+ if ( cuttail )
+ VOID genrevs(
+ cuttail->num, (char *)0, (char *)0, (char *)0,
+ &gendeltas
+ );
+ buildtree();
+ changed = true;
+ keepRCStime = false;
+ }
+
+ if (nerror)
+ continue;
+
+ putadmin();
+ if ( Head )
+ puttree(Head, frewrite);
+ putdesc(textflag,textfile);
+
+ if ( Head) {
+ if (delrev.strt || messagelst) {
+ if (!cuttail || buildeltatext(gendeltas)) {
+ advise_access(finptr, MADV_SEQUENTIAL);
+ scanlogtext((struct hshentry *)0, false);
+ /* copy rest of delta text nodes that are not deleted */
+ changed = true;
+ }
+ }
+ }
+
+ if (initflag) {
+ /* Adjust things for donerewrite's sake. */
+ if (stat(workname, &RCSstat) != 0) {
+# if bad_creat0
+ mode_t m = umask(0);
+ (void) umask(m);
+ RCSstat.st_mode = (S_IRUSR|S_IRGRP|S_IROTH) & ~m;
+# else
+ changed = -1;
+# endif
+ }
+ RCSstat.st_nlink = 0;
+ keepRCStime = false;
+ }
+ if (donerewrite(changed,
+ keepRCStime ? RCSstat.st_mtime : (time_t)-1
+ ) != 0)
+ break;
+
+ diagnose("done\n");
+ }
+
+ tempunlink();
+ exitmain(exitstatus);
+} /* end of main (rcs) */
+
+ static void
+cleanup()
+{
+ if (nerror) exitstatus = EXIT_FAILURE;
+ Izclose(&finptr);
+ Ozclose(&fcopy);
+ ORCSclose();
+ dirtempunlink();
+}
+
+ void
+exiterr()
+{
+ ORCSerror();
+ dirtempunlink();
+ tempunlink();
+ _exit(EXIT_FAILURE);
+}
+
+
+ static void
+getassoclst(flag, sp)
+int flag;
+char * sp;
+/* Function: associate a symbolic name to a revision or branch, */
+/* and store in assoclst */
+
+{
+ struct Symrev * pt;
+ char const *temp;
+ int c;
+
+ while ((c = *++sp) == ' ' || c == '\t' || c =='\n')
+ continue;
+ temp = sp;
+ sp = checksym(sp, ':'); /* check for invalid symbolic name */
+ c = *sp; *sp = '\0';
+ while( c == ' ' || c == '\t' || c == '\n') c = *++sp;
+
+ if ( c != ':' && c != '\0') {
+ error("invalid string %s after option -n or -N",sp);
+ return;
+ }
+
+ pt = talloc(struct Symrev);
+ pt->ssymbol = temp;
+ pt->override = flag;
+ if (c == '\0') /* delete symbol */
+ pt->revno = 0;
+ else {
+ while ((c = *++sp) == ' ' || c == '\n' || c == '\t')
+ continue;
+ pt->revno = sp;
+ }
+ pt->nextsym = 0;
+ *nextassoc = pt;
+ nextassoc = &pt->nextsym;
+}
+
+
+ static void
+getchaccess(login, command)
+ char const *login;
+ enum changeaccess command;
+{
+ register struct chaccess *pt;
+
+ pt = talloc(struct chaccess);
+ pt->login = login;
+ pt->command = command;
+ pt->nextchaccess = 0;
+ *nextchaccess = pt;
+ nextchaccess = &pt->nextchaccess;
+}
+
+
+
+ static void
+getaccessor(opt, command)
+ char *opt;
+ enum changeaccess command;
+/* Function: get the accessor list of options -e and -a, */
+/* and store in chaccess */
+
+
+{
+ register c;
+ register char *sp;
+
+ sp = opt;
+ while ((c = *++sp) == ' ' || c == '\n' || c == '\t' || c == ',')
+ continue;
+ if ( c == '\0') {
+ if (command == erase && sp-opt == 1) {
+ getchaccess((char*)0, command);
+ return;
+ }
+ error("missing login name after option -a or -e");
+ return;
+ }
+
+ while( c != '\0') {
+ getchaccess(sp, command);
+ sp = checkid(sp,',');
+ c = *sp; *sp = '\0';
+ while( c == ' ' || c == '\n' || c == '\t'|| c == ',')c =(*++sp);
+ }
+}
+
+
+ static void
+getmessage(option)
+ char *option;
+{
+ struct Message *pt;
+ struct cbuf cb;
+ char *m;
+
+ if (!(m = strchr(option, ':'))) {
+ error("-m option lacks revision number");
+ return;
+ }
+ *m++ = 0;
+ cb = cleanlogmsg(m, strlen(m));
+ if (!cb.size) {
+ error("-m option lacks log message");
+ return;
+ }
+ pt = talloc(struct Message);
+ pt->revno = option;
+ pt->message = cb;
+ pt->nextmessage = 0;
+ *nextmessage = pt;
+ nextmessage = &pt->nextmessage;
+}
+
+
+ static void
+getstates(sp)
+char *sp;
+/* Function: get one state attribute and the corresponding */
+/* revision and store in statelst */
+
+{
+ char const *temp;
+ struct Status *pt;
+ register c;
+
+ while ((c = *++sp) ==' ' || c == '\t' || c == '\n')
+ continue;
+ temp = sp;
+ sp = checkid(sp,':'); /* check for invalid state attribute */
+ c = *sp; *sp = '\0';
+ while( c == ' ' || c == '\t' || c == '\n' ) c = *++sp;
+
+ if ( c == '\0' ) { /* change state of def. branch or Head */
+ chgheadstate = true;
+ headstate = temp;
+ return;
+ }
+ else if ( c != ':' ) {
+ error("missing ':' after state in option -s");
+ return;
+ }
+
+ while ((c = *++sp) == ' ' || c == '\t' || c == '\n')
+ continue;
+ pt = talloc(struct Status);
+ pt->status = temp;
+ pt->revno = sp;
+ pt->nextstatus = 0;
+ *nextstate = pt;
+ nextstate = &pt->nextstatus;
+}
+
+
+
+ static void
+getdelrev(sp)
+char *sp;
+/* Function: get revision range or branch to be deleted, */
+/* and place in delrev */
+{
+ int c;
+ struct delrevpair *pt;
+ int separator;
+
+ pt = &delrev;
+ while ((c = (*++sp)) == ' ' || c == '\n' || c == '\t')
+ continue;
+
+ /* Support old ambiguous '-' syntax; this will go away. */
+ if (strchr(sp,':'))
+ separator = ':';
+ else {
+ if (strchr(sp,'-') && VERSION(5) <= RCSversion)
+ warn("`-' is obsolete in `-o%s'; use `:' instead", sp);
+ separator = '-';
+ }
+
+ if (c == separator) { /* -o:rev */
+ while ((c = (*++sp)) == ' ' || c == '\n' || c == '\t')
+ continue;
+ pt->strt = sp; pt->code = 1;
+ while( c != ' ' && c != '\n' && c != '\t' && c != '\0') c =(*++sp);
+ *sp = '\0';
+ pt->end = 0;
+ return;
+ }
+ else {
+ pt->strt = sp;
+ while( c != ' ' && c != '\n' && c != '\t' && c != '\0'
+ && c != separator ) c = *++sp;
+ *sp = '\0';
+ while( c == ' ' || c == '\n' || c == '\t' ) c = *++sp;
+ if ( c == '\0' ) { /* -o rev or branch */
+ pt->code = 0;
+ pt->end = 0;
+ return;
+ }
+ if (c != separator) {
+ error("invalid range %s %s after -o", pt->strt, sp);
+ }
+ while ((c = *++sp) == ' ' || c == '\n' || c == '\t')
+ continue;
+ if (!c) { /* -orev: */
+ pt->code = 2;
+ pt->end = 0;
+ return;
+ }
+ }
+ /* -orev1:rev2 */
+ pt->end = sp; pt->code = 3;
+ while( c!= ' ' && c != '\n' && c != '\t' && c != '\0') c = *++sp;
+ *sp = '\0';
+}
+
+
+
+
+ static void
+scanlogtext(delta,edit)
+ struct hshentry *delta;
+ int edit;
+/* Function: Scans delta text nodes up to and including the one given
+ * by delta, or up to last one present, if !delta.
+ * For the one given by delta (if delta), the log message is saved into
+ * delta->log if delta==cuttail; the text is edited if EDIT is set, else copied.
+ * Assumes the initial lexeme must be read in first.
+ * Does not advance nexttok after it is finished, except if !delta.
+ */
+{
+ struct hshentry const *nextdelta;
+ struct cbuf cb;
+
+ for (;;) {
+ foutptr = 0;
+ if (eoflex()) {
+ if(delta)
+ rcsfaterror("can't find delta for revision %s",
+ delta->num
+ );
+ return; /* no more delta text nodes */
+ }
+ nextlex();
+ if (!(nextdelta=getnum()))
+ fatserror("delta number corrupted");
+ if (nextdelta->selector) {
+ foutptr = frewrite;
+ aprintf(frewrite,DELNUMFORM,nextdelta->num,Klog);
+ }
+ getkeystring(Klog);
+ if (nextdelta == cuttail) {
+ cb = savestring(&curlogbuf);
+ if (!delta->log.string)
+ delta->log = cleanlogmsg(curlogbuf.string, cb.size);
+ nextlex();
+ delta->igtext = getphrases(Ktext);
+ } else {
+ if (nextdelta->log.string && nextdelta->selector) {
+ foutptr = 0;
+ readstring();
+ foutptr = frewrite;
+ putstring(foutptr, false, nextdelta->log, true);
+ afputc(nextc, foutptr);
+ } else
+ readstring();
+ ignorephrases(Ktext);
+ }
+ getkeystring(Ktext);
+
+ if (delta==nextdelta)
+ break;
+ readstring(); /* skip over it */
+
+ }
+ /* got the one we're looking for */
+ if (edit)
+ editstring((struct hshentry*)0);
+ else
+ enterstring();
+}
+
+
+
+ static struct Lockrev **
+rmnewlocklst(which)
+ char const *which;
+/* Remove lock to revision WHICH from newlocklst. */
+{
+ struct Lockrev *pt, **pre;
+
+ pre = &newlocklst;
+ while ((pt = *pre))
+ if (strcmp(pt->revno, which) != 0)
+ pre = &pt->nextrev;
+ else {
+ *pre = pt->nextrev;
+ tfree(pt);
+ }
+ return pre;
+}
+
+
+
+ static int
+doaccess()
+{
+ register struct chaccess *ch;
+ register struct access **p, *t;
+ register int changed = false;
+
+ for (ch = chaccess; ch; ch = ch->nextchaccess) {
+ switch (ch->command) {
+ case erase:
+ if (!ch->login) {
+ if (AccessList) {
+ AccessList = 0;
+ changed = true;
+ }
+ } else
+ for (p = &AccessList; (t = *p); p = &t->nextaccess)
+ if (strcmp(ch->login, t->login) == 0) {
+ *p = t->nextaccess;
+ changed = true;
+ break;
+ }
+ break;
+ case append:
+ for (p = &AccessList; ; p = &t->nextaccess)
+ if (!(t = *p)) {
+ *p = t = ftalloc(struct access);
+ t->login = ch->login;
+ t->nextaccess = 0;
+ changed = true;
+ break;
+ } else if (strcmp(ch->login, t->login) == 0)
+ break;
+ break;
+ }
+ }
+ return changed;
+}
+
+
+ static int
+sendmail(Delta, who)
+ char const *Delta, *who;
+/* Function: mail to who, informing him that his lock on delta was
+ * broken by caller. Ask first whether to go ahead. Return false on
+ * error or if user decides not to break the lock.
+ */
+{
+#ifdef SENDMAIL
+ char const *messagefile;
+ int old1, old2, c, status;
+ FILE * mailmess;
+#endif
+
+
+ aprintf(stderr, "Revision %s is already locked by %s.\n", Delta, who);
+ if (suppress_mail)
+ return true;
+ if (!yesorno(false, "Do you want to break the lock? [ny](n): "))
+ return false;
+
+ /* go ahead with breaking */
+#ifdef SENDMAIL
+ messagefile = maketemp(0);
+ if (!(mailmess = fopenSafer(messagefile, "w+"))) {
+ efaterror(messagefile);
+ }
+
+ aprintf(mailmess, "Subject: Broken lock on %s\n\nYour lock on revision %s of file %s\nhas been broken by %s for the following reason:\n",
+ basefilename(RCSname), Delta, getfullRCSname(), getcaller()
+ );
+ aputs("State the reason for breaking the lock:\n(terminate with single '.' or end of file)\n>> ", stderr);
+ eflush();
+
+ old1 = '\n'; old2 = ' ';
+ for (; ;) {
+ c = getcstdin();
+ if (feof(stdin)) {
+ aprintf(mailmess, "%c\n", old1);
+ break;
+ }
+ else if ( c == '\n' && old1 == '.' && old2 == '\n')
+ break;
+ else {
+ afputc(old1, mailmess);
+ old2 = old1; old1 = c;
+ if (c == '\n') {
+ aputs(">> ", stderr);
+ eflush();
+ }
+ }
+ }
+ Orewind(mailmess);
+ aflush(mailmess);
+ status = run(fileno(mailmess), (char*)0, SENDMAIL, who, (char*)0);
+ Ozclose(&mailmess);
+ if (status == 0)
+ return true;
+ warn("Mail failed.");
+#endif
+ warn("Mail notification of broken locks is not available.");
+ warn("Please tell `%s' why you broke the lock.", who);
+ return(true);
+}
+
+
+
+ static int
+breaklock(delta)
+ struct hshentry const *delta;
+/* function: Finds the lock held by caller on delta,
+ * and removes it.
+ * Sends mail if a lock different from the caller's is broken.
+ * Prints an error message if there is no such lock or error.
+ */
+{
+ register struct rcslock *next, **trail;
+ char const *num;
+
+ num=delta->num;
+ for (trail = &Locks; (next = *trail); trail = &next->nextlock)
+ if (strcmp(num, next->delta->num) == 0) {
+ if (
+ strcmp(getcaller(),next->login) != 0
+ && !sendmail(num, next->login)
+ ) {
+ rcserror("revision %s still locked by %s",
+ num, next->login
+ );
+ return false;
+ }
+ diagnose("%s unlocked\n", next->delta->num);
+ *trail = next->nextlock;
+ next->delta->lockedby = 0;
+ return true;
+ }
+ rcserror("no lock set on revision %s", num);
+ return false;
+}
+
+
+
+ static struct hshentry *
+searchcutpt(object, length, store)
+ char const *object;
+ int length;
+ struct hshentries *store;
+/* Function: Search store and return entry with number being object. */
+/* cuttail = 0, if the entry is Head; otherwise, cuttail */
+/* is the entry point to the one with number being object */
+
+{
+ cuthead = 0;
+ while (compartial(store->first->num, object, length)) {
+ cuthead = store->first;
+ store = store->rest;
+ }
+ return store->first;
+}
+
+
+
+ static int
+branchpoint(strt, tail)
+struct hshentry *strt, *tail;
+/* Function: check whether the deltas between strt and tail */
+/* are locked or branch point, return 1 if any is */
+/* locked or branch point; otherwise, return 0 and */
+/* mark deleted */
+
+{
+ struct hshentry *pt;
+ struct rcslock const *lockpt;
+
+ for (pt = strt; pt != tail; pt = pt->next) {
+ if ( pt->branches ){ /* a branch point */
+ rcserror("can't remove branch point %s", pt->num);
+ return true;
+ }
+ for (lockpt = Locks; lockpt; lockpt = lockpt->nextlock)
+ if (lockpt->delta == pt) {
+ rcserror("can't remove locked revision %s", pt->num);
+ return true;
+ }
+ pt->selector = false;
+ diagnose("deleting revision %s\n",pt->num);
+ }
+ return false;
+}
+
+
+
+ static int
+removerevs()
+/* Function: get the revision range to be removed, and place the */
+/* first revision removed in delstrt, the revision before */
+/* delstrt in cuthead (0, if delstrt is head), and the */
+/* revision after the last removed revision in cuttail (0 */
+/* if the last is a leaf */
+
+{
+ struct hshentry *target, *target2, *temp;
+ int length;
+ int cmp;
+
+ if (!expandsym(delrev.strt, &numrev)) return 0;
+ target = genrevs(numrev.string,(char*)0,(char*)0,(char*)0,&gendeltas);
+ if ( ! target ) return 0;
+ cmp = cmpnum(target->num, numrev.string);
+ length = countnumflds(numrev.string);
+
+ if (delrev.code == 0) { /* -o rev or -o branch */
+ if (length & 1)
+ temp=searchcutpt(target->num,length+1,gendeltas);
+ else if (cmp) {
+ rcserror("Revision %s doesn't exist.", numrev.string);
+ return 0;
+ }
+ else
+ temp = searchcutpt(numrev.string, length, gendeltas);
+ cuttail = target->next;
+ if ( branchpoint(temp, cuttail) ) {
+ cuttail = 0;
+ return 0;
+ }
+ delstrt = temp; /* first revision to be removed */
+ return 1;
+ }
+
+ if (length & 1) { /* invalid branch after -o */
+ rcserror("invalid branch range %s after -o", numrev.string);
+ return 0;
+ }
+
+ if (delrev.code == 1) { /* -o -rev */
+ if ( length > 2 ) {
+ temp = searchcutpt( target->num, length-1, gendeltas);
+ cuttail = target->next;
+ }
+ else {
+ temp = searchcutpt(target->num, length, gendeltas);
+ cuttail = target;
+ while( cuttail && ! cmpnumfld(target->num,cuttail->num,1) )
+ cuttail = cuttail->next;
+ }
+ if ( branchpoint(temp, cuttail) ){
+ cuttail = 0;
+ return 0;
+ }
+ delstrt = temp;
+ return 1;
+ }
+
+ if (delrev.code == 2) { /* -o rev- */
+ if ( length == 2 ) {
+ temp = searchcutpt(target->num, 1,gendeltas);
+ if (cmp)
+ cuttail = target;
+ else
+ cuttail = target->next;
+ }
+ else {
+ if (cmp) {
+ cuthead = target;
+ if ( !(temp = target->next) ) return 0;
+ }
+ else
+ temp = searchcutpt(target->num, length, gendeltas);
+ getbranchno(temp->num, &numrev); /* get branch number */
+ VOID genrevs(numrev.string, (char*)0, (char*)0, (char*)0, &gendeltas);
+ }
+ if ( branchpoint( temp, cuttail ) ) {
+ cuttail = 0;
+ return 0;
+ }
+ delstrt = temp;
+ return 1;
+ }
+
+ /* -o rev1-rev2 */
+ if (!expandsym(delrev.end, &numrev)) return 0;
+ if (
+ length != countnumflds(numrev.string)
+ || (length>2 && compartial(numrev.string, target->num, length-1))
+ ) {
+ rcserror("invalid revision range %s-%s",
+ target->num, numrev.string
+ );
+ return 0;
+ }
+
+ target2 = genrevs(numrev.string,(char*)0,(char*)0,(char*)0,&gendeltas);
+ if ( ! target2 ) return 0;
+
+ if ( length > 2) { /* delete revisions on branches */
+ if ( cmpnum(target->num, target2->num) > 0) {
+ cmp = cmpnum(target2->num, numrev.string);
+ temp = target;
+ target = target2;
+ target2 = temp;
+ }
+ if (cmp) {
+ if ( ! cmpnum(target->num, target2->num) ) {
+ rcserror("Revisions %s-%s don't exist.",
+ delrev.strt, delrev.end
+ );
+ return 0;
+ }
+ cuthead = target;
+ temp = target->next;
+ }
+ else
+ temp = searchcutpt(target->num, length, gendeltas);
+ cuttail = target2->next;
+ }
+ else { /* delete revisions on trunk */
+ if ( cmpnum( target->num, target2->num) < 0 ) {
+ temp = target;
+ target = target2;
+ target2 = temp;
+ }
+ else
+ cmp = cmpnum(target2->num, numrev.string);
+ if (cmp) {
+ if ( ! cmpnum(target->num, target2->num) ) {
+ rcserror("Revisions %s-%s don't exist.",
+ delrev.strt, delrev.end
+ );
+ return 0;
+ }
+ cuttail = target2;
+ }
+ else
+ cuttail = target2->next;
+ temp = searchcutpt(target->num, length, gendeltas);
+ }
+ if ( branchpoint(temp, cuttail) ) {
+ cuttail = 0;
+ return 0;
+ }
+ delstrt = temp;
+ return 1;
+}
+
+
+
+ static int
+doassoc()
+/* Add or delete (if !revno) association that is stored in assoclst. */
+{
+ char const *p;
+ int changed = false;
+ struct Symrev const *curassoc;
+ struct assoc **pre, *pt;
+
+ /* add new associations */
+ for (curassoc = assoclst; curassoc; curassoc = curassoc->nextsym) {
+ char const *ssymbol = curassoc->ssymbol;
+
+ if (!curassoc->revno) { /* delete symbol */
+ for (pre = &Symbols; ; pre = &pt->nextassoc)
+ if (!(pt = *pre)) {
+ rcswarn("can't delete nonexisting symbol %s", ssymbol);
+ break;
+ } else if (strcmp(pt->symbol, ssymbol) == 0) {
+ *pre = pt->nextassoc;
+ changed = true;
+ break;
+ }
+ }
+ else {
+ if (curassoc->revno[0]) {
+ p = 0;
+ if (expandsym(curassoc->revno, &numrev))
+ p = fstr_save(numrev.string);
+ } else if (!(p = tiprev()))
+ rcserror("no latest revision to associate with symbol %s",
+ ssymbol
+ );
+ if (p)
+ changed |= addsymbol(p, ssymbol, curassoc->override);
+ }
+ }
+ return changed;
+}
+
+
+
+ static int
+dolocks()
+/* Function: remove lock for caller or first lock if unlockcaller is set;
+ * remove locks which are stored in rmvlocklst,
+ * add new locks which are stored in newlocklst,
+ * add lock for Dbranch or Head if lockhead is set.
+ */
+{
+ struct Lockrev const *lockpt;
+ struct hshentry *target;
+ int changed = false;
+
+ if (unlockcaller) { /* find lock for caller */
+ if ( Head ) {
+ if (Locks) {
+ switch (findlock(true, &target)) {
+ case 0:
+ /* remove most recent lock */
+ changed |= breaklock(Locks->delta);
+ break;
+ case 1:
+ diagnose("%s unlocked\n",target->num);
+ changed = true;
+ break;
+ }
+ } else {
+ rcswarn("No locks are set.");
+ }
+ } else {
+ rcswarn("can't unlock an empty tree");
+ }
+ }
+
+ /* remove locks which are stored in rmvlocklst */
+ for (lockpt = rmvlocklst; lockpt; lockpt = lockpt->nextrev)
+ if (expandsym(lockpt->revno, &numrev)) {
+ target = genrevs(numrev.string, (char *)0, (char *)0, (char *)0, &gendeltas);
+ if ( target )
+ if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string))
+ rcserror("can't unlock nonexisting revision %s",
+ lockpt->revno
+ );
+ else
+ changed |= breaklock(target);
+ /* breaklock does its own diagnose */
+ }
+
+ /* add new locks which stored in newlocklst */
+ for (lockpt = newlocklst; lockpt; lockpt = lockpt->nextrev)
+ changed |= setlock(lockpt->revno);
+
+ if (lockhead) /* lock default branch or head */
+ if (Dbranch)
+ changed |= setlock(Dbranch);
+ else if (Head)
+ changed |= setlock(Head->num);
+ else
+ rcswarn("can't lock an empty tree");
+ return changed;
+}
+
+
+
+ static int
+setlock(rev)
+ char const *rev;
+/* Function: Given a revision or branch number, finds the corresponding
+ * delta and locks it for caller.
+ */
+{
+ struct hshentry *target;
+ int r;
+
+ if (expandsym(rev, &numrev)) {
+ target = genrevs(numrev.string, (char*)0, (char*)0,
+ (char*)0, &gendeltas);
+ if ( target )
+ if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string))
+ rcserror("can't lock nonexisting revision %s",
+ numrev.string
+ );
+ else {
+ if ((r = addlock(target, false)) < 0 && breaklock(target))
+ r = addlock(target, true);
+ if (0 <= r) {
+ if (r)
+ diagnose("%s locked\n", target->num);
+ return r;
+ }
+ }
+ }
+ return 0;
+}
+
+
+ static int
+domessages()
+{
+ struct hshentry *target;
+ struct Message *p;
+ int changed = false;
+
+ for (p = messagelst; p; p = p->nextmessage)
+ if (
+ expandsym(p->revno, &numrev) &&
+ (target = genrevs(
+ numrev.string, (char*)0, (char*)0, (char*)0, &gendeltas
+ ))
+ ) {
+ /*
+ * We can't check the old log -- it's much later in the file.
+ * We pessimistically assume that it changed.
+ */
+ target->log = p->message;
+ changed = true;
+ }
+ return changed;
+}
+
+
+ static int
+rcs_setstate(rev,status)
+ char const *rev, *status;
+/* Function: Given a revision or branch number, finds the corresponding delta
+ * and sets its state to status.
+ */
+{
+ struct hshentry *target;
+
+ if (expandsym(rev, &numrev)) {
+ target = genrevs(numrev.string, (char*)0, (char*)0,
+ (char*)0, &gendeltas);
+ if ( target )
+ if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string))
+ rcserror("can't set state of nonexisting revision %s",
+ numrev.string
+ );
+ else if (strcmp(target->state, status) != 0) {
+ target->state = status;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+ static int
+buildeltatext(deltas)
+ struct hshentries const *deltas;
+/* Function: put the delta text on frewrite and make necessary */
+/* change to delta text */
+{
+ register FILE *fcut; /* temporary file to rebuild delta tree */
+ char const *cutname;
+
+ fcut = 0;
+ cuttail->selector = false;
+ scanlogtext(deltas->first, false);
+ if ( cuthead ) {
+ cutname = maketemp(3);
+ if (!(fcut = fopenSafer(cutname, FOPEN_WPLUS_WORK))) {
+ efaterror(cutname);
+ }
+
+ while (deltas->first != cuthead) {
+ deltas = deltas->rest;
+ scanlogtext(deltas->first, true);
+ }
+
+ snapshotedit(fcut);
+ Orewind(fcut);
+ aflush(fcut);
+ }
+
+ while (deltas->first != cuttail)
+ scanlogtext((deltas = deltas->rest)->first, true);
+ finishedit((struct hshentry*)0, (FILE*)0, true);
+ Ozclose(&fcopy);
+
+ if (fcut) {
+ char const *diffname = maketemp(0);
+ char const *diffv[6 + !!OPEN_O_BINARY];
+ char const **diffp = diffv;
+ *++diffp = DIFF;
+ *++diffp = DIFFFLAGS;
+# if OPEN_O_BINARY
+ if (Expand == BINARY_EXPAND)
+ *++diffp == "--binary";
+# endif
+ *++diffp = "-";
+ *++diffp = resultname;
+ *++diffp = 0;
+ switch (runv(fileno(fcut), diffname, diffv)) {
+ case DIFF_FAILURE: case DIFF_SUCCESS: break;
+ default: rcsfaterror("diff failed");
+ }
+ Ofclose(fcut);
+ return putdtext(cuttail,diffname,frewrite,true);
+ } else
+ return putdtext(cuttail,resultname,frewrite,false);
+}
+
+
+
+ static void
+buildtree()
+/* Function: actually removes revisions whose selector field */
+/* is false, and rebuilds the linkage of deltas. */
+/* asks for reconfirmation if deleting last revision*/
+{
+ struct hshentry * Delta;
+ struct branchhead *pt, *pre;
+
+ if ( cuthead )
+ if ( cuthead->next == delstrt )
+ cuthead->next = cuttail;
+ else {
+ pre = pt = cuthead->branches;
+ while( pt && pt->hsh != delstrt ) {
+ pre = pt;
+ pt = pt->nextbranch;
+ }
+ if ( cuttail )
+ pt->hsh = cuttail;
+ else if ( pt == pre )
+ cuthead->branches = pt->nextbranch;
+ else
+ pre->nextbranch = pt->nextbranch;
+ }
+ else {
+ if (!cuttail && !quietflag) {
+ if (!yesorno(false, "Do you really want to delete all revisions? [ny](n): ")) {
+ rcserror("No revision deleted");
+ Delta = delstrt;
+ while( Delta) {
+ Delta->selector = true;
+ Delta = Delta->next;
+ }
+ return;
+ }
+ }
+ Head = cuttail;
+ }
+ return;
+}
+
+#if RCS_lint
+/* This lets us lint everything all at once. */
+
+char const cmdid[] = "";
+
+#define go(p,e) {int p P((int,char**)); void e P((void)); if(*argv)return p(argc,argv);if(*argv[1])e();}
+
+ int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ go(ciId, ciExit);
+ go(coId, coExit);
+ go(identId, identExit);
+ go(mergeId, mergeExit);
+ go(rcsId, exiterr);
+ go(rcscleanId, rcscleanExit);
+ go(rcsdiffId, rdiffExit);
+ go(rcsmergeId, rmergeExit);
+ go(rlogId, rlogExit);
+ return 0;
+}
+#endif
OpenPOWER on IntegriCloud