ctm.c revision 21673
1/*
2 * ----------------------------------------------------------------------------
3 * "THE BEER-WARE LICENSE" (Revision 42):
4 * <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
5 * can do whatever you want with this stuff. If we meet some day, and you think
6 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7 * ----------------------------------------------------------------------------
8 *
9 * $FreeBSD: head/usr.sbin/ctm/ctm/ctm.c 21673 1997-01-14 07:20:47Z jkh $
10 *
11 * This is the client program of 'CTM'.  It will apply a CTM-patch to a
12 * collection of files.
13 *
14 * Options we'd like to see:
15 *
16 * -a 			Attempt best effort.
17 * -d <int>		Debug TBD.
18 * -m <mail-addr>	Email me instead.
19 * -r <name>		Reconstruct file.
20 * -R <file>		Read list of files to reconstruct.
21 *
22 * Options we have:
23 * -b <dir>		Base-dir
24 * -B <file>		Backup to tar-file.
25 * -t 			Tar command (default as in TARCMD).
26 * -c			Check it out, don't do anything.
27 * -F      		Force
28 * -q 			Tell us less.
29 * -T <tmpdir>.		Temporary files.
30 * -u			Set all file modification times to the timestamp
31 * -v 			Tell us more.
32 * -V <level>		Tell us more level = number of -v
33 * -k			Keep files and directories that would have been removed.
34 * -l			List actions.
35 *
36 * Options we don't actually use:
37 * -p			Less paranoid.
38 * -P			Paranoid.
39 */
40
41#define EXTERN /* */
42#include "ctm.h"
43
44#define CTM_STATUS ".ctm_status"
45
46extern int Proc(char *, unsigned applied);
47
48int
49main(int argc, char **argv)
50{
51    int stat=0, err=0;
52    int c;
53    extern int optopt,optind;
54    extern char * optarg;
55    unsigned applied = 0;
56    FILE *statfile;
57    struct CTM_Filter *nfilter = NULL;	/* new filter */
58    u_char * basedir;
59
60    basedir = NULL;
61    Verbose = 1;
62    Paranoid = 1;
63    SetTime = 0;
64    KeepIt = 0;
65    ListIt = 0;
66    BackupFile = NULL;
67    TarCmd = TARCMD;
68    LastFilter = FilterList = NULL;
69    setbuf(stderr,0);
70    setbuf(stdout,0);
71
72    while((c=getopt(argc,argv,"ab:B:cd:e:Fklm:pPqr:R:t:T:uV:vx:")) != -1) {
73	switch (c) {
74	    case 'b': basedir = optarg;	break; /* Base Directory */
75	    case 'B': BackupFile = optarg;	break;
76	    case 'c': CheckIt++;	break; /* Only check it */
77	    case 'F': Force = 1;	break;
78	    case 'k': KeepIt++;		break; /* Don't do removes */
79	    case 'l': ListIt++;		break; /* Only list actions and files */
80	    case 'p': Paranoid--;	break; /* Less Paranoid */
81	    case 'P': Paranoid++;	break; /* More Paranoid */
82	    case 'q': Verbose--;	break; /* Quiet */
83	    case 't': TarCmd = optarg;	break; /* archiver command */
84	    case 'T': TmpDir = optarg;	break; /* set temporary directory */
85	    case 'u': SetTime++;	break; /* Set timestamp on files */
86	    case 'v': Verbose++;	break; /* Verbose */
87	    case 'V': sscanf(optarg,"%d", &c); /* Verbose */
88		      Verbose += c;
89		      break;
90	    case 'e':				/* filter expressions */
91	    case 'x':
92		if (NULL == (nfilter =  Malloc(sizeof(struct CTM_Filter)))) {
93			fprintf(stderr,
94				"Out of memory for expressions: \"%s\"\n",
95			  	optarg);
96			 stat++;
97			break;
98		}
99
100		(void) memset(nfilter, 0, sizeof(struct CTM_Filter));
101
102		if (0 != (err =
103			regcomp(&nfilter->CompiledRegex, optarg, REG_NOSUB))) {
104
105			char errmsg[128];
106
107			regerror(err, &nfilter->CompiledRegex, errmsg,
108				sizeof(errmsg));
109			fprintf(stderr, "Regular expression: \"%s\"\n", errmsg);
110			stat++;
111			break;
112		}
113
114		/* note whether the filter enables or disables on match */
115		nfilter->Action =
116			(('e' == c) ? CTM_FILTER_ENABLE : CTM_FILTER_DISABLE);
117
118		/* link in the expression into the list */
119	        nfilter->Next = NULL;
120		if (NULL == FilterList) {
121		  LastFilter = FilterList = nfilter; /* init head and tail */
122		} else {    /* place at tail */
123		  LastFilter->Next = nfilter;
124		  LastFilter = nfilter;
125		}
126		break;
127	    case ':':
128		fprintf(stderr,"Option '%c' requires an argument.\n",optopt);
129		stat++;
130		break;
131	    case '?':
132		fprintf(stderr,"Option '%c' not supported.\n",optopt);
133		stat++;
134		break;
135	    default:
136		fprintf(stderr,"Option '%c' not yet implemented.\n",optopt);
137		break;
138	}
139    }
140
141    if(stat) {
142	fprintf(stderr,"%d errors during option processing\n",stat);
143	return Exit_Pilot;
144    }
145    stat = Exit_Done;
146    argc -= optind;
147    argv += optind;
148
149    if (basedir == NULL) {
150	Buffer = (u_char *)Malloc(BUFSIZ + strlen(SUBSUFF) +1);
151	CatPtr = Buffer;
152	*Buffer  = '\0';
153    } else {
154	Buffer = (u_char *)Malloc(strlen(basedir)+ BUFSIZ + strlen(SUBSUFF) +1);
155	strcpy(Buffer, basedir);
156	CatPtr = Buffer + strlen(basedir);
157	if (CatPtr[-1] != '/') {
158		strcat(Buffer, "/");
159		CatPtr++;
160	}
161    }
162    strcat(Buffer, CTM_STATUS);
163
164    if(ListIt)
165	applied = 0;
166    else
167	if((statfile = fopen(Buffer, "r")) == NULL) {
168	    if (Verbose > 0)
169	    	fprintf(stderr, "Warning: %s not found.\n", Buffer);
170	} else {
171	    fscanf(statfile, "%*s %u", &applied);
172	    fclose(statfile);
173	}
174
175    if(!argc)
176	stat |= Proc("-", applied);
177
178    while(argc-- && stat == Exit_Done) {
179	stat |= Proc(*argv++, applied);
180	stat &= ~(Exit_Version | Exit_NoMatch);
181    }
182
183    if(stat == Exit_Done)
184	stat = Exit_OK;
185
186    if(Verbose > 0)
187	fprintf(stderr,"Exit(%d)\n",stat);
188
189    if (FilterList)
190	for (nfilter = FilterList; nfilter; ) {
191	    struct CTM_Filter *tmp = nfilter->Next;
192	    Free(nfilter);
193	    nfilter = tmp;
194	}
195    return stat;
196}
197
198int
199Proc(char *filename, unsigned applied)
200{
201    FILE *f;
202    int i;
203    char *p = strrchr(filename,'.');
204
205    if(!strcmp(filename,"-")) {
206	p = 0;
207	f = stdin;
208    } else if(p && (!strcmp(p,".gz") || !strcmp(p,".Z"))) {
209	p = alloca(20 + strlen(filename));
210	strcpy(p,"gunzip < ");
211	strcat(p,filename);
212	f = popen(p,"r");
213	if(!f) { perror(p); return Exit_Garbage; }
214    } else {
215	p = 0;
216	f = fopen(filename,"r");
217    }
218    if(!f) {
219	perror(filename);
220	return Exit_Garbage;
221    }
222
223    if(Verbose > 1)
224	fprintf(stderr,"Working on <%s>\n",filename);
225
226    Delete(FileName);
227    FileName = String(filename);
228
229    /* If we cannot seek, we're doomed, so copy to a tmp-file in that case */
230    if(!p &&  -1 == fseek(f,0,SEEK_END)) {
231	char *fn = tempnam(TmpDir,"CTMclient");
232	FILE *f2 = fopen(fn,"w+");
233	int i;
234
235	if(!f2) {
236	    perror(fn);
237	    fclose(f);
238	    return Exit_Broke;
239	}
240	unlink(fn);
241	if (Verbose > 0)
242	    fprintf(stderr,"Writing tmp-file \"%s\"\n",fn);
243	while(EOF != (i=getc(f)))
244	    if(EOF == putc(i,f2)) {
245		fclose(f2);
246		return Exit_Broke;
247	    }
248	fclose(f);
249	f = f2;
250    }
251
252    if(!p)
253	rewind(f);
254
255    if((i=Pass1(f, applied)))
256	goto exit_and_close;
257
258    if(ListIt) {
259	i = Exit_Done;
260	goto exit_and_close;
261    }
262
263    if(!p) {
264        rewind(f);
265    } else {
266	pclose(f);
267	f = popen(p,"r");
268	if(!f) { perror(p); return Exit_Broke; }
269    }
270
271    i=Pass2(f);
272
273    if(!p) {
274        rewind(f);
275    } else {
276	pclose(f);
277	f = popen(p,"r");
278	if(!f) { perror(p); return Exit_Broke; }
279    }
280
281    if(i) {
282	if((!Force) || (i & ~Exit_Forcible))
283	    goto exit_and_close;
284    }
285
286    if(CheckIt) {
287	if (Verbose > 0)
288	    fprintf(stderr,"All checks out ok.\n");
289	i = Exit_Done;
290	goto exit_and_close;
291    }
292
293    /* backup files if requested */
294    if(BackupFile) {
295
296	i = PassB(f);
297
298	if(!p) {
299	    rewind(f);
300	} else {
301	    pclose(f);
302	    f = popen(p,"r");
303	    if(!f) { perror(p); return Exit_Broke; }
304	}
305    }
306
307    i=Pass3(f);
308
309exit_and_close:
310    if(!p)
311        fclose(f);
312    else
313	pclose(f);
314
315    if(i)
316	return i;
317
318    if (Verbose > 0)
319	fprintf(stderr,"All done ok\n");
320
321    return Exit_Done;
322}
323