xref: /dragonfly/gnu/usr.bin/rcs/rcs/rcs.c (revision 86d7f5d3)
1*86d7f5d3SJohn Marino /* Change RCS file attributes.  */
2*86d7f5d3SJohn Marino 
3*86d7f5d3SJohn Marino /* Copyright 1982, 1988, 1989 Walter Tichy
4*86d7f5d3SJohn Marino    Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
5*86d7f5d3SJohn Marino    Distributed under license by the Free Software Foundation, Inc.
6*86d7f5d3SJohn Marino 
7*86d7f5d3SJohn Marino This file is part of RCS.
8*86d7f5d3SJohn Marino 
9*86d7f5d3SJohn Marino RCS is free software; you can redistribute it and/or modify
10*86d7f5d3SJohn Marino it under the terms of the GNU General Public License as published by
11*86d7f5d3SJohn Marino the Free Software Foundation; either version 2, or (at your option)
12*86d7f5d3SJohn Marino any later version.
13*86d7f5d3SJohn Marino 
14*86d7f5d3SJohn Marino RCS is distributed in the hope that it will be useful,
15*86d7f5d3SJohn Marino but WITHOUT ANY WARRANTY; without even the implied warranty of
16*86d7f5d3SJohn Marino MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17*86d7f5d3SJohn Marino GNU General Public License for more details.
18*86d7f5d3SJohn Marino 
19*86d7f5d3SJohn Marino You should have received a copy of the GNU General Public License
20*86d7f5d3SJohn Marino along with RCS; see the file COPYING.
21*86d7f5d3SJohn Marino If not, write to the Free Software Foundation,
22*86d7f5d3SJohn Marino 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23*86d7f5d3SJohn Marino 
24*86d7f5d3SJohn Marino Report problems and direct all questions to:
25*86d7f5d3SJohn Marino 
26*86d7f5d3SJohn Marino     rcs-bugs@cs.purdue.edu
27*86d7f5d3SJohn Marino 
28*86d7f5d3SJohn Marino */
29*86d7f5d3SJohn Marino 
30*86d7f5d3SJohn Marino /*
31*86d7f5d3SJohn Marino  * $FreeBSD: src/gnu/usr.bin/rcs/rcs/rcs.c,v 1.7 1999/08/27 23:36:52 peter Exp $
32*86d7f5d3SJohn Marino  * $DragonFly: src/gnu/usr.bin/rcs/rcs/rcs.c,v 1.2 2003/06/17 04:25:47 dillon Exp $
33*86d7f5d3SJohn Marino  *
34*86d7f5d3SJohn Marino  * Revision 5.21  1995/06/16 06:19:24  eggert
35*86d7f5d3SJohn Marino  * Update FSF address.
36*86d7f5d3SJohn Marino  *
37*86d7f5d3SJohn Marino  * Revision 5.20  1995/06/01 16:23:43  eggert
38*86d7f5d3SJohn Marino  * (main): Warn if no options were given.  Punctuate messages properly.
39*86d7f5d3SJohn Marino  *
40*86d7f5d3SJohn Marino  * (sendmail): Rewind mailmess before flushing it.
41*86d7f5d3SJohn Marino  * Output another warning if mail should work but fails.
42*86d7f5d3SJohn Marino  *
43*86d7f5d3SJohn Marino  * (buildeltatext): Pass "--binary" if -kb and if --binary makes a difference.
44*86d7f5d3SJohn Marino  *
45*86d7f5d3SJohn Marino  * Revision 5.19  1994/03/17 14:05:48  eggert
46*86d7f5d3SJohn Marino  * Use ORCSerror to clean up after a fatal error.  Remove lint.
47*86d7f5d3SJohn Marino  * Specify subprocess input via file descriptor, not file name.  Remove lint.
48*86d7f5d3SJohn Marino  * Flush stderr after prompt.
49*86d7f5d3SJohn Marino  *
50*86d7f5d3SJohn Marino  * Revision 5.18  1993/11/09 17:40:15  eggert
51*86d7f5d3SJohn Marino  * -V now prints version on stdout and exits.  Don't print usage twice.
52*86d7f5d3SJohn Marino  *
53*86d7f5d3SJohn Marino  * Revision 5.17  1993/11/03 17:42:27  eggert
54*86d7f5d3SJohn Marino  * Add -z.  Don't lose track of -m or -t when there are no other changes.
55*86d7f5d3SJohn Marino  * Don't discard ignored phrases.  Improve quality of diagnostics.
56*86d7f5d3SJohn Marino  *
57*86d7f5d3SJohn Marino  * Revision 5.16  1992/07/28  16:12:44  eggert
58*86d7f5d3SJohn Marino  * rcs -l now asks whether you want to break the lock.
59*86d7f5d3SJohn Marino  * Add -V.  Set RCS file's mode and time at right moment.
60*86d7f5d3SJohn Marino  *
61*86d7f5d3SJohn Marino  * Revision 5.15  1992/02/17  23:02:20  eggert
62*86d7f5d3SJohn Marino  * Add -T.
63*86d7f5d3SJohn Marino  *
64*86d7f5d3SJohn Marino  * Revision 5.14  1992/01/27  16:42:53  eggert
65*86d7f5d3SJohn Marino  * Add -M.  Avoid invoking umask(); it's one less thing to configure.
66*86d7f5d3SJohn Marino  * Add support for bad_creat0.  lint -> RCS_lint
67*86d7f5d3SJohn Marino  *
68*86d7f5d3SJohn Marino  * Revision 5.13  1992/01/06  02:42:34  eggert
69*86d7f5d3SJohn Marino  * Avoid changing RCS file in common cases where no change can occur.
70*86d7f5d3SJohn Marino  *
71*86d7f5d3SJohn Marino  * Revision 5.12  1991/11/20  17:58:08  eggert
72*86d7f5d3SJohn Marino  * Don't read the delta tree from a nonexistent RCS file.
73*86d7f5d3SJohn Marino  *
74*86d7f5d3SJohn Marino  * Revision 5.11  1991/10/07  17:32:46  eggert
75*86d7f5d3SJohn Marino  * Remove lint.
76*86d7f5d3SJohn Marino  *
77*86d7f5d3SJohn Marino  * Revision 5.10  1991/08/19  23:17:54  eggert
78*86d7f5d3SJohn Marino  * Add -m, -r$, piece tables.  Revision separator is `:', not `-'.  Tune.
79*86d7f5d3SJohn Marino  *
80*86d7f5d3SJohn Marino  * Revision 5.9  1991/04/21  11:58:18  eggert
81*86d7f5d3SJohn Marino  * Add -x, RCSINIT, MS-DOS support.
82*86d7f5d3SJohn Marino  *
83*86d7f5d3SJohn Marino  * Revision 5.8  1991/02/25  07:12:38  eggert
84*86d7f5d3SJohn Marino  * strsave -> str_save (DG/UX name clash)
85*86d7f5d3SJohn Marino  * 0444 -> S_IRUSR|S_IRGRP|S_IROTH for portability
86*86d7f5d3SJohn Marino  *
87*86d7f5d3SJohn Marino  * Revision 5.7  1990/12/18  17:19:21  eggert
88*86d7f5d3SJohn Marino  * Fix bug with multiple -n and -N options.
89*86d7f5d3SJohn Marino  *
90*86d7f5d3SJohn Marino  * Revision 5.6  1990/12/04  05:18:40  eggert
91*86d7f5d3SJohn Marino  * Use -I for prompts and -q for diagnostics.
92*86d7f5d3SJohn Marino  *
93*86d7f5d3SJohn Marino  * Revision 5.5  1990/11/11  00:06:35  eggert
94*86d7f5d3SJohn Marino  * Fix `rcs -e' core dump.
95*86d7f5d3SJohn Marino  *
96*86d7f5d3SJohn Marino  * Revision 5.4  1990/11/01  05:03:33  eggert
97*86d7f5d3SJohn Marino  * Add -I and new -t behavior.  Permit arbitrary data in logs.
98*86d7f5d3SJohn Marino  *
99*86d7f5d3SJohn Marino  * Revision 5.3  1990/10/04  06:30:16  eggert
100*86d7f5d3SJohn Marino  * Accumulate exit status across files.
101*86d7f5d3SJohn Marino  *
102*86d7f5d3SJohn Marino  * Revision 5.2  1990/09/04  08:02:17  eggert
103*86d7f5d3SJohn Marino  * Standardize yes-or-no procedure.
104*86d7f5d3SJohn Marino  *
105*86d7f5d3SJohn Marino  * Revision 5.1  1990/08/29  07:13:51  eggert
106*86d7f5d3SJohn Marino  * Remove unused setuid support.  Clean old log messages too.
107*86d7f5d3SJohn Marino  *
108*86d7f5d3SJohn Marino  * Revision 5.0  1990/08/22  08:12:42  eggert
109*86d7f5d3SJohn Marino  * Don't lose names when applying -a option to multiple files.
110*86d7f5d3SJohn Marino  * Remove compile-time limits; use malloc instead.  Add setuid support.
111*86d7f5d3SJohn Marino  * Permit dates past 1999/12/31.  Make lock and temp files faster and safer.
112*86d7f5d3SJohn Marino  * Ansify and Posixate.  Add -V.  Fix umask bug.  Make linting easier.  Tune.
113*86d7f5d3SJohn Marino  * Yield proper exit status.  Check diff's output.
114*86d7f5d3SJohn Marino  *
115*86d7f5d3SJohn Marino  * Revision 4.11  89/05/01  15:12:06  narten
116*86d7f5d3SJohn Marino  * changed copyright header to reflect current distribution rules
117*86d7f5d3SJohn Marino  *
118*86d7f5d3SJohn Marino  * Revision 4.10  88/11/08  16:01:54  narten
119*86d7f5d3SJohn Marino  * didn't install previous patch correctly
120*86d7f5d3SJohn Marino  *
121*86d7f5d3SJohn Marino  * Revision 4.9  88/11/08  13:56:01  narten
122*86d7f5d3SJohn Marino  * removed include <sysexits.h> (not needed)
123*86d7f5d3SJohn Marino  * minor fix for -A option
124*86d7f5d3SJohn Marino  *
125*86d7f5d3SJohn Marino  * Revision 4.8  88/08/09  19:12:27  eggert
126*86d7f5d3SJohn Marino  * Don't access freed storage.
127*86d7f5d3SJohn Marino  * Use execv(), not system(); yield proper exit status; remove lint.
128*86d7f5d3SJohn Marino  *
129*86d7f5d3SJohn Marino  * Revision 4.7  87/12/18  11:37:17  narten
130*86d7f5d3SJohn Marino  * lint cleanups (Guy Harris)
131*86d7f5d3SJohn Marino  *
132*86d7f5d3SJohn Marino  * Revision 4.6  87/10/18  10:28:48  narten
133*86d7f5d3SJohn Marino  * Updating verison numbers. Changes relative to 1.1 are actually
134*86d7f5d3SJohn Marino  * relative to 4.3
135*86d7f5d3SJohn Marino  *
136*86d7f5d3SJohn Marino  * Revision 1.4  87/09/24  13:58:52  narten
137*86d7f5d3SJohn Marino  * Sources now pass through lint (if you ignore printf/sprintf/fprintf
138*86d7f5d3SJohn Marino  * warnings)
139*86d7f5d3SJohn Marino  *
140*86d7f5d3SJohn Marino  * Revision 1.3  87/03/27  14:21:55  jenkins
141*86d7f5d3SJohn Marino  * Port to suns
142*86d7f5d3SJohn Marino  *
143*86d7f5d3SJohn Marino  * Revision 1.2  85/12/17  13:59:09  albitz
144*86d7f5d3SJohn Marino  * Changed setstate to rcs_setstate because of conflict with random.o.
145*86d7f5d3SJohn Marino  *
146*86d7f5d3SJohn Marino  * Revision 4.3  83/12/15  12:27:33  wft
147*86d7f5d3SJohn Marino  * rcs -u now breaks most recent lock if it can't find a lock by the caller.
148*86d7f5d3SJohn Marino  *
149*86d7f5d3SJohn Marino  * Revision 4.2  83/12/05  10:18:20  wft
150*86d7f5d3SJohn Marino  * Added conditional compilation for sending mail.
151*86d7f5d3SJohn Marino  * Alternatives: V4_2BSD, V6, USG, and other.
152*86d7f5d3SJohn Marino  *
153*86d7f5d3SJohn Marino  * Revision 4.1  83/05/10  16:43:02  wft
154*86d7f5d3SJohn Marino  * Simplified breaklock(); added calls to findlock() and getcaller().
155*86d7f5d3SJohn Marino  * Added option -b (default branch). Updated -s and -w for -b.
156*86d7f5d3SJohn Marino  * Removed calls to stat(); now done by pairfilenames().
157*86d7f5d3SJohn Marino  * Replaced most catchints() calls with restoreints().
158*86d7f5d3SJohn Marino  * Removed check for exit status of delivermail().
159*86d7f5d3SJohn Marino  * Directed all interactive output to stderr.
160*86d7f5d3SJohn Marino  *
161*86d7f5d3SJohn Marino  * Revision 3.9.1.1  83/12/02  22:08:51  wft
162*86d7f5d3SJohn Marino  * Added conditional compilation for 4.2 sendmail and 4.1 delivermail.
163*86d7f5d3SJohn Marino  *
164*86d7f5d3SJohn Marino  * Revision 3.9  83/02/15  15:38:39  wft
165*86d7f5d3SJohn Marino  * Added call to fastcopy() to copy remainder of RCS file.
166*86d7f5d3SJohn Marino  *
167*86d7f5d3SJohn Marino  * Revision 3.8  83/01/18  17:37:51  wft
168*86d7f5d3SJohn Marino  * Changed sendmail(): now uses delivermail, and asks whether to break the lock.
169*86d7f5d3SJohn Marino  *
170*86d7f5d3SJohn Marino  * Revision 3.7  83/01/15  18:04:25  wft
171*86d7f5d3SJohn Marino  * Removed putree(); replaced with puttree() in rcssyn.c.
172*86d7f5d3SJohn Marino  * Combined putdellog() and scanlogtext(); deleted putdellog().
173*86d7f5d3SJohn Marino  * Cleaned up diagnostics and error messages. Fixed problem with
174*86d7f5d3SJohn Marino  * mutilated files in case of deletions in 2 files in a single command.
175*86d7f5d3SJohn Marino  * Changed marking of selector from 'D' to DELETE.
176*86d7f5d3SJohn Marino  *
177*86d7f5d3SJohn Marino  * Revision 3.6  83/01/14  15:37:31  wft
178*86d7f5d3SJohn Marino  * Added ignoring of interrupts while new RCS file is renamed;
179*86d7f5d3SJohn Marino  * Avoids deletion of RCS files by interrupts.
180*86d7f5d3SJohn Marino  *
181*86d7f5d3SJohn Marino  * Revision 3.5  82/12/10  21:11:39  wft
182*86d7f5d3SJohn Marino  * Removed unused variables, fixed checking of return code from diff,
183*86d7f5d3SJohn Marino  * introduced variant COMPAT2 for skipping Suffix on -A files.
184*86d7f5d3SJohn Marino  *
185*86d7f5d3SJohn Marino  * Revision 3.4  82/12/04  13:18:20  wft
186*86d7f5d3SJohn Marino  * Replaced getdelta() with gettree(), changed breaklock to update
187*86d7f5d3SJohn Marino  * field lockedby, added some diagnostics.
188*86d7f5d3SJohn Marino  *
189*86d7f5d3SJohn Marino  * Revision 3.3  82/12/03  17:08:04  wft
190*86d7f5d3SJohn Marino  * Replaced getlogin() with getpwuid(), flcose() with ffclose(),
191*86d7f5d3SJohn Marino  * /usr/ucb/Mail with macro MAIL. Removed handling of Suffix (-x).
192*86d7f5d3SJohn Marino  * fixed -u for missing revno. Disambiguated structure members.
193*86d7f5d3SJohn Marino  *
194*86d7f5d3SJohn Marino  * Revision 3.2  82/10/18  21:05:07  wft
195*86d7f5d3SJohn Marino  * rcs -i now generates a file mode given by the umask minus write permission;
196*86d7f5d3SJohn Marino  * otherwise, rcs keeps the mode, but removes write permission.
197*86d7f5d3SJohn Marino  * I added a check for write error, fixed call to getlogin(), replaced
198*86d7f5d3SJohn Marino  * curdir() with getfullRCSname(), cleaned up handling -U/L, and changed
199*86d7f5d3SJohn Marino  * conflicting, long identifiers.
200*86d7f5d3SJohn Marino  *
201*86d7f5d3SJohn Marino  * Revision 3.1  82/10/13  16:11:07  wft
202*86d7f5d3SJohn Marino  * fixed type of variables receiving from getc() (char -> int).
203*86d7f5d3SJohn Marino  */
204*86d7f5d3SJohn Marino 
205*86d7f5d3SJohn Marino 
206*86d7f5d3SJohn Marino #include "rcsbase.h"
207*86d7f5d3SJohn Marino 
208*86d7f5d3SJohn Marino struct  Lockrev {
209*86d7f5d3SJohn Marino 	char const *revno;
210*86d7f5d3SJohn Marino         struct  Lockrev   * nextrev;
211*86d7f5d3SJohn Marino };
212*86d7f5d3SJohn Marino 
213*86d7f5d3SJohn Marino struct  Symrev {
214*86d7f5d3SJohn Marino 	char const *revno;
215*86d7f5d3SJohn Marino 	char const *ssymbol;
216*86d7f5d3SJohn Marino         int     override;
217*86d7f5d3SJohn Marino         struct  Symrev  * nextsym;
218*86d7f5d3SJohn Marino };
219*86d7f5d3SJohn Marino 
220*86d7f5d3SJohn Marino struct Message {
221*86d7f5d3SJohn Marino 	char const *revno;
222*86d7f5d3SJohn Marino 	struct cbuf message;
223*86d7f5d3SJohn Marino 	struct Message *nextmessage;
224*86d7f5d3SJohn Marino };
225*86d7f5d3SJohn Marino 
226*86d7f5d3SJohn Marino struct  Status {
227*86d7f5d3SJohn Marino 	char const *revno;
228*86d7f5d3SJohn Marino 	char const *status;
229*86d7f5d3SJohn Marino         struct  Status  * nextstatus;
230*86d7f5d3SJohn Marino };
231*86d7f5d3SJohn Marino 
232*86d7f5d3SJohn Marino enum changeaccess {append, erase};
233*86d7f5d3SJohn Marino struct chaccess {
234*86d7f5d3SJohn Marino 	char const *login;
235*86d7f5d3SJohn Marino 	enum changeaccess command;
236*86d7f5d3SJohn Marino 	struct chaccess *nextchaccess;
237*86d7f5d3SJohn Marino };
238*86d7f5d3SJohn Marino 
239*86d7f5d3SJohn Marino struct delrevpair {
240*86d7f5d3SJohn Marino 	char const *strt;
241*86d7f5d3SJohn Marino 	char const *end;
242*86d7f5d3SJohn Marino         int     code;
243*86d7f5d3SJohn Marino };
244*86d7f5d3SJohn Marino 
245*86d7f5d3SJohn Marino static int branchpoint P((struct hshentry*,struct hshentry*));
246*86d7f5d3SJohn Marino static int breaklock P((struct hshentry const*));
247*86d7f5d3SJohn Marino static int buildeltatext P((struct hshentries const*));
248*86d7f5d3SJohn Marino static int doaccess P((void));
249*86d7f5d3SJohn Marino static int doassoc P((void));
250*86d7f5d3SJohn Marino static int dolocks P((void));
251*86d7f5d3SJohn Marino static int domessages P((void));
252*86d7f5d3SJohn Marino static int rcs_setstate P((char const*,char const*));
253*86d7f5d3SJohn Marino static int removerevs P((void));
254*86d7f5d3SJohn Marino static int sendmail P((char const*,char const*));
255*86d7f5d3SJohn Marino static int setlock P((char const*));
256*86d7f5d3SJohn Marino static struct Lockrev **rmnewlocklst P((char const*));
257*86d7f5d3SJohn Marino static struct hshentry *searchcutpt P((char const*,int,struct hshentries*));
258*86d7f5d3SJohn Marino static void buildtree P((void));
259*86d7f5d3SJohn Marino static void cleanup P((void));
260*86d7f5d3SJohn Marino static void getaccessor P((char*,enum changeaccess));
261*86d7f5d3SJohn Marino static void getassoclst P((int,char*));
262*86d7f5d3SJohn Marino static void getchaccess P((char const*,enum changeaccess));
263*86d7f5d3SJohn Marino static void getdelrev P((char*));
264*86d7f5d3SJohn Marino static void getmessage P((char*));
265*86d7f5d3SJohn Marino static void getstates P((char*));
266*86d7f5d3SJohn Marino static void scanlogtext P((struct hshentry*,int));
267*86d7f5d3SJohn Marino 
268*86d7f5d3SJohn Marino static struct buf numrev;
269*86d7f5d3SJohn Marino static char const *headstate;
270*86d7f5d3SJohn Marino static int chgheadstate, exitstatus, lockhead, unlockcaller;
271*86d7f5d3SJohn Marino static int suppress_mail;
272*86d7f5d3SJohn Marino static struct Lockrev *newlocklst, *rmvlocklst;
273*86d7f5d3SJohn Marino static struct Message *messagelst, **nextmessage;
274*86d7f5d3SJohn Marino static struct Status *statelst, **nextstate;
275*86d7f5d3SJohn Marino static struct Symrev *assoclst, **nextassoc;
276*86d7f5d3SJohn Marino static struct chaccess *chaccess, **nextchaccess;
277*86d7f5d3SJohn Marino static struct delrevpair delrev;
278*86d7f5d3SJohn Marino static struct hshentry *cuthead, *cuttail, *delstrt;
279*86d7f5d3SJohn Marino static struct hshentries *gendeltas;
280*86d7f5d3SJohn Marino 
281*86d7f5d3SJohn Marino mainProg(rcsId, "rcs", "$DragonFly: src/gnu/usr.bin/rcs/rcs/rcs.c,v 1.2 2003/06/17 04:25:47 dillon Exp $")
282*86d7f5d3SJohn Marino {
283*86d7f5d3SJohn Marino 	static char const cmdusage[] =
284*86d7f5d3SJohn Marino 		"\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 ...";
285*86d7f5d3SJohn Marino 
286*86d7f5d3SJohn Marino 	char *a, **newargv, *textfile;
287*86d7f5d3SJohn Marino 	char const *branchsym, *commsyml;
288*86d7f5d3SJohn Marino 	int branchflag, changed, expmode, initflag;
289*86d7f5d3SJohn Marino 	int strictlock, strict_selected, textflag;
290*86d7f5d3SJohn Marino 	int keepRCStime, Ttimeflag;
291*86d7f5d3SJohn Marino 	size_t commsymlen;
292*86d7f5d3SJohn Marino 	struct buf branchnum;
293*86d7f5d3SJohn Marino 	struct Lockrev *lockpt;
294*86d7f5d3SJohn Marino 	struct Lockrev **curlock, **rmvlock;
295*86d7f5d3SJohn Marino         struct  Status  * curstate;
296*86d7f5d3SJohn Marino 
297*86d7f5d3SJohn Marino 	nosetid();
298*86d7f5d3SJohn Marino 
299*86d7f5d3SJohn Marino 	nextassoc = &assoclst;
300*86d7f5d3SJohn Marino 	nextchaccess = &chaccess;
301*86d7f5d3SJohn Marino 	nextmessage = &messagelst;
302*86d7f5d3SJohn Marino 	nextstate = &statelst;
303*86d7f5d3SJohn Marino 	branchsym = commsyml = textfile = 0;
304*86d7f5d3SJohn Marino 	branchflag = strictlock = false;
305*86d7f5d3SJohn Marino 	bufautobegin(&branchnum);
306*86d7f5d3SJohn Marino 	commsymlen = 0;
307*86d7f5d3SJohn Marino 	curlock = &newlocklst;
308*86d7f5d3SJohn Marino 	rmvlock = &rmvlocklst;
309*86d7f5d3SJohn Marino 	expmode = -1;
310*86d7f5d3SJohn Marino 	suffixes = X_DEFAULT;
311*86d7f5d3SJohn Marino         initflag= textflag = false;
312*86d7f5d3SJohn Marino         strict_selected = 0;
313*86d7f5d3SJohn Marino 	Ttimeflag = false;
314*86d7f5d3SJohn Marino 
315*86d7f5d3SJohn Marino         /*  preprocessing command options    */
316*86d7f5d3SJohn Marino 	if (1 < argc  &&  argv[1][0] != '-')
317*86d7f5d3SJohn Marino 		warn("No options were given; this usage is obsolescent.");
318*86d7f5d3SJohn Marino 
319*86d7f5d3SJohn Marino 	argc = getRCSINIT(argc, argv, &newargv);
320*86d7f5d3SJohn Marino 	argv = newargv;
321*86d7f5d3SJohn Marino 	while (a = *++argv,  0<--argc && *a++=='-') {
322*86d7f5d3SJohn Marino 		switch (*a++) {
323*86d7f5d3SJohn Marino 
324*86d7f5d3SJohn Marino 		case 'i':   /*  initial version  */
325*86d7f5d3SJohn Marino                         initflag = true;
326*86d7f5d3SJohn Marino                         break;
327*86d7f5d3SJohn Marino 
328*86d7f5d3SJohn Marino                 case 'b':  /* change default branch */
329*86d7f5d3SJohn Marino 			if (branchflag) redefined('b');
330*86d7f5d3SJohn Marino                         branchflag= true;
331*86d7f5d3SJohn Marino 			branchsym = a;
332*86d7f5d3SJohn Marino                         break;
333*86d7f5d3SJohn Marino 
334*86d7f5d3SJohn Marino                 case 'c':   /*  change comment symbol   */
335*86d7f5d3SJohn Marino 			if (commsyml) redefined('c');
336*86d7f5d3SJohn Marino 			commsyml = a;
337*86d7f5d3SJohn Marino 			commsymlen = strlen(a);
338*86d7f5d3SJohn Marino                         break;
339*86d7f5d3SJohn Marino 
340*86d7f5d3SJohn Marino                 case 'a':  /*  add new accessor   */
341*86d7f5d3SJohn Marino 			getaccessor(*argv+1, append);
342*86d7f5d3SJohn Marino                         break;
343*86d7f5d3SJohn Marino 
344*86d7f5d3SJohn Marino                 case 'A':  /*  append access list according to accessfile  */
345*86d7f5d3SJohn Marino 			if (!*a) {
346*86d7f5d3SJohn Marino 			    error("missing pathname after -A");
347*86d7f5d3SJohn Marino                             break;
348*86d7f5d3SJohn Marino                         }
349*86d7f5d3SJohn Marino 			*argv = a;
350*86d7f5d3SJohn Marino 			if (0 < pairnames(1,argv,rcsreadopen,true,false)) {
351*86d7f5d3SJohn Marino 			    while (AccessList) {
352*86d7f5d3SJohn Marino 				getchaccess(str_save(AccessList->login),append);
353*86d7f5d3SJohn Marino 				AccessList = AccessList->nextaccess;
354*86d7f5d3SJohn Marino 			    }
355*86d7f5d3SJohn Marino 			    Izclose(&finptr);
356*86d7f5d3SJohn Marino                         }
357*86d7f5d3SJohn Marino                         break;
358*86d7f5d3SJohn Marino 
359*86d7f5d3SJohn Marino                 case 'e':    /*  remove accessors   */
360*86d7f5d3SJohn Marino 			getaccessor(*argv+1, erase);
361*86d7f5d3SJohn Marino                         break;
362*86d7f5d3SJohn Marino 
363*86d7f5d3SJohn Marino                 case 'l':    /*   lock a revision if it is unlocked   */
364*86d7f5d3SJohn Marino 			if (!*a) {
365*86d7f5d3SJohn Marino 			    /* Lock head or default branch.  */
366*86d7f5d3SJohn Marino                             lockhead = true;
367*86d7f5d3SJohn Marino                             break;
368*86d7f5d3SJohn Marino                         }
369*86d7f5d3SJohn Marino 			*curlock = lockpt = talloc(struct Lockrev);
370*86d7f5d3SJohn Marino 			lockpt->revno = a;
371*86d7f5d3SJohn Marino 			lockpt->nextrev = 0;
372*86d7f5d3SJohn Marino 			curlock = &lockpt->nextrev;
373*86d7f5d3SJohn Marino                         break;
374*86d7f5d3SJohn Marino 
375*86d7f5d3SJohn Marino                 case 'u':   /*  release lock of a locked revision   */
376*86d7f5d3SJohn Marino 			if (!*a) {
377*86d7f5d3SJohn Marino                             unlockcaller=true;
378*86d7f5d3SJohn Marino                             break;
379*86d7f5d3SJohn Marino                         }
380*86d7f5d3SJohn Marino 			*rmvlock = lockpt = talloc(struct Lockrev);
381*86d7f5d3SJohn Marino 			lockpt->revno = a;
382*86d7f5d3SJohn Marino 			lockpt->nextrev = 0;
383*86d7f5d3SJohn Marino 			rmvlock = &lockpt->nextrev;
384*86d7f5d3SJohn Marino 			curlock = rmnewlocklst(lockpt->revno);
385*86d7f5d3SJohn Marino                         break;
386*86d7f5d3SJohn Marino 
387*86d7f5d3SJohn Marino                 case 'L':   /*  set strict locking */
388*86d7f5d3SJohn Marino 			if (strict_selected) {
389*86d7f5d3SJohn Marino 			   if (!strictlock)	  /* Already selected -U? */
390*86d7f5d3SJohn Marino 			       warn("-U overridden by -L");
391*86d7f5d3SJohn Marino                         }
392*86d7f5d3SJohn Marino                         strictlock = true;
393*86d7f5d3SJohn Marino 			strict_selected = true;
394*86d7f5d3SJohn Marino                         break;
395*86d7f5d3SJohn Marino 
396*86d7f5d3SJohn Marino                 case 'U':   /*  release strict locking */
397*86d7f5d3SJohn Marino 			if (strict_selected) {
398*86d7f5d3SJohn Marino 			   if (strictlock)	  /* Already selected -L? */
399*86d7f5d3SJohn Marino 			       warn("-L overridden by -U");
400*86d7f5d3SJohn Marino                         }
401*86d7f5d3SJohn Marino 			strict_selected = true;
402*86d7f5d3SJohn Marino                         break;
403*86d7f5d3SJohn Marino 
404*86d7f5d3SJohn Marino                 case 'n':    /*  add new association: error, if name exists */
405*86d7f5d3SJohn Marino 			if (!*a) {
406*86d7f5d3SJohn Marino 			    error("missing symbolic name after -n");
407*86d7f5d3SJohn Marino                             break;
408*86d7f5d3SJohn Marino                         }
409*86d7f5d3SJohn Marino                         getassoclst(false, (*argv)+1);
410*86d7f5d3SJohn Marino                         break;
411*86d7f5d3SJohn Marino 
412*86d7f5d3SJohn Marino                 case 'N':   /*  add or change association   */
413*86d7f5d3SJohn Marino 			if (!*a) {
414*86d7f5d3SJohn Marino 			    error("missing symbolic name after -N");
415*86d7f5d3SJohn Marino                             break;
416*86d7f5d3SJohn Marino                         }
417*86d7f5d3SJohn Marino                         getassoclst(true, (*argv)+1);
418*86d7f5d3SJohn Marino                         break;
419*86d7f5d3SJohn Marino 
420*86d7f5d3SJohn Marino 		case 'm':   /*  change log message  */
421*86d7f5d3SJohn Marino 			getmessage(a);
422*86d7f5d3SJohn Marino 			break;
423*86d7f5d3SJohn Marino 
424*86d7f5d3SJohn Marino 		case 'M':   /*  do not send mail */
425*86d7f5d3SJohn Marino 			suppress_mail = true;
426*86d7f5d3SJohn Marino 			break;
427*86d7f5d3SJohn Marino 
428*86d7f5d3SJohn Marino 		case 'o':   /*  delete revisions  */
429*86d7f5d3SJohn Marino 			if (delrev.strt) redefined('o');
430*86d7f5d3SJohn Marino 			if (!*a) {
431*86d7f5d3SJohn Marino 			    error("missing revision range after -o");
432*86d7f5d3SJohn Marino                             break;
433*86d7f5d3SJohn Marino                         }
434*86d7f5d3SJohn Marino                         getdelrev( (*argv)+1 );
435*86d7f5d3SJohn Marino                         break;
436*86d7f5d3SJohn Marino 
437*86d7f5d3SJohn Marino                 case 's':   /*  change state attribute of a revision  */
438*86d7f5d3SJohn Marino 			if (!*a) {
439*86d7f5d3SJohn Marino 			    error("state missing after -s");
440*86d7f5d3SJohn Marino                             break;
441*86d7f5d3SJohn Marino                         }
442*86d7f5d3SJohn Marino                         getstates( (*argv)+1);
443*86d7f5d3SJohn Marino                         break;
444*86d7f5d3SJohn Marino 
445*86d7f5d3SJohn Marino                 case 't':   /*  change descriptive text   */
446*86d7f5d3SJohn Marino                         textflag=true;
447*86d7f5d3SJohn Marino 			if (*a) {
448*86d7f5d3SJohn Marino 				if (textfile) redefined('t');
449*86d7f5d3SJohn Marino 				textfile = a;
450*86d7f5d3SJohn Marino                         }
451*86d7f5d3SJohn Marino                         break;
452*86d7f5d3SJohn Marino 
453*86d7f5d3SJohn Marino 		case 'T':  /*  do not update last-mod time for minor changes */
454*86d7f5d3SJohn Marino 			if (*a)
455*86d7f5d3SJohn Marino 				goto unknown;
456*86d7f5d3SJohn Marino 			Ttimeflag = true;
457*86d7f5d3SJohn Marino 			break;
458*86d7f5d3SJohn Marino 
459*86d7f5d3SJohn Marino 		case 'I':
460*86d7f5d3SJohn Marino 			interactiveflag = true;
461*86d7f5d3SJohn Marino 			break;
462*86d7f5d3SJohn Marino 
463*86d7f5d3SJohn Marino                 case 'q':
464*86d7f5d3SJohn Marino                         quietflag = true;
465*86d7f5d3SJohn Marino                         break;
466*86d7f5d3SJohn Marino 
467*86d7f5d3SJohn Marino 		case 'x':
468*86d7f5d3SJohn Marino 			suffixes = a;
469*86d7f5d3SJohn Marino 			break;
470*86d7f5d3SJohn Marino 
471*86d7f5d3SJohn Marino 		case 'V':
472*86d7f5d3SJohn Marino 			setRCSversion(*argv);
473*86d7f5d3SJohn Marino 			break;
474*86d7f5d3SJohn Marino 
475*86d7f5d3SJohn Marino 		case 'z':
476*86d7f5d3SJohn Marino 			zone_set(a);
477*86d7f5d3SJohn Marino 			break;
478*86d7f5d3SJohn Marino 
479*86d7f5d3SJohn Marino 		case 'k':    /*  set keyword expand mode  */
480*86d7f5d3SJohn Marino 			if (0 <= expmode) redefined('k');
481*86d7f5d3SJohn Marino 			if (0 <= (expmode = str2expmode(a)))
482*86d7f5d3SJohn Marino 			    break;
483*86d7f5d3SJohn Marino 			/* fall into */
484*86d7f5d3SJohn Marino                 default:
485*86d7f5d3SJohn Marino 		unknown:
486*86d7f5d3SJohn Marino 			error("unknown option: %s%s", *argv, cmdusage);
487*86d7f5d3SJohn Marino                 };
488*86d7f5d3SJohn Marino         }  /* end processing of options */
489*86d7f5d3SJohn Marino 
490*86d7f5d3SJohn Marino 	/* Now handle all pathnames.  */
491*86d7f5d3SJohn Marino 	if (nerror) cleanup();
492*86d7f5d3SJohn Marino 	else if (argc < 1) faterror("no input file%s", cmdusage);
493*86d7f5d3SJohn Marino 	else for (;  0 < argc;  cleanup(), ++argv, --argc) {
494*86d7f5d3SJohn Marino 
495*86d7f5d3SJohn Marino 	ffree();
496*86d7f5d3SJohn Marino 
497*86d7f5d3SJohn Marino         if ( initflag ) {
498*86d7f5d3SJohn Marino 	    switch (pairnames(argc, argv, rcswriteopen, false, false)) {
499*86d7f5d3SJohn Marino                 case -1: break;        /*  not exist; ok */
500*86d7f5d3SJohn Marino                 case  0: continue;     /*  error         */
501*86d7f5d3SJohn Marino 		case  1: rcserror("already exists");
502*86d7f5d3SJohn Marino                          continue;
503*86d7f5d3SJohn Marino             }
504*86d7f5d3SJohn Marino 	}
505*86d7f5d3SJohn Marino         else  {
506*86d7f5d3SJohn Marino 	    switch (pairnames(argc, argv, rcswriteopen, true, false)) {
507*86d7f5d3SJohn Marino                 case -1: continue;    /*  not exist      */
508*86d7f5d3SJohn Marino                 case  0: continue;    /*  errors         */
509*86d7f5d3SJohn Marino                 case  1: break;       /*  file exists; ok*/
510*86d7f5d3SJohn Marino             }
511*86d7f5d3SJohn Marino 	}
512*86d7f5d3SJohn Marino 
513*86d7f5d3SJohn Marino 
514*86d7f5d3SJohn Marino 	/*
515*86d7f5d3SJohn Marino 	 * RCSname contains the name of the RCS file, and
516*86d7f5d3SJohn Marino 	 * workname contains the name of the working file.
517*86d7f5d3SJohn Marino          * if !initflag, finptr contains the file descriptor for the
518*86d7f5d3SJohn Marino          * RCS file. The admin node is initialized.
519*86d7f5d3SJohn Marino          */
520*86d7f5d3SJohn Marino 
521*86d7f5d3SJohn Marino 	diagnose("RCS file: %s\n", RCSname);
522*86d7f5d3SJohn Marino 
523*86d7f5d3SJohn Marino 	changed = initflag | textflag;
524*86d7f5d3SJohn Marino 	keepRCStime = Ttimeflag;
525*86d7f5d3SJohn Marino 	if (!initflag) {
526*86d7f5d3SJohn Marino 		if (!checkaccesslist()) continue;
527*86d7f5d3SJohn Marino 		gettree(); /* Read the delta tree.  */
528*86d7f5d3SJohn Marino 	}
529*86d7f5d3SJohn Marino 
530*86d7f5d3SJohn Marino         /*  update admin. node    */
531*86d7f5d3SJohn Marino 	if (strict_selected) {
532*86d7f5d3SJohn Marino 		changed  |=  StrictLocks ^ strictlock;
533*86d7f5d3SJohn Marino 		StrictLocks = strictlock;
534*86d7f5d3SJohn Marino 	}
535*86d7f5d3SJohn Marino 	if (
536*86d7f5d3SJohn Marino 	    commsyml &&
537*86d7f5d3SJohn Marino 	    (
538*86d7f5d3SJohn Marino 		commsymlen != Comment.size ||
539*86d7f5d3SJohn Marino 		memcmp(commsyml, Comment.string, commsymlen) != 0
540*86d7f5d3SJohn Marino 	    )
541*86d7f5d3SJohn Marino 	) {
542*86d7f5d3SJohn Marino 		Comment.string = commsyml;
543*86d7f5d3SJohn Marino 		Comment.size = strlen(commsyml);
544*86d7f5d3SJohn Marino 		changed = true;
545*86d7f5d3SJohn Marino 	}
546*86d7f5d3SJohn Marino 	if (0 <= expmode  &&  Expand != expmode) {
547*86d7f5d3SJohn Marino 		Expand = expmode;
548*86d7f5d3SJohn Marino 		changed = true;
549*86d7f5d3SJohn Marino 	}
550*86d7f5d3SJohn Marino 
551*86d7f5d3SJohn Marino         /* update default branch */
552*86d7f5d3SJohn Marino 	if (branchflag && expandsym(branchsym, &branchnum)) {
553*86d7f5d3SJohn Marino 	    if (countnumflds(branchnum.string)) {
554*86d7f5d3SJohn Marino 		if (cmpnum(Dbranch, branchnum.string) != 0) {
555*86d7f5d3SJohn Marino 			Dbranch = branchnum.string;
556*86d7f5d3SJohn Marino 			changed = true;
557*86d7f5d3SJohn Marino 		}
558*86d7f5d3SJohn Marino             } else
559*86d7f5d3SJohn Marino 		if (Dbranch) {
560*86d7f5d3SJohn Marino 			Dbranch = 0;
561*86d7f5d3SJohn Marino 			changed = true;
562*86d7f5d3SJohn Marino 		}
563*86d7f5d3SJohn Marino 	}
564*86d7f5d3SJohn Marino 
565*86d7f5d3SJohn Marino 	changed |= doaccess();	/* Update access list.  */
566*86d7f5d3SJohn Marino 
567*86d7f5d3SJohn Marino 	changed |= doassoc();	/* Update association list.  */
568*86d7f5d3SJohn Marino 
569*86d7f5d3SJohn Marino 	changed |= dolocks();	/* Update locks.  */
570*86d7f5d3SJohn Marino 
571*86d7f5d3SJohn Marino 	changed |= domessages();	/* Update log messages.  */
572*86d7f5d3SJohn Marino 
573*86d7f5d3SJohn Marino         /*  update state attribution  */
574*86d7f5d3SJohn Marino         if (chgheadstate) {
575*86d7f5d3SJohn Marino             /* change state of default branch or head */
576*86d7f5d3SJohn Marino 	    if (!Dbranch) {
577*86d7f5d3SJohn Marino 		if (!Head)
578*86d7f5d3SJohn Marino 		    rcswarn("can't change states in an empty tree");
579*86d7f5d3SJohn Marino 		else if (strcmp(Head->state, headstate) != 0) {
580*86d7f5d3SJohn Marino 		    Head->state = headstate;
581*86d7f5d3SJohn Marino 		    changed = true;
582*86d7f5d3SJohn Marino 		}
583*86d7f5d3SJohn Marino 	    } else
584*86d7f5d3SJohn Marino 		changed |= rcs_setstate(Dbranch,headstate);
585*86d7f5d3SJohn Marino         }
586*86d7f5d3SJohn Marino 	for (curstate = statelst;  curstate;  curstate = curstate->nextstatus)
587*86d7f5d3SJohn Marino 	    changed |= rcs_setstate(curstate->revno,curstate->status);
588*86d7f5d3SJohn Marino 
589*86d7f5d3SJohn Marino 	cuthead = cuttail = 0;
590*86d7f5d3SJohn Marino 	if (delrev.strt && removerevs()) {
591*86d7f5d3SJohn Marino             /*  rebuild delta tree if some deltas are deleted   */
592*86d7f5d3SJohn Marino             if ( cuttail )
593*86d7f5d3SJohn Marino 		VOID genrevs(
594*86d7f5d3SJohn Marino 			cuttail->num, (char *)0, (char *)0, (char *)0,
595*86d7f5d3SJohn Marino 			&gendeltas
596*86d7f5d3SJohn Marino 		);
597*86d7f5d3SJohn Marino             buildtree();
598*86d7f5d3SJohn Marino 	    changed = true;
599*86d7f5d3SJohn Marino 	    keepRCStime = false;
600*86d7f5d3SJohn Marino         }
601*86d7f5d3SJohn Marino 
602*86d7f5d3SJohn Marino 	if (nerror)
603*86d7f5d3SJohn Marino 		continue;
604*86d7f5d3SJohn Marino 
605*86d7f5d3SJohn Marino 	putadmin();
606*86d7f5d3SJohn Marino         if ( Head )
607*86d7f5d3SJohn Marino            puttree(Head, frewrite);
608*86d7f5d3SJohn Marino 	putdesc(textflag,textfile);
609*86d7f5d3SJohn Marino 
610*86d7f5d3SJohn Marino         if ( Head) {
611*86d7f5d3SJohn Marino 	    if (delrev.strt || messagelst) {
612*86d7f5d3SJohn Marino 		if (!cuttail || buildeltatext(gendeltas)) {
613*86d7f5d3SJohn Marino 		    advise_access(finptr, MADV_SEQUENTIAL);
614*86d7f5d3SJohn Marino 		    scanlogtext((struct hshentry *)0, false);
615*86d7f5d3SJohn Marino                     /* copy rest of delta text nodes that are not deleted      */
616*86d7f5d3SJohn Marino 		    changed = true;
617*86d7f5d3SJohn Marino 		}
618*86d7f5d3SJohn Marino             }
619*86d7f5d3SJohn Marino         }
620*86d7f5d3SJohn Marino 
621*86d7f5d3SJohn Marino 	if (initflag) {
622*86d7f5d3SJohn Marino 		/* Adjust things for donerewrite's sake.  */
623*86d7f5d3SJohn Marino 		if (stat(workname, &RCSstat) != 0) {
624*86d7f5d3SJohn Marino #		    if bad_creat0
625*86d7f5d3SJohn Marino 			mode_t m = umask(0);
626*86d7f5d3SJohn Marino 			(void) umask(m);
627*86d7f5d3SJohn Marino 			RCSstat.st_mode = (S_IRUSR|S_IRGRP|S_IROTH) & ~m;
628*86d7f5d3SJohn Marino #		    else
629*86d7f5d3SJohn Marino 			changed = -1;
630*86d7f5d3SJohn Marino #		    endif
631*86d7f5d3SJohn Marino 		}
632*86d7f5d3SJohn Marino 		RCSstat.st_nlink = 0;
633*86d7f5d3SJohn Marino 		keepRCStime = false;
634*86d7f5d3SJohn Marino 	}
635*86d7f5d3SJohn Marino 	if (donerewrite(changed,
636*86d7f5d3SJohn Marino 		keepRCStime ? RCSstat.st_mtime : (time_t)-1
637*86d7f5d3SJohn Marino 	) != 0)
638*86d7f5d3SJohn Marino 	    break;
639*86d7f5d3SJohn Marino 
640*86d7f5d3SJohn Marino 	diagnose("done\n");
641*86d7f5d3SJohn Marino 	}
642*86d7f5d3SJohn Marino 
643*86d7f5d3SJohn Marino 	tempunlink();
644*86d7f5d3SJohn Marino 	exitmain(exitstatus);
645*86d7f5d3SJohn Marino }       /* end of main (rcs) */
646*86d7f5d3SJohn Marino 
647*86d7f5d3SJohn Marino 	static void
cleanup()648*86d7f5d3SJohn Marino cleanup()
649*86d7f5d3SJohn Marino {
650*86d7f5d3SJohn Marino 	if (nerror) exitstatus = EXIT_FAILURE;
651*86d7f5d3SJohn Marino 	Izclose(&finptr);
652*86d7f5d3SJohn Marino 	Ozclose(&fcopy);
653*86d7f5d3SJohn Marino 	ORCSclose();
654*86d7f5d3SJohn Marino 	dirtempunlink();
655*86d7f5d3SJohn Marino }
656*86d7f5d3SJohn Marino 
657*86d7f5d3SJohn Marino 	void
exiterr()658*86d7f5d3SJohn Marino exiterr()
659*86d7f5d3SJohn Marino {
660*86d7f5d3SJohn Marino 	ORCSerror();
661*86d7f5d3SJohn Marino 	dirtempunlink();
662*86d7f5d3SJohn Marino 	tempunlink();
663*86d7f5d3SJohn Marino 	_exit(EXIT_FAILURE);
664*86d7f5d3SJohn Marino }
665*86d7f5d3SJohn Marino 
666*86d7f5d3SJohn Marino 
667*86d7f5d3SJohn Marino 	static void
getassoclst(flag,sp)668*86d7f5d3SJohn Marino getassoclst(flag, sp)
669*86d7f5d3SJohn Marino int     flag;
670*86d7f5d3SJohn Marino char    * sp;
671*86d7f5d3SJohn Marino /*  Function:   associate a symbolic name to a revision or branch,      */
672*86d7f5d3SJohn Marino /*              and store in assoclst                                   */
673*86d7f5d3SJohn Marino 
674*86d7f5d3SJohn Marino {
675*86d7f5d3SJohn Marino         struct   Symrev  * pt;
676*86d7f5d3SJohn Marino 	char const *temp;
677*86d7f5d3SJohn Marino         int                c;
678*86d7f5d3SJohn Marino 
679*86d7f5d3SJohn Marino 	while ((c = *++sp) == ' ' || c == '\t' || c =='\n')
680*86d7f5d3SJohn Marino 	    continue;
681*86d7f5d3SJohn Marino         temp = sp;
682*86d7f5d3SJohn Marino 	sp = checksym(sp, ':');  /*  check for invalid symbolic name  */
683*86d7f5d3SJohn Marino 	c = *sp;   *sp = '\0';
684*86d7f5d3SJohn Marino         while( c == ' ' || c == '\t' || c == '\n')  c = *++sp;
685*86d7f5d3SJohn Marino 
686*86d7f5d3SJohn Marino         if ( c != ':' && c != '\0') {
687*86d7f5d3SJohn Marino 	    error("invalid string %s after option -n or -N",sp);
688*86d7f5d3SJohn Marino             return;
689*86d7f5d3SJohn Marino         }
690*86d7f5d3SJohn Marino 
691*86d7f5d3SJohn Marino 	pt = talloc(struct Symrev);
692*86d7f5d3SJohn Marino         pt->ssymbol = temp;
693*86d7f5d3SJohn Marino         pt->override = flag;
694*86d7f5d3SJohn Marino 	if (c == '\0')  /*  delete symbol  */
695*86d7f5d3SJohn Marino 	    pt->revno = 0;
696*86d7f5d3SJohn Marino         else {
697*86d7f5d3SJohn Marino 	    while ((c = *++sp) == ' ' || c == '\n' || c == '\t')
698*86d7f5d3SJohn Marino 		continue;
699*86d7f5d3SJohn Marino 	    pt->revno = sp;
700*86d7f5d3SJohn Marino         }
701*86d7f5d3SJohn Marino 	pt->nextsym = 0;
702*86d7f5d3SJohn Marino 	*nextassoc = pt;
703*86d7f5d3SJohn Marino 	nextassoc = &pt->nextsym;
704*86d7f5d3SJohn Marino }
705*86d7f5d3SJohn Marino 
706*86d7f5d3SJohn Marino 
707*86d7f5d3SJohn Marino 	static void
getchaccess(login,command)708*86d7f5d3SJohn Marino getchaccess(login, command)
709*86d7f5d3SJohn Marino 	char const *login;
710*86d7f5d3SJohn Marino 	enum changeaccess command;
711*86d7f5d3SJohn Marino {
712*86d7f5d3SJohn Marino 	register struct chaccess *pt;
713*86d7f5d3SJohn Marino 
714*86d7f5d3SJohn Marino 	pt = talloc(struct chaccess);
715*86d7f5d3SJohn Marino 	pt->login = login;
716*86d7f5d3SJohn Marino 	pt->command = command;
717*86d7f5d3SJohn Marino 	pt->nextchaccess = 0;
718*86d7f5d3SJohn Marino 	*nextchaccess = pt;
719*86d7f5d3SJohn Marino 	nextchaccess = &pt->nextchaccess;
720*86d7f5d3SJohn Marino }
721*86d7f5d3SJohn Marino 
722*86d7f5d3SJohn Marino 
723*86d7f5d3SJohn Marino 
724*86d7f5d3SJohn Marino 	static void
getaccessor(opt,command)725*86d7f5d3SJohn Marino getaccessor(opt, command)
726*86d7f5d3SJohn Marino 	char *opt;
727*86d7f5d3SJohn Marino 	enum changeaccess command;
728*86d7f5d3SJohn Marino /*   Function:  get the accessor list of options -e and -a,     */
729*86d7f5d3SJohn Marino /*		and store in chaccess				*/
730*86d7f5d3SJohn Marino 
731*86d7f5d3SJohn Marino 
732*86d7f5d3SJohn Marino {
733*86d7f5d3SJohn Marino         register int c;
734*86d7f5d3SJohn Marino 	register char *sp;
735*86d7f5d3SJohn Marino 
736*86d7f5d3SJohn Marino 	sp = opt;
737*86d7f5d3SJohn Marino 	while ((c = *++sp) == ' ' || c == '\n' || c == '\t' || c == ',')
738*86d7f5d3SJohn Marino 	    continue;
739*86d7f5d3SJohn Marino         if ( c == '\0') {
740*86d7f5d3SJohn Marino 	    if (command == erase  &&  sp-opt == 1) {
741*86d7f5d3SJohn Marino 		getchaccess((char*)0, command);
742*86d7f5d3SJohn Marino 		return;
743*86d7f5d3SJohn Marino 	    }
744*86d7f5d3SJohn Marino 	    error("missing login name after option -a or -e");
745*86d7f5d3SJohn Marino 	    return;
746*86d7f5d3SJohn Marino         }
747*86d7f5d3SJohn Marino 
748*86d7f5d3SJohn Marino         while( c != '\0') {
749*86d7f5d3SJohn Marino 		getchaccess(sp, command);
750*86d7f5d3SJohn Marino 		sp = checkid(sp,',');
751*86d7f5d3SJohn Marino 		c = *sp;   *sp = '\0';
752*86d7f5d3SJohn Marino                 while( c == ' ' || c == '\n' || c == '\t'|| c == ',')c =(*++sp);
753*86d7f5d3SJohn Marino         }
754*86d7f5d3SJohn Marino }
755*86d7f5d3SJohn Marino 
756*86d7f5d3SJohn Marino 
757*86d7f5d3SJohn Marino 	static void
getmessage(option)758*86d7f5d3SJohn Marino getmessage(option)
759*86d7f5d3SJohn Marino 	char *option;
760*86d7f5d3SJohn Marino {
761*86d7f5d3SJohn Marino 	struct Message *pt;
762*86d7f5d3SJohn Marino 	struct cbuf cb;
763*86d7f5d3SJohn Marino 	char *m;
764*86d7f5d3SJohn Marino 
765*86d7f5d3SJohn Marino 	if (!(m = strchr(option, ':'))) {
766*86d7f5d3SJohn Marino 		error("-m option lacks revision number");
767*86d7f5d3SJohn Marino 		return;
768*86d7f5d3SJohn Marino 	}
769*86d7f5d3SJohn Marino 	*m++ = 0;
770*86d7f5d3SJohn Marino 	cb = cleanlogmsg(m, strlen(m));
771*86d7f5d3SJohn Marino 	if (!cb.size) {
772*86d7f5d3SJohn Marino 		error("-m option lacks log message");
773*86d7f5d3SJohn Marino 		return;
774*86d7f5d3SJohn Marino 	}
775*86d7f5d3SJohn Marino 	pt = talloc(struct Message);
776*86d7f5d3SJohn Marino 	pt->revno = option;
777*86d7f5d3SJohn Marino 	pt->message = cb;
778*86d7f5d3SJohn Marino 	pt->nextmessage = 0;
779*86d7f5d3SJohn Marino 	*nextmessage = pt;
780*86d7f5d3SJohn Marino 	nextmessage = &pt->nextmessage;
781*86d7f5d3SJohn Marino }
782*86d7f5d3SJohn Marino 
783*86d7f5d3SJohn Marino 
784*86d7f5d3SJohn Marino 	static void
getstates(sp)785*86d7f5d3SJohn Marino getstates(sp)
786*86d7f5d3SJohn Marino char    *sp;
787*86d7f5d3SJohn Marino /*   Function:  get one state attribute and the corresponding   */
788*86d7f5d3SJohn Marino /*              revision and store in statelst                  */
789*86d7f5d3SJohn Marino 
790*86d7f5d3SJohn Marino {
791*86d7f5d3SJohn Marino 	char const *temp;
792*86d7f5d3SJohn Marino         struct  Status  *pt;
793*86d7f5d3SJohn Marino         register int    c;
794*86d7f5d3SJohn Marino 
795*86d7f5d3SJohn Marino 	while ((c = *++sp) ==' ' || c == '\t' || c == '\n')
796*86d7f5d3SJohn Marino 	    continue;
797*86d7f5d3SJohn Marino         temp = sp;
798*86d7f5d3SJohn Marino 	sp = checkid(sp,':');  /* check for invalid state attribute */
799*86d7f5d3SJohn Marino 	c = *sp;   *sp = '\0';
800*86d7f5d3SJohn Marino         while( c == ' ' || c == '\t' || c == '\n' )  c = *++sp;
801*86d7f5d3SJohn Marino 
802*86d7f5d3SJohn Marino         if ( c == '\0' ) {  /*  change state of def. branch or Head  */
803*86d7f5d3SJohn Marino             chgheadstate = true;
804*86d7f5d3SJohn Marino             headstate  = temp;
805*86d7f5d3SJohn Marino             return;
806*86d7f5d3SJohn Marino         }
807*86d7f5d3SJohn Marino         else if ( c != ':' ) {
808*86d7f5d3SJohn Marino 	    error("missing ':' after state in option -s");
809*86d7f5d3SJohn Marino             return;
810*86d7f5d3SJohn Marino         }
811*86d7f5d3SJohn Marino 
812*86d7f5d3SJohn Marino 	while ((c = *++sp) == ' ' || c == '\t' || c == '\n')
813*86d7f5d3SJohn Marino 	    continue;
814*86d7f5d3SJohn Marino 	pt = talloc(struct Status);
815*86d7f5d3SJohn Marino         pt->status     = temp;
816*86d7f5d3SJohn Marino         pt->revno      = sp;
817*86d7f5d3SJohn Marino 	pt->nextstatus = 0;
818*86d7f5d3SJohn Marino 	*nextstate = pt;
819*86d7f5d3SJohn Marino 	nextstate = &pt->nextstatus;
820*86d7f5d3SJohn Marino }
821*86d7f5d3SJohn Marino 
822*86d7f5d3SJohn Marino 
823*86d7f5d3SJohn Marino 
824*86d7f5d3SJohn Marino 	static void
getdelrev(sp)825*86d7f5d3SJohn Marino getdelrev(sp)
826*86d7f5d3SJohn Marino char    *sp;
827*86d7f5d3SJohn Marino /*   Function:  get revision range or branch to be deleted,     */
828*86d7f5d3SJohn Marino /*              and place in delrev                             */
829*86d7f5d3SJohn Marino {
830*86d7f5d3SJohn Marino         int    c;
831*86d7f5d3SJohn Marino         struct  delrevpair      *pt;
832*86d7f5d3SJohn Marino 	int separator;
833*86d7f5d3SJohn Marino 
834*86d7f5d3SJohn Marino 	pt = &delrev;
835*86d7f5d3SJohn Marino 	while ((c = (*++sp)) == ' ' || c == '\n' || c == '\t')
836*86d7f5d3SJohn Marino 		continue;
837*86d7f5d3SJohn Marino 
838*86d7f5d3SJohn Marino 	/* Support old ambiguous '-' syntax; this will go away.  */
839*86d7f5d3SJohn Marino 	if (strchr(sp,':'))
840*86d7f5d3SJohn Marino 		separator = ':';
841*86d7f5d3SJohn Marino 	else {
842*86d7f5d3SJohn Marino 		if (strchr(sp,'-')  &&  VERSION(5) <= RCSversion)
843*86d7f5d3SJohn Marino 		    warn("`-' is obsolete in `-o%s'; use `:' instead", sp);
844*86d7f5d3SJohn Marino 		separator = '-';
845*86d7f5d3SJohn Marino 	}
846*86d7f5d3SJohn Marino 
847*86d7f5d3SJohn Marino 	if (c == separator) { /* -o:rev */
848*86d7f5d3SJohn Marino 	    while ((c = (*++sp)) == ' ' || c == '\n' || c == '\t')
849*86d7f5d3SJohn Marino 		continue;
850*86d7f5d3SJohn Marino             pt->strt = sp;    pt->code = 1;
851*86d7f5d3SJohn Marino             while( c != ' ' && c != '\n' && c != '\t' && c != '\0') c =(*++sp);
852*86d7f5d3SJohn Marino             *sp = '\0';
853*86d7f5d3SJohn Marino 	    pt->end = 0;
854*86d7f5d3SJohn Marino             return;
855*86d7f5d3SJohn Marino         }
856*86d7f5d3SJohn Marino         else {
857*86d7f5d3SJohn Marino             pt->strt = sp;
858*86d7f5d3SJohn Marino             while( c != ' ' && c != '\n' && c != '\t' && c != '\0'
859*86d7f5d3SJohn Marino 		   && c != separator )  c = *++sp;
860*86d7f5d3SJohn Marino             *sp = '\0';
861*86d7f5d3SJohn Marino             while( c == ' ' || c == '\n' || c == '\t' )  c = *++sp;
862*86d7f5d3SJohn Marino             if ( c == '\0' )  {  /*   -o rev or branch   */
863*86d7f5d3SJohn Marino 		pt->code = 0;
864*86d7f5d3SJohn Marino 		pt->end = 0;
865*86d7f5d3SJohn Marino                 return;
866*86d7f5d3SJohn Marino             }
867*86d7f5d3SJohn Marino 	    if (c != separator) {
868*86d7f5d3SJohn Marino 		error("invalid range %s %s after -o", pt->strt, sp);
869*86d7f5d3SJohn Marino             }
870*86d7f5d3SJohn Marino 	    while ((c = *++sp) == ' ' || c == '\n' || c == '\t')
871*86d7f5d3SJohn Marino 		continue;
872*86d7f5d3SJohn Marino 	    if (!c) {  /* -orev: */
873*86d7f5d3SJohn Marino 		pt->code = 2;
874*86d7f5d3SJohn Marino 		pt->end = 0;
875*86d7f5d3SJohn Marino                 return;
876*86d7f5d3SJohn Marino             }
877*86d7f5d3SJohn Marino         }
878*86d7f5d3SJohn Marino 	/* -orev1:rev2 */
879*86d7f5d3SJohn Marino 	pt->end = sp;  pt->code = 3;
880*86d7f5d3SJohn Marino         while( c!= ' ' && c != '\n' && c != '\t' && c != '\0') c = *++sp;
881*86d7f5d3SJohn Marino         *sp = '\0';
882*86d7f5d3SJohn Marino }
883*86d7f5d3SJohn Marino 
884*86d7f5d3SJohn Marino 
885*86d7f5d3SJohn Marino 
886*86d7f5d3SJohn Marino 
887*86d7f5d3SJohn Marino 	static void
scanlogtext(delta,edit)888*86d7f5d3SJohn Marino scanlogtext(delta,edit)
889*86d7f5d3SJohn Marino 	struct hshentry *delta;
890*86d7f5d3SJohn Marino 	int edit;
891*86d7f5d3SJohn Marino /* Function: Scans delta text nodes up to and including the one given
892*86d7f5d3SJohn Marino  * by delta, or up to last one present, if !delta.
893*86d7f5d3SJohn Marino  * For the one given by delta (if delta), the log message is saved into
894*86d7f5d3SJohn Marino  * delta->log if delta==cuttail; the text is edited if EDIT is set, else copied.
895*86d7f5d3SJohn Marino  * Assumes the initial lexeme must be read in first.
896*86d7f5d3SJohn Marino  * Does not advance nexttok after it is finished, except if !delta.
897*86d7f5d3SJohn Marino  */
898*86d7f5d3SJohn Marino {
899*86d7f5d3SJohn Marino 	struct hshentry const *nextdelta;
900*86d7f5d3SJohn Marino 	struct cbuf cb;
901*86d7f5d3SJohn Marino 
902*86d7f5d3SJohn Marino 	for (;;) {
903*86d7f5d3SJohn Marino 		foutptr = 0;
904*86d7f5d3SJohn Marino 		if (eoflex()) {
905*86d7f5d3SJohn Marino                     if(delta)
906*86d7f5d3SJohn Marino 			rcsfaterror("can't find delta for revision %s",
907*86d7f5d3SJohn Marino 				delta->num
908*86d7f5d3SJohn Marino 			);
909*86d7f5d3SJohn Marino 		    return; /* no more delta text nodes */
910*86d7f5d3SJohn Marino                 }
911*86d7f5d3SJohn Marino 		nextlex();
912*86d7f5d3SJohn Marino 		if (!(nextdelta=getnum()))
913*86d7f5d3SJohn Marino 			fatserror("delta number corrupted");
914*86d7f5d3SJohn Marino 		if (nextdelta->selector) {
915*86d7f5d3SJohn Marino 			foutptr = frewrite;
916*86d7f5d3SJohn Marino 			aprintf(frewrite,DELNUMFORM,nextdelta->num,Klog);
917*86d7f5d3SJohn Marino                 }
918*86d7f5d3SJohn Marino 		getkeystring(Klog);
919*86d7f5d3SJohn Marino 		if (nextdelta == cuttail) {
920*86d7f5d3SJohn Marino 			cb = savestring(&curlogbuf);
921*86d7f5d3SJohn Marino 			if (!delta->log.string)
922*86d7f5d3SJohn Marino 			    delta->log = cleanlogmsg(curlogbuf.string, cb.size);
923*86d7f5d3SJohn Marino 			nextlex();
924*86d7f5d3SJohn Marino 			delta->igtext = getphrases(Ktext);
925*86d7f5d3SJohn Marino 		} else {
926*86d7f5d3SJohn Marino 			if (nextdelta->log.string && nextdelta->selector) {
927*86d7f5d3SJohn Marino 				foutptr = 0;
928*86d7f5d3SJohn Marino 				readstring();
929*86d7f5d3SJohn Marino 				foutptr = frewrite;
930*86d7f5d3SJohn Marino 				putstring(foutptr, false, nextdelta->log, true);
931*86d7f5d3SJohn Marino 				afputc(nextc, foutptr);
932*86d7f5d3SJohn Marino 			} else
933*86d7f5d3SJohn Marino 				readstring();
934*86d7f5d3SJohn Marino 			ignorephrases(Ktext);
935*86d7f5d3SJohn Marino 		}
936*86d7f5d3SJohn Marino 		getkeystring(Ktext);
937*86d7f5d3SJohn Marino 
938*86d7f5d3SJohn Marino 		if (delta==nextdelta)
939*86d7f5d3SJohn Marino 			break;
940*86d7f5d3SJohn Marino 		readstring(); /* skip over it */
941*86d7f5d3SJohn Marino 
942*86d7f5d3SJohn Marino 	}
943*86d7f5d3SJohn Marino 	/* got the one we're looking for */
944*86d7f5d3SJohn Marino 	if (edit)
945*86d7f5d3SJohn Marino 		editstring((struct hshentry*)0);
946*86d7f5d3SJohn Marino 	else
947*86d7f5d3SJohn Marino 		enterstring();
948*86d7f5d3SJohn Marino }
949*86d7f5d3SJohn Marino 
950*86d7f5d3SJohn Marino 
951*86d7f5d3SJohn Marino 
952*86d7f5d3SJohn Marino 	static struct Lockrev **
rmnewlocklst(which)953*86d7f5d3SJohn Marino rmnewlocklst(which)
954*86d7f5d3SJohn Marino 	char const *which;
955*86d7f5d3SJohn Marino /* Remove lock to revision WHICH from newlocklst.  */
956*86d7f5d3SJohn Marino {
957*86d7f5d3SJohn Marino 	struct Lockrev *pt, **pre;
958*86d7f5d3SJohn Marino 
959*86d7f5d3SJohn Marino 	pre = &newlocklst;
960*86d7f5d3SJohn Marino 	while ((pt = *pre))
961*86d7f5d3SJohn Marino 	    if (strcmp(pt->revno, which) != 0)
962*86d7f5d3SJohn Marino 		pre = &pt->nextrev;
963*86d7f5d3SJohn Marino 	    else {
964*86d7f5d3SJohn Marino 		*pre = pt->nextrev;
965*86d7f5d3SJohn Marino 		tfree(pt);
966*86d7f5d3SJohn Marino 	    }
967*86d7f5d3SJohn Marino         return pre;
968*86d7f5d3SJohn Marino }
969*86d7f5d3SJohn Marino 
970*86d7f5d3SJohn Marino 
971*86d7f5d3SJohn Marino 
972*86d7f5d3SJohn Marino 	static int
doaccess()973*86d7f5d3SJohn Marino doaccess()
974*86d7f5d3SJohn Marino {
975*86d7f5d3SJohn Marino 	register struct chaccess *ch;
976*86d7f5d3SJohn Marino 	register struct access **p, *t;
977*86d7f5d3SJohn Marino 	register int changed = false;
978*86d7f5d3SJohn Marino 
979*86d7f5d3SJohn Marino 	for (ch = chaccess;  ch;  ch = ch->nextchaccess) {
980*86d7f5d3SJohn Marino 		switch (ch->command) {
981*86d7f5d3SJohn Marino 		case erase:
982*86d7f5d3SJohn Marino 			if (!ch->login) {
983*86d7f5d3SJohn Marino 			    if (AccessList) {
984*86d7f5d3SJohn Marino 				AccessList = 0;
985*86d7f5d3SJohn Marino 				changed = true;
986*86d7f5d3SJohn Marino 			    }
987*86d7f5d3SJohn Marino 			} else
988*86d7f5d3SJohn Marino 			    for (p = &AccessList; (t = *p); p = &t->nextaccess)
989*86d7f5d3SJohn Marino 				if (strcmp(ch->login, t->login) == 0) {
990*86d7f5d3SJohn Marino 					*p = t->nextaccess;
991*86d7f5d3SJohn Marino 					changed = true;
992*86d7f5d3SJohn Marino 					break;
993*86d7f5d3SJohn Marino 				}
994*86d7f5d3SJohn Marino 			break;
995*86d7f5d3SJohn Marino 		case append:
996*86d7f5d3SJohn Marino 			for (p = &AccessList;  ;  p = &t->nextaccess)
997*86d7f5d3SJohn Marino 				if (!(t = *p)) {
998*86d7f5d3SJohn Marino 					*p = t = ftalloc(struct access);
999*86d7f5d3SJohn Marino 					t->login = ch->login;
1000*86d7f5d3SJohn Marino 					t->nextaccess = 0;
1001*86d7f5d3SJohn Marino 					changed = true;
1002*86d7f5d3SJohn Marino 					break;
1003*86d7f5d3SJohn Marino 				} else if (strcmp(ch->login, t->login) == 0)
1004*86d7f5d3SJohn Marino 					break;
1005*86d7f5d3SJohn Marino 			break;
1006*86d7f5d3SJohn Marino 		}
1007*86d7f5d3SJohn Marino 	}
1008*86d7f5d3SJohn Marino 	return changed;
1009*86d7f5d3SJohn Marino }
1010*86d7f5d3SJohn Marino 
1011*86d7f5d3SJohn Marino 
1012*86d7f5d3SJohn Marino 	static int
sendmail(Delta,who)1013*86d7f5d3SJohn Marino sendmail(Delta, who)
1014*86d7f5d3SJohn Marino 	char const *Delta, *who;
1015*86d7f5d3SJohn Marino /*   Function:  mail to who, informing him that his lock on delta was
1016*86d7f5d3SJohn Marino  *   broken by caller. Ask first whether to go ahead. Return false on
1017*86d7f5d3SJohn Marino  *   error or if user decides not to break the lock.
1018*86d7f5d3SJohn Marino  */
1019*86d7f5d3SJohn Marino {
1020*86d7f5d3SJohn Marino #ifdef SENDMAIL
1021*86d7f5d3SJohn Marino 	char const *messagefile;
1022*86d7f5d3SJohn Marino 	int old1, old2, c, status;
1023*86d7f5d3SJohn Marino         FILE    * mailmess;
1024*86d7f5d3SJohn Marino #endif
1025*86d7f5d3SJohn Marino 
1026*86d7f5d3SJohn Marino 
1027*86d7f5d3SJohn Marino 	aprintf(stderr, "Revision %s is already locked by %s.\n", Delta, who);
1028*86d7f5d3SJohn Marino 	if (suppress_mail)
1029*86d7f5d3SJohn Marino 		return true;
1030*86d7f5d3SJohn Marino 	if (!yesorno(false, "Do you want to break the lock? [ny](n): "))
1031*86d7f5d3SJohn Marino 		return false;
1032*86d7f5d3SJohn Marino 
1033*86d7f5d3SJohn Marino         /* go ahead with breaking  */
1034*86d7f5d3SJohn Marino #ifdef SENDMAIL
1035*86d7f5d3SJohn Marino 	messagefile = maketemp(0);
1036*86d7f5d3SJohn Marino 	if (!(mailmess = fopenSafer(messagefile, "w+"))) {
1037*86d7f5d3SJohn Marino 	    efaterror(messagefile);
1038*86d7f5d3SJohn Marino         }
1039*86d7f5d3SJohn Marino 
1040*86d7f5d3SJohn Marino 	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",
1041*86d7f5d3SJohn Marino 		basefilename(RCSname), Delta, getfullRCSname(), getcaller()
1042*86d7f5d3SJohn Marino 	);
1043*86d7f5d3SJohn Marino 	aputs("State the reason for breaking the lock:\n(terminate with single '.' or end of file)\n>> ", stderr);
1044*86d7f5d3SJohn Marino 	eflush();
1045*86d7f5d3SJohn Marino 
1046*86d7f5d3SJohn Marino         old1 = '\n';    old2 = ' ';
1047*86d7f5d3SJohn Marino         for (; ;) {
1048*86d7f5d3SJohn Marino 	    c = getcstdin();
1049*86d7f5d3SJohn Marino 	    if (feof(stdin)) {
1050*86d7f5d3SJohn Marino 		aprintf(mailmess, "%c\n", old1);
1051*86d7f5d3SJohn Marino                 break;
1052*86d7f5d3SJohn Marino             }
1053*86d7f5d3SJohn Marino             else if ( c == '\n' && old1 == '.' && old2 == '\n')
1054*86d7f5d3SJohn Marino                 break;
1055*86d7f5d3SJohn Marino             else {
1056*86d7f5d3SJohn Marino 		afputc(old1, mailmess);
1057*86d7f5d3SJohn Marino                 old2 = old1;   old1 = c;
1058*86d7f5d3SJohn Marino 		if (c == '\n') {
1059*86d7f5d3SJohn Marino 		    aputs(">> ", stderr);
1060*86d7f5d3SJohn Marino 		    eflush();
1061*86d7f5d3SJohn Marino 		}
1062*86d7f5d3SJohn Marino             }
1063*86d7f5d3SJohn Marino         }
1064*86d7f5d3SJohn Marino 	Orewind(mailmess);
1065*86d7f5d3SJohn Marino 	aflush(mailmess);
1066*86d7f5d3SJohn Marino 	status = run(fileno(mailmess), (char*)0, SENDMAIL, who, (char*)0);
1067*86d7f5d3SJohn Marino 	Ozclose(&mailmess);
1068*86d7f5d3SJohn Marino 	if (status == 0)
1069*86d7f5d3SJohn Marino 		return true;
1070*86d7f5d3SJohn Marino 	warn("Mail failed.");
1071*86d7f5d3SJohn Marino #endif
1072*86d7f5d3SJohn Marino 	warn("Mail notification of broken locks is not available.");
1073*86d7f5d3SJohn Marino 	warn("Please tell `%s' why you broke the lock.", who);
1074*86d7f5d3SJohn Marino 	return(true);
1075*86d7f5d3SJohn Marino }
1076*86d7f5d3SJohn Marino 
1077*86d7f5d3SJohn Marino 
1078*86d7f5d3SJohn Marino 
1079*86d7f5d3SJohn Marino 	static int
breaklock(delta)1080*86d7f5d3SJohn Marino breaklock(delta)
1081*86d7f5d3SJohn Marino 	struct hshentry const *delta;
1082*86d7f5d3SJohn Marino /* function: Finds the lock held by caller on delta,
1083*86d7f5d3SJohn Marino  * and removes it.
1084*86d7f5d3SJohn Marino  * Sends mail if a lock different from the caller's is broken.
1085*86d7f5d3SJohn Marino  * Prints an error message if there is no such lock or error.
1086*86d7f5d3SJohn Marino  */
1087*86d7f5d3SJohn Marino {
1088*86d7f5d3SJohn Marino 	register struct rcslock *next, **trail;
1089*86d7f5d3SJohn Marino 	char const *num;
1090*86d7f5d3SJohn Marino 
1091*86d7f5d3SJohn Marino 	num=delta->num;
1092*86d7f5d3SJohn Marino 	for (trail = &Locks;  (next = *trail);  trail = &next->nextlock)
1093*86d7f5d3SJohn Marino 		if (strcmp(num, next->delta->num) == 0) {
1094*86d7f5d3SJohn Marino 			if (
1095*86d7f5d3SJohn Marino 				strcmp(getcaller(),next->login) != 0
1096*86d7f5d3SJohn Marino 			    &&	!sendmail(num, next->login)
1097*86d7f5d3SJohn Marino 			) {
1098*86d7f5d3SJohn Marino 			    rcserror("revision %s still locked by %s",
1099*86d7f5d3SJohn Marino 				num, next->login
1100*86d7f5d3SJohn Marino 			    );
1101*86d7f5d3SJohn Marino 			    return false;
1102*86d7f5d3SJohn Marino 			}
1103*86d7f5d3SJohn Marino 			diagnose("%s unlocked\n", next->delta->num);
1104*86d7f5d3SJohn Marino 			*trail = next->nextlock;
1105*86d7f5d3SJohn Marino 			next->delta->lockedby = 0;
1106*86d7f5d3SJohn Marino 			return true;
1107*86d7f5d3SJohn Marino                 }
1108*86d7f5d3SJohn Marino 	rcserror("no lock set on revision %s", num);
1109*86d7f5d3SJohn Marino 	return false;
1110*86d7f5d3SJohn Marino }
1111*86d7f5d3SJohn Marino 
1112*86d7f5d3SJohn Marino 
1113*86d7f5d3SJohn Marino 
1114*86d7f5d3SJohn Marino 	static struct hshentry *
searchcutpt(object,length,store)1115*86d7f5d3SJohn Marino searchcutpt(object, length, store)
1116*86d7f5d3SJohn Marino 	char const *object;
1117*86d7f5d3SJohn Marino 	int length;
1118*86d7f5d3SJohn Marino 	struct hshentries *store;
1119*86d7f5d3SJohn Marino /*   Function:  Search store and return entry with number being object. */
1120*86d7f5d3SJohn Marino /*		cuttail = 0, if the entry is Head; otherwise, cuttail   */
1121*86d7f5d3SJohn Marino /*              is the entry point to the one with number being object  */
1122*86d7f5d3SJohn Marino 
1123*86d7f5d3SJohn Marino {
1124*86d7f5d3SJohn Marino 	cuthead = 0;
1125*86d7f5d3SJohn Marino 	while (compartial(store->first->num, object, length)) {
1126*86d7f5d3SJohn Marino 		cuthead = store->first;
1127*86d7f5d3SJohn Marino 		store = store->rest;
1128*86d7f5d3SJohn Marino 	}
1129*86d7f5d3SJohn Marino 	return store->first;
1130*86d7f5d3SJohn Marino }
1131*86d7f5d3SJohn Marino 
1132*86d7f5d3SJohn Marino 
1133*86d7f5d3SJohn Marino 
1134*86d7f5d3SJohn Marino 	static int
branchpoint(strt,tail)1135*86d7f5d3SJohn Marino branchpoint(strt, tail)
1136*86d7f5d3SJohn Marino struct  hshentry        *strt,  *tail;
1137*86d7f5d3SJohn Marino /*   Function: check whether the deltas between strt and tail	*/
1138*86d7f5d3SJohn Marino /*		are locked or branch point, return 1 if any is  */
1139*86d7f5d3SJohn Marino /*		locked or branch point; otherwise, return 0 and */
1140*86d7f5d3SJohn Marino /*		mark deleted					*/
1141*86d7f5d3SJohn Marino 
1142*86d7f5d3SJohn Marino {
1143*86d7f5d3SJohn Marino         struct  hshentry    *pt;
1144*86d7f5d3SJohn Marino 	struct rcslock const *lockpt;
1145*86d7f5d3SJohn Marino 
1146*86d7f5d3SJohn Marino 	for (pt = strt;  pt != tail;  pt = pt->next) {
1147*86d7f5d3SJohn Marino             if ( pt->branches ){ /*  a branch point  */
1148*86d7f5d3SJohn Marino 		rcserror("can't remove branch point %s", pt->num);
1149*86d7f5d3SJohn Marino 		return true;
1150*86d7f5d3SJohn Marino             }
1151*86d7f5d3SJohn Marino 	    for (lockpt = Locks;  lockpt;  lockpt = lockpt->nextlock)
1152*86d7f5d3SJohn Marino 		if (lockpt->delta == pt) {
1153*86d7f5d3SJohn Marino 		    rcserror("can't remove locked revision %s", pt->num);
1154*86d7f5d3SJohn Marino 		    return true;
1155*86d7f5d3SJohn Marino 		}
1156*86d7f5d3SJohn Marino 	    pt->selector = false;
1157*86d7f5d3SJohn Marino 	    diagnose("deleting revision %s\n",pt->num);
1158*86d7f5d3SJohn Marino         }
1159*86d7f5d3SJohn Marino 	return false;
1160*86d7f5d3SJohn Marino }
1161*86d7f5d3SJohn Marino 
1162*86d7f5d3SJohn Marino 
1163*86d7f5d3SJohn Marino 
1164*86d7f5d3SJohn Marino 	static int
removerevs()1165*86d7f5d3SJohn Marino removerevs()
1166*86d7f5d3SJohn Marino /*   Function:  get the revision range to be removed, and place the     */
1167*86d7f5d3SJohn Marino /*              first revision removed in delstrt, the revision before  */
1168*86d7f5d3SJohn Marino /*		delstrt in cuthead (0, if delstrt is head), and the	*/
1169*86d7f5d3SJohn Marino /*		revision after the last removed revision in cuttail (0	*/
1170*86d7f5d3SJohn Marino /*              if the last is a leaf                                   */
1171*86d7f5d3SJohn Marino 
1172*86d7f5d3SJohn Marino {
1173*86d7f5d3SJohn Marino 	struct  hshentry *target, *target2, *temp;
1174*86d7f5d3SJohn Marino 	int length;
1175*86d7f5d3SJohn Marino 	int cmp;
1176*86d7f5d3SJohn Marino 
1177*86d7f5d3SJohn Marino 	if (!expandsym(delrev.strt, &numrev)) return 0;
1178*86d7f5d3SJohn Marino 	target = genrevs(numrev.string,(char*)0,(char*)0,(char*)0,&gendeltas);
1179*86d7f5d3SJohn Marino         if ( ! target ) return 0;
1180*86d7f5d3SJohn Marino 	cmp = cmpnum(target->num, numrev.string);
1181*86d7f5d3SJohn Marino 	length = countnumflds(numrev.string);
1182*86d7f5d3SJohn Marino 
1183*86d7f5d3SJohn Marino 	if (delrev.code == 0) {  /*  -o  rev    or    -o  branch   */
1184*86d7f5d3SJohn Marino 	    if (length & 1)
1185*86d7f5d3SJohn Marino 		temp=searchcutpt(target->num,length+1,gendeltas);
1186*86d7f5d3SJohn Marino 	    else if (cmp) {
1187*86d7f5d3SJohn Marino 		rcserror("Revision %s doesn't exist.", numrev.string);
1188*86d7f5d3SJohn Marino 		return 0;
1189*86d7f5d3SJohn Marino 	    }
1190*86d7f5d3SJohn Marino 	    else
1191*86d7f5d3SJohn Marino 		temp = searchcutpt(numrev.string, length, gendeltas);
1192*86d7f5d3SJohn Marino 	    cuttail = target->next;
1193*86d7f5d3SJohn Marino             if ( branchpoint(temp, cuttail) ) {
1194*86d7f5d3SJohn Marino 		cuttail = 0;
1195*86d7f5d3SJohn Marino                 return 0;
1196*86d7f5d3SJohn Marino             }
1197*86d7f5d3SJohn Marino             delstrt = temp;     /* first revision to be removed   */
1198*86d7f5d3SJohn Marino             return 1;
1199*86d7f5d3SJohn Marino         }
1200*86d7f5d3SJohn Marino 
1201*86d7f5d3SJohn Marino 	if (length & 1) {   /*  invalid branch after -o   */
1202*86d7f5d3SJohn Marino 	    rcserror("invalid branch range %s after -o", numrev.string);
1203*86d7f5d3SJohn Marino             return 0;
1204*86d7f5d3SJohn Marino         }
1205*86d7f5d3SJohn Marino 
1206*86d7f5d3SJohn Marino 	if (delrev.code == 1) {  /*  -o  -rev   */
1207*86d7f5d3SJohn Marino             if ( length > 2 ) {
1208*86d7f5d3SJohn Marino                 temp = searchcutpt( target->num, length-1, gendeltas);
1209*86d7f5d3SJohn Marino                 cuttail = target->next;
1210*86d7f5d3SJohn Marino             }
1211*86d7f5d3SJohn Marino             else {
1212*86d7f5d3SJohn Marino                 temp = searchcutpt(target->num, length, gendeltas);
1213*86d7f5d3SJohn Marino                 cuttail = target;
1214*86d7f5d3SJohn Marino                 while( cuttail && ! cmpnumfld(target->num,cuttail->num,1) )
1215*86d7f5d3SJohn Marino                     cuttail = cuttail->next;
1216*86d7f5d3SJohn Marino             }
1217*86d7f5d3SJohn Marino             if ( branchpoint(temp, cuttail) ){
1218*86d7f5d3SJohn Marino 		cuttail = 0;
1219*86d7f5d3SJohn Marino                 return 0;
1220*86d7f5d3SJohn Marino             }
1221*86d7f5d3SJohn Marino             delstrt = temp;
1222*86d7f5d3SJohn Marino             return 1;
1223*86d7f5d3SJohn Marino         }
1224*86d7f5d3SJohn Marino 
1225*86d7f5d3SJohn Marino 	if (delrev.code == 2) {   /*  -o  rev-   */
1226*86d7f5d3SJohn Marino             if ( length == 2 ) {
1227*86d7f5d3SJohn Marino                 temp = searchcutpt(target->num, 1,gendeltas);
1228*86d7f5d3SJohn Marino 		if (cmp)
1229*86d7f5d3SJohn Marino                     cuttail = target;
1230*86d7f5d3SJohn Marino                 else
1231*86d7f5d3SJohn Marino                     cuttail = target->next;
1232*86d7f5d3SJohn Marino             }
1233*86d7f5d3SJohn Marino             else  {
1234*86d7f5d3SJohn Marino 		if (cmp) {
1235*86d7f5d3SJohn Marino                     cuthead = target;
1236*86d7f5d3SJohn Marino                     if ( !(temp = target->next) ) return 0;
1237*86d7f5d3SJohn Marino                 }
1238*86d7f5d3SJohn Marino                 else
1239*86d7f5d3SJohn Marino                     temp = searchcutpt(target->num, length, gendeltas);
1240*86d7f5d3SJohn Marino 		getbranchno(temp->num, &numrev);  /* get branch number */
1241*86d7f5d3SJohn Marino 		VOID genrevs(numrev.string, (char*)0, (char*)0, (char*)0, &gendeltas);
1242*86d7f5d3SJohn Marino             }
1243*86d7f5d3SJohn Marino             if ( branchpoint( temp, cuttail ) ) {
1244*86d7f5d3SJohn Marino 		cuttail = 0;
1245*86d7f5d3SJohn Marino                 return 0;
1246*86d7f5d3SJohn Marino             }
1247*86d7f5d3SJohn Marino             delstrt = temp;
1248*86d7f5d3SJohn Marino             return 1;
1249*86d7f5d3SJohn Marino         }
1250*86d7f5d3SJohn Marino 
1251*86d7f5d3SJohn Marino         /*   -o   rev1-rev2   */
1252*86d7f5d3SJohn Marino 	if (!expandsym(delrev.end, &numrev)) return 0;
1253*86d7f5d3SJohn Marino 	if (
1254*86d7f5d3SJohn Marino 		length != countnumflds(numrev.string)
1255*86d7f5d3SJohn Marino 	    ||	(length>2 && compartial(numrev.string, target->num, length-1))
1256*86d7f5d3SJohn Marino 	) {
1257*86d7f5d3SJohn Marino 	    rcserror("invalid revision range %s-%s",
1258*86d7f5d3SJohn Marino 		target->num, numrev.string
1259*86d7f5d3SJohn Marino 	    );
1260*86d7f5d3SJohn Marino             return 0;
1261*86d7f5d3SJohn Marino         }
1262*86d7f5d3SJohn Marino 
1263*86d7f5d3SJohn Marino 	target2 = genrevs(numrev.string,(char*)0,(char*)0,(char*)0,&gendeltas);
1264*86d7f5d3SJohn Marino         if ( ! target2 ) return 0;
1265*86d7f5d3SJohn Marino 
1266*86d7f5d3SJohn Marino         if ( length > 2) {  /* delete revisions on branches  */
1267*86d7f5d3SJohn Marino             if ( cmpnum(target->num, target2->num) > 0) {
1268*86d7f5d3SJohn Marino 		cmp = cmpnum(target2->num, numrev.string);
1269*86d7f5d3SJohn Marino                 temp = target;
1270*86d7f5d3SJohn Marino                 target = target2;
1271*86d7f5d3SJohn Marino                 target2 = temp;
1272*86d7f5d3SJohn Marino             }
1273*86d7f5d3SJohn Marino 	    if (cmp) {
1274*86d7f5d3SJohn Marino                 if ( ! cmpnum(target->num, target2->num) ) {
1275*86d7f5d3SJohn Marino 		    rcserror("Revisions %s-%s don't exist.",
1276*86d7f5d3SJohn Marino 			delrev.strt, delrev.end
1277*86d7f5d3SJohn Marino 		    );
1278*86d7f5d3SJohn Marino                     return 0;
1279*86d7f5d3SJohn Marino                 }
1280*86d7f5d3SJohn Marino                 cuthead = target;
1281*86d7f5d3SJohn Marino                 temp = target->next;
1282*86d7f5d3SJohn Marino             }
1283*86d7f5d3SJohn Marino             else
1284*86d7f5d3SJohn Marino                 temp = searchcutpt(target->num, length, gendeltas);
1285*86d7f5d3SJohn Marino             cuttail = target2->next;
1286*86d7f5d3SJohn Marino         }
1287*86d7f5d3SJohn Marino         else { /*  delete revisions on trunk  */
1288*86d7f5d3SJohn Marino             if ( cmpnum( target->num, target2->num) < 0 ) {
1289*86d7f5d3SJohn Marino                 temp = target;
1290*86d7f5d3SJohn Marino                 target = target2;
1291*86d7f5d3SJohn Marino                 target2 = temp;
1292*86d7f5d3SJohn Marino             }
1293*86d7f5d3SJohn Marino             else
1294*86d7f5d3SJohn Marino 		cmp = cmpnum(target2->num, numrev.string);
1295*86d7f5d3SJohn Marino 	    if (cmp) {
1296*86d7f5d3SJohn Marino                 if ( ! cmpnum(target->num, target2->num) ) {
1297*86d7f5d3SJohn Marino 		    rcserror("Revisions %s-%s don't exist.",
1298*86d7f5d3SJohn Marino 			delrev.strt, delrev.end
1299*86d7f5d3SJohn Marino 		    );
1300*86d7f5d3SJohn Marino                     return 0;
1301*86d7f5d3SJohn Marino                 }
1302*86d7f5d3SJohn Marino                 cuttail = target2;
1303*86d7f5d3SJohn Marino             }
1304*86d7f5d3SJohn Marino             else
1305*86d7f5d3SJohn Marino                 cuttail = target2->next;
1306*86d7f5d3SJohn Marino             temp = searchcutpt(target->num, length, gendeltas);
1307*86d7f5d3SJohn Marino         }
1308*86d7f5d3SJohn Marino         if ( branchpoint(temp, cuttail) )  {
1309*86d7f5d3SJohn Marino 	    cuttail = 0;
1310*86d7f5d3SJohn Marino             return 0;
1311*86d7f5d3SJohn Marino         }
1312*86d7f5d3SJohn Marino         delstrt = temp;
1313*86d7f5d3SJohn Marino         return 1;
1314*86d7f5d3SJohn Marino }
1315*86d7f5d3SJohn Marino 
1316*86d7f5d3SJohn Marino 
1317*86d7f5d3SJohn Marino 
1318*86d7f5d3SJohn Marino 	static int
doassoc()1319*86d7f5d3SJohn Marino doassoc()
1320*86d7f5d3SJohn Marino /* Add or delete (if !revno) association that is stored in assoclst.  */
1321*86d7f5d3SJohn Marino {
1322*86d7f5d3SJohn Marino 	char const *p;
1323*86d7f5d3SJohn Marino 	int changed = false;
1324*86d7f5d3SJohn Marino 	struct Symrev const *curassoc;
1325*86d7f5d3SJohn Marino 	struct assoc **pre, *pt;
1326*86d7f5d3SJohn Marino 
1327*86d7f5d3SJohn Marino         /*  add new associations   */
1328*86d7f5d3SJohn Marino 	for (curassoc = assoclst;  curassoc;  curassoc = curassoc->nextsym) {
1329*86d7f5d3SJohn Marino 	    char const *ssymbol = curassoc->ssymbol;
1330*86d7f5d3SJohn Marino 
1331*86d7f5d3SJohn Marino 	    if (!curassoc->revno) {  /* delete symbol  */
1332*86d7f5d3SJohn Marino 		for (pre = &Symbols;  ;  pre = &pt->nextassoc)
1333*86d7f5d3SJohn Marino 		    if (!(pt = *pre)) {
1334*86d7f5d3SJohn Marino 			rcswarn("can't delete nonexisting symbol %s", ssymbol);
1335*86d7f5d3SJohn Marino 			break;
1336*86d7f5d3SJohn Marino 		    } else if (strcmp(pt->symbol, ssymbol) == 0) {
1337*86d7f5d3SJohn Marino 			*pre = pt->nextassoc;
1338*86d7f5d3SJohn Marino 			changed = true;
1339*86d7f5d3SJohn Marino 			break;
1340*86d7f5d3SJohn Marino 		    }
1341*86d7f5d3SJohn Marino 	    }
1342*86d7f5d3SJohn Marino 	    else {
1343*86d7f5d3SJohn Marino 		if (curassoc->revno[0]) {
1344*86d7f5d3SJohn Marino 		    p = 0;
1345*86d7f5d3SJohn Marino 		    if (expandsym(curassoc->revno, &numrev))
1346*86d7f5d3SJohn Marino 			p = fstr_save(numrev.string);
1347*86d7f5d3SJohn Marino 		} else if (!(p = tiprev()))
1348*86d7f5d3SJohn Marino 		    rcserror("no latest revision to associate with symbol %s",
1349*86d7f5d3SJohn Marino 			    ssymbol
1350*86d7f5d3SJohn Marino 		    );
1351*86d7f5d3SJohn Marino 		if (p)
1352*86d7f5d3SJohn Marino 		    changed |= addsymbol(p, ssymbol, curassoc->override);
1353*86d7f5d3SJohn Marino 	    }
1354*86d7f5d3SJohn Marino         }
1355*86d7f5d3SJohn Marino 	return changed;
1356*86d7f5d3SJohn Marino }
1357*86d7f5d3SJohn Marino 
1358*86d7f5d3SJohn Marino 
1359*86d7f5d3SJohn Marino 
1360*86d7f5d3SJohn Marino 	static int
dolocks()1361*86d7f5d3SJohn Marino dolocks()
1362*86d7f5d3SJohn Marino /* Function: remove lock for caller or first lock if unlockcaller is set;
1363*86d7f5d3SJohn Marino  *           remove locks which are stored in rmvlocklst,
1364*86d7f5d3SJohn Marino  *           add new locks which are stored in newlocklst,
1365*86d7f5d3SJohn Marino  *           add lock for Dbranch or Head if lockhead is set.
1366*86d7f5d3SJohn Marino  */
1367*86d7f5d3SJohn Marino {
1368*86d7f5d3SJohn Marino 	struct Lockrev const *lockpt;
1369*86d7f5d3SJohn Marino 	struct hshentry *target;
1370*86d7f5d3SJohn Marino 	int changed = false;
1371*86d7f5d3SJohn Marino 
1372*86d7f5d3SJohn Marino 	if (unlockcaller) { /*  find lock for caller  */
1373*86d7f5d3SJohn Marino             if ( Head ) {
1374*86d7f5d3SJohn Marino 		if (Locks) {
1375*86d7f5d3SJohn Marino 		    switch (findlock(true, &target)) {
1376*86d7f5d3SJohn Marino 		      case 0:
1377*86d7f5d3SJohn Marino 			/* remove most recent lock */
1378*86d7f5d3SJohn Marino 			changed |= breaklock(Locks->delta);
1379*86d7f5d3SJohn Marino 			break;
1380*86d7f5d3SJohn Marino 		      case 1:
1381*86d7f5d3SJohn Marino 			diagnose("%s unlocked\n",target->num);
1382*86d7f5d3SJohn Marino 			changed = true;
1383*86d7f5d3SJohn Marino 			break;
1384*86d7f5d3SJohn Marino 		    }
1385*86d7f5d3SJohn Marino 		} else {
1386*86d7f5d3SJohn Marino 		    rcswarn("No locks are set.");
1387*86d7f5d3SJohn Marino 		}
1388*86d7f5d3SJohn Marino             } else {
1389*86d7f5d3SJohn Marino 		rcswarn("can't unlock an empty tree");
1390*86d7f5d3SJohn Marino             }
1391*86d7f5d3SJohn Marino         }
1392*86d7f5d3SJohn Marino 
1393*86d7f5d3SJohn Marino         /*  remove locks which are stored in rmvlocklst   */
1394*86d7f5d3SJohn Marino 	for (lockpt = rmvlocklst;  lockpt;  lockpt = lockpt->nextrev)
1395*86d7f5d3SJohn Marino 	    if (expandsym(lockpt->revno, &numrev)) {
1396*86d7f5d3SJohn Marino 		target = genrevs(numrev.string, (char *)0, (char *)0, (char *)0, &gendeltas);
1397*86d7f5d3SJohn Marino                 if ( target ) {
1398*86d7f5d3SJohn Marino 		   if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string))
1399*86d7f5d3SJohn Marino 			rcserror("can't unlock nonexisting revision %s",
1400*86d7f5d3SJohn Marino 				lockpt->revno
1401*86d7f5d3SJohn Marino 			);
1402*86d7f5d3SJohn Marino                    else
1403*86d7f5d3SJohn Marino 			changed |= breaklock(target);
1404*86d7f5d3SJohn Marino                         /* breaklock does its own diagnose */
1405*86d7f5d3SJohn Marino 		}
1406*86d7f5d3SJohn Marino             }
1407*86d7f5d3SJohn Marino 
1408*86d7f5d3SJohn Marino         /*  add new locks which stored in newlocklst  */
1409*86d7f5d3SJohn Marino 	for (lockpt = newlocklst;  lockpt;  lockpt = lockpt->nextrev)
1410*86d7f5d3SJohn Marino 	    changed |= setlock(lockpt->revno);
1411*86d7f5d3SJohn Marino 
1412*86d7f5d3SJohn Marino 	if (lockhead) { /*  lock default branch or head  */
1413*86d7f5d3SJohn Marino 	    if (Dbranch)
1414*86d7f5d3SJohn Marino 		changed |= setlock(Dbranch);
1415*86d7f5d3SJohn Marino 	    else if (Head)
1416*86d7f5d3SJohn Marino 		changed |= setlock(Head->num);
1417*86d7f5d3SJohn Marino 	    else
1418*86d7f5d3SJohn Marino 		rcswarn("can't lock an empty tree");
1419*86d7f5d3SJohn Marino 	}
1420*86d7f5d3SJohn Marino 	return changed;
1421*86d7f5d3SJohn Marino }
1422*86d7f5d3SJohn Marino 
1423*86d7f5d3SJohn Marino 
1424*86d7f5d3SJohn Marino 
1425*86d7f5d3SJohn Marino 	static int
setlock(rev)1426*86d7f5d3SJohn Marino setlock(rev)
1427*86d7f5d3SJohn Marino 	char const *rev;
1428*86d7f5d3SJohn Marino /* Function: Given a revision or branch number, finds the corresponding
1429*86d7f5d3SJohn Marino  * delta and locks it for caller.
1430*86d7f5d3SJohn Marino  */
1431*86d7f5d3SJohn Marino {
1432*86d7f5d3SJohn Marino         struct  hshentry *target;
1433*86d7f5d3SJohn Marino 	int r;
1434*86d7f5d3SJohn Marino 
1435*86d7f5d3SJohn Marino 	if (expandsym(rev, &numrev)) {
1436*86d7f5d3SJohn Marino 	    target = genrevs(numrev.string, (char*)0, (char*)0,
1437*86d7f5d3SJohn Marino 			     (char*)0, &gendeltas);
1438*86d7f5d3SJohn Marino             if ( target ) {
1439*86d7f5d3SJohn Marino 	       if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string))
1440*86d7f5d3SJohn Marino 		    rcserror("can't lock nonexisting revision %s",
1441*86d7f5d3SJohn Marino 			numrev.string
1442*86d7f5d3SJohn Marino 		    );
1443*86d7f5d3SJohn Marino 	       else {
1444*86d7f5d3SJohn Marino 		    if ((r = addlock(target, false)) < 0  &&  breaklock(target))
1445*86d7f5d3SJohn Marino 			r = addlock(target, true);
1446*86d7f5d3SJohn Marino 		    if (0 <= r) {
1447*86d7f5d3SJohn Marino 			if (r)
1448*86d7f5d3SJohn Marino 			    diagnose("%s locked\n", target->num);
1449*86d7f5d3SJohn Marino 			return r;
1450*86d7f5d3SJohn Marino 		    }
1451*86d7f5d3SJohn Marino 	       }
1452*86d7f5d3SJohn Marino 	    }
1453*86d7f5d3SJohn Marino 	}
1454*86d7f5d3SJohn Marino 	return 0;
1455*86d7f5d3SJohn Marino }
1456*86d7f5d3SJohn Marino 
1457*86d7f5d3SJohn Marino 
1458*86d7f5d3SJohn Marino 	static int
domessages()1459*86d7f5d3SJohn Marino domessages()
1460*86d7f5d3SJohn Marino {
1461*86d7f5d3SJohn Marino 	struct hshentry *target;
1462*86d7f5d3SJohn Marino 	struct Message *p;
1463*86d7f5d3SJohn Marino 	int changed = false;
1464*86d7f5d3SJohn Marino 
1465*86d7f5d3SJohn Marino 	for (p = messagelst;  p;  p = p->nextmessage)
1466*86d7f5d3SJohn Marino 	    if (
1467*86d7f5d3SJohn Marino 		expandsym(p->revno, &numrev)  &&
1468*86d7f5d3SJohn Marino 		(target = genrevs(
1469*86d7f5d3SJohn Marino 			numrev.string, (char*)0, (char*)0, (char*)0, &gendeltas
1470*86d7f5d3SJohn Marino 		))
1471*86d7f5d3SJohn Marino 	    ) {
1472*86d7f5d3SJohn Marino 		/*
1473*86d7f5d3SJohn Marino 		 * We can't check the old log -- it's much later in the file.
1474*86d7f5d3SJohn Marino 		 * We pessimistically assume that it changed.
1475*86d7f5d3SJohn Marino 		 */
1476*86d7f5d3SJohn Marino 		target->log = p->message;
1477*86d7f5d3SJohn Marino 		changed = true;
1478*86d7f5d3SJohn Marino 	    }
1479*86d7f5d3SJohn Marino 	return changed;
1480*86d7f5d3SJohn Marino }
1481*86d7f5d3SJohn Marino 
1482*86d7f5d3SJohn Marino 
1483*86d7f5d3SJohn Marino 	static int
rcs_setstate(rev,status)1484*86d7f5d3SJohn Marino rcs_setstate(rev,status)
1485*86d7f5d3SJohn Marino 	char const *rev, *status;
1486*86d7f5d3SJohn Marino /* Function: Given a revision or branch number, finds the corresponding delta
1487*86d7f5d3SJohn Marino  * and sets its state to status.
1488*86d7f5d3SJohn Marino  */
1489*86d7f5d3SJohn Marino {
1490*86d7f5d3SJohn Marino         struct  hshentry *target;
1491*86d7f5d3SJohn Marino 
1492*86d7f5d3SJohn Marino 	if (expandsym(rev, &numrev)) {
1493*86d7f5d3SJohn Marino 	    target = genrevs(numrev.string, (char*)0, (char*)0,
1494*86d7f5d3SJohn Marino 			     (char*)0, &gendeltas);
1495*86d7f5d3SJohn Marino             if ( target ) {
1496*86d7f5d3SJohn Marino 	       if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string))
1497*86d7f5d3SJohn Marino 		    rcserror("can't set state of nonexisting revision %s",
1498*86d7f5d3SJohn Marino 			numrev.string
1499*86d7f5d3SJohn Marino 		    );
1500*86d7f5d3SJohn Marino 	       else if (strcmp(target->state, status) != 0) {
1501*86d7f5d3SJohn Marino                     target->state = status;
1502*86d7f5d3SJohn Marino 		    return true;
1503*86d7f5d3SJohn Marino 	       }
1504*86d7f5d3SJohn Marino 	    }
1505*86d7f5d3SJohn Marino 	}
1506*86d7f5d3SJohn Marino 	return false;
1507*86d7f5d3SJohn Marino }
1508*86d7f5d3SJohn Marino 
1509*86d7f5d3SJohn Marino 
1510*86d7f5d3SJohn Marino 
1511*86d7f5d3SJohn Marino 
1512*86d7f5d3SJohn Marino 
1513*86d7f5d3SJohn Marino 	static int
buildeltatext(deltas)1514*86d7f5d3SJohn Marino buildeltatext(deltas)
1515*86d7f5d3SJohn Marino 	struct hshentries const *deltas;
1516*86d7f5d3SJohn Marino /*   Function:  put the delta text on frewrite and make necessary   */
1517*86d7f5d3SJohn Marino /*              change to delta text                                */
1518*86d7f5d3SJohn Marino {
1519*86d7f5d3SJohn Marino 	register FILE *fcut;	/* temporary file to rebuild delta tree */
1520*86d7f5d3SJohn Marino 	char const *cutname;
1521*86d7f5d3SJohn Marino 
1522*86d7f5d3SJohn Marino 	fcut = 0;
1523*86d7f5d3SJohn Marino 	cuttail->selector = false;
1524*86d7f5d3SJohn Marino 	scanlogtext(deltas->first, false);
1525*86d7f5d3SJohn Marino         if ( cuthead )  {
1526*86d7f5d3SJohn Marino 	    cutname = maketemp(3);
1527*86d7f5d3SJohn Marino 	    if (!(fcut = fopenSafer(cutname, FOPEN_WPLUS_WORK))) {
1528*86d7f5d3SJohn Marino 		efaterror(cutname);
1529*86d7f5d3SJohn Marino             }
1530*86d7f5d3SJohn Marino 
1531*86d7f5d3SJohn Marino 	    while (deltas->first != cuthead) {
1532*86d7f5d3SJohn Marino 		deltas = deltas->rest;
1533*86d7f5d3SJohn Marino 		scanlogtext(deltas->first, true);
1534*86d7f5d3SJohn Marino             }
1535*86d7f5d3SJohn Marino 
1536*86d7f5d3SJohn Marino 	    snapshotedit(fcut);
1537*86d7f5d3SJohn Marino 	    Orewind(fcut);
1538*86d7f5d3SJohn Marino 	    aflush(fcut);
1539*86d7f5d3SJohn Marino         }
1540*86d7f5d3SJohn Marino 
1541*86d7f5d3SJohn Marino 	while (deltas->first != cuttail)
1542*86d7f5d3SJohn Marino 	    scanlogtext((deltas = deltas->rest)->first, true);
1543*86d7f5d3SJohn Marino 	finishedit((struct hshentry*)0, (FILE*)0, true);
1544*86d7f5d3SJohn Marino 	Ozclose(&fcopy);
1545*86d7f5d3SJohn Marino 
1546*86d7f5d3SJohn Marino 	if (fcut) {
1547*86d7f5d3SJohn Marino 	    char const *diffname = maketemp(0);
1548*86d7f5d3SJohn Marino 	    char const *diffv[6 + !!OPEN_O_BINARY];
1549*86d7f5d3SJohn Marino 	    char const **diffp = diffv;
1550*86d7f5d3SJohn Marino 	    *++diffp = DIFF;
1551*86d7f5d3SJohn Marino 	    *++diffp = DIFFFLAGS;
1552*86d7f5d3SJohn Marino #	    if OPEN_O_BINARY
1553*86d7f5d3SJohn Marino 		if (Expand == BINARY_EXPAND)
1554*86d7f5d3SJohn Marino 		    *++diffp == "--binary";
1555*86d7f5d3SJohn Marino #	    endif
1556*86d7f5d3SJohn Marino 	    *++diffp = "-";
1557*86d7f5d3SJohn Marino 	    *++diffp = resultname;
1558*86d7f5d3SJohn Marino 	    *++diffp = 0;
1559*86d7f5d3SJohn Marino 	    switch (runv(fileno(fcut), diffname, diffv)) {
1560*86d7f5d3SJohn Marino 		case DIFF_FAILURE: case DIFF_SUCCESS: break;
1561*86d7f5d3SJohn Marino 		default: rcsfaterror("diff failed");
1562*86d7f5d3SJohn Marino 	    }
1563*86d7f5d3SJohn Marino 	    Ofclose(fcut);
1564*86d7f5d3SJohn Marino 	    return putdtext(cuttail,diffname,frewrite,true);
1565*86d7f5d3SJohn Marino 	} else
1566*86d7f5d3SJohn Marino 	    return putdtext(cuttail,resultname,frewrite,false);
1567*86d7f5d3SJohn Marino }
1568*86d7f5d3SJohn Marino 
1569*86d7f5d3SJohn Marino 
1570*86d7f5d3SJohn Marino 
1571*86d7f5d3SJohn Marino 	static void
buildtree()1572*86d7f5d3SJohn Marino buildtree()
1573*86d7f5d3SJohn Marino /*   Function:  actually removes revisions whose selector field  */
1574*86d7f5d3SJohn Marino /*		is false, and rebuilds the linkage of deltas.	 */
1575*86d7f5d3SJohn Marino /*              asks for reconfirmation if deleting last revision*/
1576*86d7f5d3SJohn Marino {
1577*86d7f5d3SJohn Marino 	struct	hshentry   * Delta;
1578*86d7f5d3SJohn Marino         struct  branchhead      *pt, *pre;
1579*86d7f5d3SJohn Marino 
1580*86d7f5d3SJohn Marino         if ( cuthead )
1581*86d7f5d3SJohn Marino            if ( cuthead->next == delstrt )
1582*86d7f5d3SJohn Marino                 cuthead->next = cuttail;
1583*86d7f5d3SJohn Marino            else {
1584*86d7f5d3SJohn Marino                 pre = pt = cuthead->branches;
1585*86d7f5d3SJohn Marino                 while( pt && pt->hsh != delstrt )  {
1586*86d7f5d3SJohn Marino                     pre = pt;
1587*86d7f5d3SJohn Marino                     pt = pt->nextbranch;
1588*86d7f5d3SJohn Marino                 }
1589*86d7f5d3SJohn Marino                 if ( cuttail )
1590*86d7f5d3SJohn Marino                     pt->hsh = cuttail;
1591*86d7f5d3SJohn Marino                 else if ( pt == pre )
1592*86d7f5d3SJohn Marino                     cuthead->branches = pt->nextbranch;
1593*86d7f5d3SJohn Marino                 else
1594*86d7f5d3SJohn Marino                     pre->nextbranch = pt->nextbranch;
1595*86d7f5d3SJohn Marino             }
1596*86d7f5d3SJohn Marino 	else {
1597*86d7f5d3SJohn Marino 	    if (!cuttail && !quietflag) {
1598*86d7f5d3SJohn Marino 		if (!yesorno(false, "Do you really want to delete all revisions? [ny](n): ")) {
1599*86d7f5d3SJohn Marino 		    rcserror("No revision deleted");
1600*86d7f5d3SJohn Marino 		    Delta = delstrt;
1601*86d7f5d3SJohn Marino 		    while( Delta) {
1602*86d7f5d3SJohn Marino 			Delta->selector = true;
1603*86d7f5d3SJohn Marino 			Delta = Delta->next;
1604*86d7f5d3SJohn Marino 		    }
1605*86d7f5d3SJohn Marino 		    return;
1606*86d7f5d3SJohn Marino 		}
1607*86d7f5d3SJohn Marino 	    }
1608*86d7f5d3SJohn Marino             Head = cuttail;
1609*86d7f5d3SJohn Marino 	}
1610*86d7f5d3SJohn Marino         return;
1611*86d7f5d3SJohn Marino }
1612*86d7f5d3SJohn Marino 
1613*86d7f5d3SJohn Marino #if RCS_lint
1614*86d7f5d3SJohn Marino /* This lets us lint everything all at once. */
1615*86d7f5d3SJohn Marino 
1616*86d7f5d3SJohn Marino char const cmdid[] = "";
1617*86d7f5d3SJohn Marino 
1618*86d7f5d3SJohn Marino #define go(p,e) {int p P((int,char**)); void e P((void)); if(*argv)return p(argc,argv);if(*argv[1])e();}
1619*86d7f5d3SJohn Marino 
1620*86d7f5d3SJohn Marino 	int
main(argc,argv)1621*86d7f5d3SJohn Marino main(argc, argv)
1622*86d7f5d3SJohn Marino 	int argc;
1623*86d7f5d3SJohn Marino 	char **argv;
1624*86d7f5d3SJohn Marino {
1625*86d7f5d3SJohn Marino 	go(ciId,	ciExit);
1626*86d7f5d3SJohn Marino 	go(coId,	coExit);
1627*86d7f5d3SJohn Marino 	go(identId,	identExit);
1628*86d7f5d3SJohn Marino 	go(mergeId,	mergeExit);
1629*86d7f5d3SJohn Marino 	go(rcsId,	exiterr);
1630*86d7f5d3SJohn Marino 	go(rcscleanId,	rcscleanExit);
1631*86d7f5d3SJohn Marino 	go(rcsdiffId,	rdiffExit);
1632*86d7f5d3SJohn Marino 	go(rcsmergeId,	rmergeExit);
1633*86d7f5d3SJohn Marino 	go(rlogId,	rlogExit);
1634*86d7f5d3SJohn Marino 	return 0;
1635*86d7f5d3SJohn Marino }
1636*86d7f5d3SJohn Marino #endif
1637