1125461Speter/*
2125461Speter * ----------------------------------------------------------------------------
3125461Speter * "THE BEER-WARE LICENSE" (Revision 42):
4125461Speter * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
5125461Speter * can do whatever you want with this stuff. If we meet some day, and you think
6125984Sobrien * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7125461Speter * ----------------------------------------------------------------------------
8125461Speter *
9125461Speter * $FreeBSD: releng/10.3/usr.sbin/ctm/ctm/ctm.c 93150 2002-03-25 13:53:46Z phk $
10125461Speter *
11126929Speter * This is the client program of 'CTM'.  It will apply a CTM-patch to a
12147687Speter * collection of files.
13125461Speter *
14209313Skib * Options we'd like to see:
15209313Skib *
16209313Skib * -a 			Attempt best effort.
17209313Skib * -d <int>		Debug TBD.
18209313Skib * -m <mail-addr>	Email me instead.
19209313Skib * -r <name>		Reconstruct file.
20125461Speter * -R <file>		Read list of files to reconstruct.
21126541Sobrien *
22126541Sobrien * Options we have:
23126541Sobrien * -b <dir>		Base-dir
24126541Sobrien * -B <file>		Backup to tar-file.
25126541Sobrien * -t 			Tar command (default as in TARCMD).
26147378Sups * -c			Check it out, don't do anything.
27147378Sups * -F      		Force
28125461Speter * -q 			Tell us less.
29126541Sobrien * -T <tmpdir>.		Temporary files.
30147378Sups * -u			Set all file modification times to the timestamp
31126638Sobrien * -v 			Tell us more.
32126637Sobrien * -V <level>		Tell us more level = number of -v
33126541Sobrien * -k			Keep files and directories that would have been removed.
34142732Sobrien * -l			List actions.
35142732Sobrien *
36142732Sobrien * Options we don't actually use:
37142732Sobrien * -p			Less paranoid.
38142732Sobrien * -P			Paranoid.
39209248Smav */
40209248Smav
41209248Smav#define EXTERN /* */
42209248Smav#include <paths.h>
43145727Sdwhite#include "ctm.h"
44209248Smav
45126541Sobrien#define CTM_STATUS ".ctm_status"
46126541Sobrien
47126541Sobrienextern int Proc(char *, unsigned applied);
48126541Sobrien
49125461Speterint
50126541Sobrienmain(int argc, char **argv)
51126541Sobrien{
52126541Sobrien    int stat=0, err=0;
53126541Sobrien    int c;
54126541Sobrien    unsigned applied = 0;
55126541Sobrien    FILE *statfile;
56126541Sobrien    struct CTM_Filter *nfilter = NULL;	/* new filter */
57126541Sobrien    u_char * basedir;
58126541Sobrien
59126541Sobrien    basedir = NULL;
60126541Sobrien    Verbose = 1;
61125461Speter    Paranoid = 1;
62125461Speter    SetTime = 0;
63125461Speter    KeepIt = 0;
64126541Sobrien    ListIt = 0;
65125461Speter    BackupFile = NULL;
66125461Speter    TarCmd = TARCMD;
67125461Speter    LastFilter = FilterList = NULL;
68125461Speter    TmpDir = getenv("TMPDIR");
69125461Speter    if (TmpDir == NULL)
70125461Speter	TmpDir = strdup(_PATH_TMP);
71125461Speter    setbuf(stderr,0);
72125461Speter    setbuf(stdout,0);
73125461Speter
74125461Speter    while((c=getopt(argc,argv,"ab:B:cd:e:Fklm:pPqr:R:t:T:uV:vx:")) != -1) {
75126541Sobrien	switch (c) {
76125461Speter	    case 'b': basedir = optarg;	break; /* Base Directory */
77125461Speter	    case 'B': BackupFile = optarg;	break;
78125461Speter	    case 'c': CheckIt++;	break; /* Only check it */
79151051Sglebius	    case 'F': Force = 1;	break;
80151051Sglebius	    case 'k': KeepIt++;		break; /* Don't do removes */
81151051Sglebius	    case 'l': ListIt++;		break; /* Only list actions and files */
82151051Sglebius	    case 'p': Paranoid--;	break; /* Less Paranoid */
83125461Speter	    case 'P': Paranoid++;	break; /* More Paranoid */
84126541Sobrien	    case 'q': Verbose--;	break; /* Quiet */
85126541Sobrien	    case 't': TarCmd = optarg;	break; /* archiver command */
86125461Speter	    case 'T': TmpDir = optarg;	break; /* set temporary directory */
87125461Speter	    case 'u': SetTime++;	break; /* Set timestamp on files */
88125461Speter	    case 'v': Verbose++;	break; /* Verbose */
89177586Sjkim	    case 'V': sscanf(optarg,"%d", &c); /* Verbose */
90177586Sjkim		      Verbose += c;
91191954Skuriyama		      break;
92177586Sjkim	    case 'e':				/* filter expressions */
93234183Sjhb	    case 'x':
94234183Sjhb		if (NULL == (nfilter =  Malloc(sizeof(struct CTM_Filter)))) {
95234183Sjhb			warnx("out of memory for expressions: \"%s\"", optarg);
96234183Sjhb			stat++;
97234183Sjhb			break;
98234183Sjhb		}
99234183Sjhb
100234183Sjhb		(void) memset(nfilter, 0, sizeof(struct CTM_Filter));
101239771Sjhb
102234183Sjhb		if (0 != (err =
103234183Sjhb			regcomp(&nfilter->CompiledRegex, optarg, REG_NOSUB))) {
104234183Sjhb
105234183Sjhb			char errmsg[128];
106125461Speter
107125461Speter			regerror(err, &nfilter->CompiledRegex, errmsg,
108125461Speter				sizeof(errmsg));
109125461Speter			warnx("regular expression: \"%s\"", errmsg);
110173160Speter			stat++;
111173160Speter			break;
112173160Speter		}
113125461Speter
114125461Speter		/* note whether the filter enables or disables on match */
115125461Speter		nfilter->Action =
116125461Speter			(('e' == c) ? CTM_FILTER_ENABLE : CTM_FILTER_DISABLE);
117152306Sru
118152306Sru		/* link in the expression into the list */
119152306Sru	        nfilter->Next = NULL;
120126541Sobrien		if (NULL == FilterList) {
121125461Speter		  LastFilter = FilterList = nfilter; /* init head and tail */
122125461Speter		} else {    /* place at tail */
123125461Speter		  LastFilter->Next = nfilter;
124125461Speter		  LastFilter = nfilter;
125125461Speter		}
126125461Speter		break;
127125461Speter	    case ':':
128125461Speter		warnx("option '%c' requires an argument",optopt);
129125461Speter		stat++;
130125461Speter		break;
131125461Speter	    case '?':
132125461Speter		warnx("option '%c' not supported",optopt);
133125461Speter		stat++;
134125461Speter		break;
135125461Speter	    default:
136125461Speter		warnx("option '%c' not yet implemented",optopt);
137125461Speter		break;
138125461Speter	}
139125461Speter    }
140125461Speter
141125461Speter    if(stat) {
142125461Speter	warnx("%d errors during option processing",stat);
143125461Speter	return Exit_Pilot;
144125461Speter    }
145125461Speter    stat = Exit_Done;
146125461Speter    argc -= optind;
147125461Speter    argv += optind;
148125461Speter
149125461Speter    if (basedir == NULL) {
150125461Speter	Buffer = (u_char *)Malloc(BUFSIZ + strlen(SUBSUFF) +1);
151125461Speter	CatPtr = Buffer;
152125461Speter	*Buffer  = '\0';
153125461Speter    } else {
154125461Speter	Buffer = (u_char *)Malloc(strlen(basedir)+ BUFSIZ + strlen(SUBSUFF) +1);
155125461Speter	strcpy(Buffer, basedir);
156125461Speter	CatPtr = Buffer + strlen(basedir);
157126541Sobrien	if (CatPtr[-1] != '/') {
158126541Sobrien		strcat(Buffer, "/");
159126541Sobrien		CatPtr++;
160125461Speter	}
161125461Speter    }
162125461Speter    strcat(Buffer, CTM_STATUS);
163125461Speter
164125461Speter    if(ListIt)
165125461Speter	applied = 0;
166125461Speter    else
167125461Speter	if((statfile = fopen(Buffer, "r")) == NULL) {
168125461Speter	    if (Verbose > 0)
169125461Speter	    	warnx("warning: %s not found", Buffer);
170125461Speter	} else {
171125461Speter	    fscanf(statfile, "%*s %u", &applied);
172188247Swkoszek	    fclose(statfile);
173188247Swkoszek	}
174188247Swkoszek
175191954Skuriyama    if(!argc)
176188247Swkoszek	stat |= Proc("-", applied);
177125461Speter
178125461Speter    while(argc-- && stat == Exit_Done) {
179125461Speter	stat |= Proc(*argv++, applied);
180125461Speter	stat &= ~(Exit_Version | Exit_NoMatch);
181197380Sdelphij    }
182197025Sdelphij
183197025Sdelphij    if(stat == Exit_Done)
184197025Sdelphij	stat = Exit_OK;
185197025Sdelphij
186197025Sdelphij    if(Verbose > 0)
187197397Sdelphij	warnx("exit(%d)",stat);
188197397Sdelphij
189197397Sdelphij    if (FilterList)
190197397Sdelphij	for (nfilter = FilterList; nfilter; ) {
191197397Sdelphij	    struct CTM_Filter *tmp = nfilter->Next;
192125461Speter	    Free(nfilter);
193125461Speter	    nfilter = tmp;
194125461Speter	}
195125461Speter    return stat;
196163535Sdes}
197163535Sdes
198163535Sdesint
199163535SdesProc(char *filename, unsigned applied)
200163535Sdes{
201163535Sdes    FILE *f;
202163535Sdes    int i;
203163535Sdes    char *p = strrchr(filename,'.');
204163535Sdes
205163535Sdes    if(!strcmp(filename,"-")) {
206163535Sdes	p = 0;
207163535Sdes	f = stdin;
208163535Sdes    } else if(p && (!strcmp(p,".gz") || !strcmp(p,".Z"))) {
209163535Sdes	p = alloca(20 + strlen(filename));
210163535Sdes	strcpy(p,"gunzip < ");
211163535Sdes	strcat(p,filename);
212163535Sdes	f = popen(p,"r");
213163535Sdes	if(!f) { warn("%s", p); return Exit_Garbage; }
214163535Sdes    } else {
215163535Sdes	p = 0;
216163535Sdes	f = fopen(filename,"r");
217163535Sdes    }
218163535Sdes    if(!f) {
219163535Sdes	warn("%s", filename);
220163535Sdes	return Exit_Garbage;
221163535Sdes    }
222163535Sdes
223163535Sdes    if(Verbose > 1)
224163535Sdes	fprintf(stderr,"Working on <%s>\n",filename);
225163535Sdes
226163535Sdes    Delete(FileName);
227163535Sdes    FileName = String(filename);
228163535Sdes
229163535Sdes    /* If we cannot seek, we're doomed, so copy to a tmp-file in that case */
230163535Sdes    if(!p &&  -1 == fseek(f,0,SEEK_END)) {
231163535Sdes	char *fn;
232163535Sdes	FILE *f2;
233163535Sdes	int fd;
234163535Sdes
235163535Sdes	if (asprintf(&fn, "%s/CTMclient.XXXXXXXXXX", TmpDir) == -1) {
236163535Sdes	    fprintf(stderr, "Cannot allocate memory\n");
237163535Sdes	    fclose(f);
238163535Sdes	    return Exit_Broke;
239163535Sdes	}
240163535Sdes	if ((fd = mkstemp(fn)) == -1 || (f2 = fdopen(fd, "w+")) == NULL) {
241163535Sdes 	    warn("%s", fn);
242163535Sdes	    free(fn);
243163535Sdes	    if (fd != -1)
244163535Sdes		close(fd);
245163535Sdes 	    fclose(f);
246163535Sdes 	    return Exit_Broke;
247163535Sdes 	}
248163535Sdes	unlink(fn);
249163535Sdes	if (Verbose > 0)
250163535Sdes	    fprintf(stderr,"Writing tmp-file \"%s\"\n",fn);
251197379Sdelphij	free(fn);
252197379Sdelphij	while(EOF != (i=getc(f)))
253197379Sdelphij	    if(EOF == putc(i,f2)) {
254126541Sobrien		fclose(f2);
255126541Sobrien		return Exit_Broke;
256126541Sobrien	    }
257126541Sobrien	fclose(f);
258125461Speter	f = f2;
259126541Sobrien    }
260156354Syar
261126541Sobrien    if(!p)
262126541Sobrien	rewind(f);
263156354Syar
264126541Sobrien    if((i=Pass1(f, applied)))
265126541Sobrien	goto exit_and_close;
266125461Speter
267125461Speter    if(ListIt) {
268125461Speter	i = Exit_Done;
269125461Speter	goto exit_and_close;
270125461Speter    }
271125461Speter
272125461Speter    if(!p) {
273126541Sobrien        rewind(f);
274125461Speter    } else {
275125461Speter	pclose(f);
276125461Speter	f = popen(p,"r");
277171146Snjl	if(!f) { warn("%s", p); return Exit_Broke; }
278171146Snjl    }
279171146Snjl
280145132Sanholt    i=Pass2(f);
281145132Sanholt
282153033Sanholt    if(!p) {
283145132Sanholt        rewind(f);
284145132Sanholt    } else {
285145132Sanholt	pclose(f);
286148211Sanholt	f = popen(p,"r");
287152909Sanholt	if(!f) { warn("%s", p); return Exit_Broke; }
288145132Sanholt    }
289145132Sanholt
290203288Srnoland    if(i) {
291145132Sanholt	if((!Force) || (i & ~Exit_Forcible))
292125461Speter	    goto exit_and_close;
293125461Speter    }
294125461Speter
295125461Speter    if(CheckIt) {
296125461Speter	if (Verbose > 0)
297255736Sdavidch	    fprintf(stderr,"All checks out ok.\n");
298255736Sdavidch	i = Exit_Done;
299125984Sobrien	goto exit_and_close;
300163494Simp    }
301129293Speter
302159549Sjhb    /* backup files if requested */
303203691Sbrucec    if(BackupFile) {
304159549Sjhb
305203691Sbrucec	i = PassB(f);
306203691Sbrucec
307203691Sbrucec	if(!p) {
308234183Sjhb	    rewind(f);
309234183Sjhb	} else {
310234183Sjhb	    pclose(f);
311159967Sobrien	    f = popen(p,"r");
312151907Sjhb	    if(!f) { warn("%s", p); return Exit_Broke; }
313228085Sphilip	}
314255323Sbryanv    }
315173491Sbenjsc
316203691Sbrucec    i=Pass3(f);
317125461Speter
318255736Sdavidchexit_and_close:
319233872Sjhb    if(!p)
320148263Speter        fclose(f);
321148263Speter    else
322148263Speter	pclose(f);
323233872Sjhb
324233872Sjhb    if(i)
325233872Sjhb	return i;
326234183Sjhb
327234183Sjhb    if (Verbose > 0)
328234183Sjhb	fprintf(stderr,"All done ok\n");
329233872Sjhb
330233872Sjhb    return Exit_Done;
331240098Sjhb}
332255323Sbryanv