BSD 4_3_Net_2 release
[unix-history] / usr / src / contrib / rcs / src / rcsutil.c
CommitLineData
82a61035
C
1/*
2 * RCS utilities
3 */
4#ifndef lint
5static char rcsid[]= "$Id: rcsutil.c,v 4.6 89/05/01 15:13:40 narten Exp $ Purdue CS";
6#endif
7
8/* Copyright (C) 1982, 1988, 1989 Walter Tichy
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms are permitted
12 * provided that the above copyright notice and this paragraph are
13 * duplicated in all such forms and that any documentation,
14 * advertising materials, and other materials related to such
15 * distribution and use acknowledge that the software was developed
16 * by Walter Tichy.
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 *
21 * Report all problems and direct all questions to:
22 * rcs-bugs@cs.purdue.edu
23 *
24
25
26
27
28
29
30
31*/
32
33
34
35
36/* $Log: rcsutil.c,v $
37 * Revision 4.6 89/05/01 15:13:40 narten
38 * changed copyright header to reflect current distribution rules
39 *
40 * Revision 4.5 88/11/08 16:01:02 narten
41 * corrected use of varargs routines
42 *
43 * Revision 4.4 88/11/08 12:00:28 narten
44 * changes from eggert@sm.unisys.com (Paul Eggert)
45 *
46 * Revision 4.4 88/08/09 19:13:24 eggert
47 * Check for memory exhaustion.
48 * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch.
49 * Use execv(), not system(); yield exit status like diff(1)'s.
50 *
51 * Revision 4.3 87/10/18 10:40:22 narten
52 * Updating version numbers. Changes relative to 1.1 actually
53 * relative to 4.1
54 *
55 * Revision 1.3 87/09/24 14:01:01 narten
56 * Sources now pass through lint (if you ignore printf/sprintf/fprintf
57 * warnings)
58 *
59 * Revision 1.2 87/03/27 14:22:43 jenkins
60 * Port to suns
61 *
62 * Revision 1.1 84/01/23 14:50:43 kcs
63 * Initial revision
64 *
65 * Revision 4.1 83/05/10 15:53:13 wft
66 * Added getcaller() and findlock().
67 * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal
68 * (needed for background jobs in older shells). Added restoreints().
69 * Removed printing of full RCS path from logcommand().
70 *
71 * Revision 3.8 83/02/15 15:41:49 wft
72 * Added routine fastcopy() to copy remainder of a file in blocks.
73 *
74 * Revision 3.7 82/12/24 15:25:19 wft
75 * added catchints(), ignoreints() for catching and ingnoring interrupts;
76 * fixed catchsig().
77 *
78 * Revision 3.6 82/12/08 21:52:05 wft
79 * Using DATEFORM to format dates.
80 *
81 * Revision 3.5 82/12/04 18:20:49 wft
82 * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update
83 * lockedby-field.
84 *
85 * Revision 3.4 82/12/03 17:17:43 wft
86 * Added check to addlock() ensuring only one lock per person.
87 * Addlock also returns a pointer to the lock created. Deleted fancydate().
88 *
89 * Revision 3.3 82/11/27 12:24:37 wft
90 * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c.
91 * Introduced macro SNOOP so that snoop can be placed in directory other than
92 * TARGETDIR. Changed %02d to %.2d for compatibility reasons.
93 *
94 * Revision 3.2 82/10/18 21:15:11 wft
95 * added function getfullRCSname().
96 *
97 * Revision 3.1 82/10/13 16:17:37 wft
98 * Cleanup message is now suppressed in quiet mode.
99 */
100
101
102
103
104#include <sys/types.h>
105#include <sys/stat.h>
106#include <signal.h>
107#include "rcsbase.h"
108#include <pwd.h>
109#include <varargs.h>
110
111#if defined(USG) || defined(V4_2BSD)
112#include <fcntl.h>
113#endif
114
115#ifndef V4_2BSD
116#define vfork fork
117#endif
118
119extern char * bindex();
120extern FILE * finptr;
121extern char * RCSfilename;
122extern char * getlogin();
123extern char * malloc();
124
125
126char * talloc(size)
127unsigned size;
128{
129 char * p;
130 if (!(p = malloc(size))) {
131 faterror("out of memory");
132 }
133 return p;
134}
135
136
137
138char * getcaller()
139/* Function: gets the callers login from his uid.
140 * If the uid is root, tries to get the true login with getlogin().
141 */
142{
143 struct passwd *pw;
144 char *name;
145 static int special_user();
146
147 pw = getpwuid(getuid());
148 if (pw == NULL || special_user(name = pw->pw_name))
149 if ((name = getlogin()) == NULL || *name == 0)
150 name = pw == NULL ? "nobody" : pw->pw_name;
151 return (name);
152}
153
154static
155special_user(s)
156 register char *s;
157{
158 register char **p;
159 static char *u[] = { "root", "daemon", "bin", "news", 0 };
160
161 for (p = u; *p; p++)
162 if (**p == *s && strcmp(*p, s) == 0)
163 return (1);
164 return (0);
165}
166
167
168struct hshentry * findlock(who,delete)
169char * who; int delete;
170/* Finds the first lock held by who and returns a pointer
171 * to the locked delta; also removes the lock if delete==true.
172 * Returns nil if there is no lock held by who.
173 */
174{
175 register struct lock * next, * trail;
176 struct lock dummy;
177
178 dummy.nextlock=next=Locks;
179 trail = &dummy;
180 while (next!=nil) {
181 if(strcmp(who,next->login)==0) break; /*found a lock*/
182 trail=next;
183 next=next->nextlock;
184 }
185 if (next!=nil) {
186 /* found one */
187 if (delete) {
188 /* delete it */
189 trail->nextlock=next->nextlock;
190 Locks=dummy.nextlock;
191 next->delta->lockedby=nil; /* reset locked-by */
192 }
193 return next->delta;
194 } else return nil;
195}
196
197
198
199
200
201
202
203struct lock * addlock(delta,who)
204struct hshentry * delta; char * who;
205/* Given a delta, addlock checks whether
206 * the delta is locked by somebody other than who.
207 * If so, an error message is printed, and false returned.
208 * If the delta is not reserved at all, a lock for it is added,
209 * and a pointer for the lock returned.
210 */
211{
212 struct lock * next;
213
214 next=Locks;
215 while (next!=nil) {
216 if (cmpnum(delta->num,next->delta->num)==0) {
217 if (strcmp(who,next->login)==0)
218 return next;
219 /* lock exists already */
220 else {
221 error("revision %s already locked by %s",
222 delta->num, next->login);
223 return false;
224 }
225 } else {
226 if (strcmp(who,next->login)==0) {
227 error("you already locked %s; only one lock allowed per person.",
228 next->delta->num);
229 return false;
230 } else {
231 next=next->nextlock;
232 }
233 }
234 }
235 /* not found; set up new lockblock */
236 next= (struct lock *) talloc(sizeof (struct lock));
237 delta->lockedby=next->login=who;
238 next->delta= delta;
239 next->nextlock=Locks;
240 Locks=next;
241 return next;
242}
243
244
245
246int addsymbol(delta,name,rebind)
247struct hshentry * delta; char * name; int rebind;
248/* Function: adds a new symbolic name and associates it with node delta.
249 * If name already exists and rebind is true, the name is associated
250 * with the new delta; otherwise, an error message is printed and
251 * false returned. Returns true it successful.
252 */
253{ register struct assoc * next;
254 next=Symbols;
255 while (next!=nil) {
256 if (strcmp(name,next->symbol)==0) {
257 if (rebind) {
258 next->delta=delta;
259 return true;
260 } else {
261 error("symbolic name %s already bound to %s",
262 name,next->delta->num);
263 return false;
264 }
265 } else next = next->nextassoc;
266 }
267 /* not found; insert new pair. */
268 next = (struct assoc *) talloc(sizeof(struct assoc));
269 next->symbol=name;
270 next->delta=delta;
271 next->nextassoc=Symbols;
272 Symbols = next;
273 return true;
274}
275
276
277
278
279int checkaccesslist(who)
280char * who;
281/* function: Returns true if who is the superuser, the owner of the
282 * file, the access list is empty, or who is on the access list.
283 * Prints an error message and returns false otherwise.
284 */
285{
286 register struct access * next;
287 struct stat statbuf;
288
289 if ((AccessList==nil) || (strcmp(who,"root")==0))
290 return true;
291
292 next=AccessList;
293 do {
294 if (strcmp(who,next->login)==0)
295 return true;
296 next=next->nextaccess;
297 } while (next!=nil);
298
299 VOID fstat(fileno(finptr),&statbuf); /* get owner of file */
300 if (getuid() == statbuf.st_uid) return true;
301
302 error("User %s not on the access list",who);
303 return false;
304}
305
306
307static SIGNAL_TYPE catchsig(s)
308{
309 ignoreints();
310 diagnose("\nRCS: cleaning up\n");
311 VOID cleanup();
312 exit(2);
313#ifdef lint
314 catchsig(s);
315#endif
316}
317
318static sig[] = {SIGINT,SIGHUP,SIGQUIT,SIGPIPE,SIGTERM};
319#define SIGS (sizeof(sig)/sizeof(*sig))
320static SIGNAL_TYPE (*catcher[SIGS])();
321
322 void catchints()
323 {
324 register i;
325 for (i=SIGS; 0<=--i; )
326 catcher[i] =
327 signal(sig[i],SIG_IGN) == SIG_IGN ? SIG_IGN : catchsig;
328 restoreints();
329 }
330
331 void ignoreints()
332 {
333 register i;
334 for (i=SIGS; 0<=--i; )
335 VOID signal(sig[i], SIG_IGN);
336 }
337
338void restoreints()
339{
340 register i;
341 for (i=SIGS; 0<=--i; )
342 if (catcher[i] != SIG_IGN)
343 VOID signal(sig[i], catcher[i]);
344}
345
346fastcopy(inf,outf)
347FILE * inf, * outf;
348/* Function: copies the remainder of file inf to outf. First copies the
349 * rest that is in the IO-buffer of inf character by character, and then
350 * copies the remainder in blocks.
351 */
352{ char buf[BUFSIZ];
353 register int rcount, wcount;
354
355 /* write the rest of the buffer to outf */
356#ifdef _FSTDIO
357 while (--inf->_r >= 0)
358 VOID putc(*inf->_p++, outf);
359#else
360 while ((--inf->_cnt)>=0) {
361 VOID putc(*inf->_ptr++&0377,outf);
362 }
363#endif
364 if (fflush(outf) == EOF) {
365 writeerror();
366 }
367
368 /*now read the rest of the file in blocks*/
369 while ((rcount=read(fileno(inf),buf,BUFSIZ))>0) {
370 wcount=write(fileno(outf),buf,rcount);
371 if (wcount!=rcount) {
372 writeerror();
373 }
374 }
375}
376
377
378
379
380
381
382#ifdef SNOOPFILE
383
384#include "time.h"
385extern struct tm* localtime();
386extern long time();
387
388logcommand(commandname,delta, sequence,login)
389char* commandname; struct hshentry * delta, * sequence[];char * login;
390/* Function: start a process to write the file that
391 * logs the RCS command.
392 * Each line in the log file contains the following information:
393 * operation, revision(r), backward deltas applied(b), forward deltas applied(f),
394 * total deltas present(t), creation date of delta(d), date of operation(o),
395 * login of caller, RCS file name.
396 */
397{
398 char logline[200];
399 char curdate[datelength];
400 char *inoutargs[5];
401 register int i, backward, forward;
402 long clock;
403 struct tm * tm;
404
405 clock=time((long *)0);
406 tm=localtime(&clock);
407
408 VOID sprintf(curdate,DATEFORM,
409 tm->tm_year, tm->tm_mon+1, tm->tm_mday,
410 tm->tm_hour, tm->tm_min, tm->tm_sec);
411
412 i= backward=forward=0;
413 while(sequence[i]!=nil) { /* count deltas to be applied*/
414 if (countnumflds(sequence[i]->num) == 2)
415 backward++; /* reverse delta */
416 else forward++; /* branch delta */
417 i++;
418 }
419 VOID sprintf(logline,"%s %10sr %3db %3df %3dt %sc %so %s %s",
420 commandname,delta->num,backward,forward,TotalDeltas,delta->date,
421 curdate,login,bindex(getfullRCSname(),'/'));
422 inoutargs[0] = nil;
423 inoutargs[1] = nil;
424 inoutargs[2] = SNOOP;
425 inoutargs[3] = logline;
426 inoutargs[4] = nil;
427 VOID run_back(inoutargs);
428}
429#endif
430
431
432static int fdreopen(fd, file, flags, mode)
433 char *file;
434{
435 int newfd;
436 VOID close(fd);
437 newfd = flags==-1 ? creat(file,mode) : open(file,flags,mode);
438 if (newfd < 0 || newfd == fd)
439 return newfd;
440#ifdef F_DUPFD
441 fd = fcntl(newfd, F_DUPFD, fd);
442#else
443 fd = dup2(newfd, fd);
444#endif
445 VOID close(newfd);
446 return fd;
447}
448
449static void tryopen(fd,file,flags)
450 char *file;
451{
452 if (file && fdreopen(fd,file,flags,0600) != fd) {
453 VOID write(fileno(stderr), file, strlen(file));
454 VOID write(fileno(stderr), ": cannot open\n", 14);
455 _exit(2);
456 }
457}
458
459/*
460/* Run in the background a command specified by the strings in 'inoutargs'.
461/* inoutargs[0], if nonnil, is the name of the input file.
462/* inoutargs[1], if nonnil, is the name of the output file.
463/* inoutargs[2..] form the command to be run in the background.
464/*/
465static int run_back(inoutargs)
466 register char **inoutargs;
467{
468 int pid;
469 if (fflush(stdout) == EOF || fflush(stderr) == EOF)
470 return -1;
471 if (!(pid = vfork())) {
472 tryopen(fileno(stdin), inoutargs[0], 0);
473 tryopen(fileno(stdout), inoutargs[1], -1);
474 VOID execv(inoutargs[2], &inoutargs[2]);
475 inoutargs[1] = "/bin/sh";
476 VOID execv(inoutargs[1], &inoutargs[1]);
477 VOID write(fileno(stderr), "/bin/sh: not found\n", 19);
478 _exit(2);
479 }
480 return pid;
481}
482
483#define CARGSMAX 20
484/*
485/* Run a command.
486/* The first two arguments are the input and output files (if nonnil);
487/* the rest specify the command and its arguments.
488/*/
489int run(va_alist)
490 va_dcl
491{
492 va_list ap;
493 int pid, wstatus, w;
494 char *rgargs[CARGSMAX];
495 register i = 0;
496 va_start(ap);
497 rgargs[0] = va_arg(ap, char *);
498 rgargs[1] = va_arg(ap, char *);
499 for (i =2; i< CARGSMAX; i++) {
500 rgargs[i] = va_arg(ap, char *);
501 if (rgargs[i] == NULL)
502 break;
503 }
504 va_end(ap);
505 pid = run_back(rgargs);
506 if (pid < 0)
507 return pid;
508 for (;;)
509 if ((w = wait(&wstatus)) < 0)
510 return w;
511 else if (w == pid)
512 return wstatus;
513}