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