]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/ctm/ctm/ctm.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / usr.sbin / ctm / ctm / ctm.c
1 /*
2  * ----------------------------------------------------------------------------
3  * "THE BEER-WARE LICENSE" (Revision 42):
4  * <phk@FreeBSD.org> 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$
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 <paths.h>
43 #include "ctm.h"
44
45 #define CTM_STATUS ".ctm_status"
46
47 extern int Proc(char *, unsigned applied);
48
49 int
50 main(int argc, char **argv)
51 {
52     int stat=0, err=0;
53     int c;
54     unsigned applied = 0;
55     FILE *statfile;
56     struct CTM_Filter *nfilter = NULL;  /* new filter */
57     u_char * basedir;
58
59     basedir = NULL;
60     Verbose = 1;
61     Paranoid = 1;
62     SetTime = 0;
63     KeepIt = 0;
64     ListIt = 0;
65     BackupFile = NULL;
66     TarCmd = TARCMD;
67     LastFilter = FilterList = NULL;
68     TmpDir = getenv("TMPDIR");
69     if (TmpDir == NULL)
70         TmpDir = strdup(_PATH_TMP);
71     setbuf(stderr,0);
72     setbuf(stdout,0);
73
74     while((c=getopt(argc,argv,"ab:B:cd:e:Fklm:pPqr:R:t:T:uV:vx:")) != -1) {
75         switch (c) {
76             case 'b': basedir = optarg; break; /* Base Directory */
77             case 'B': BackupFile = optarg;      break;
78             case 'c': CheckIt++;        break; /* Only check it */
79             case 'F': Force = 1;        break; 
80             case 'k': KeepIt++;         break; /* Don't do removes */
81             case 'l': ListIt++;         break; /* Only list actions and files */
82             case 'p': Paranoid--;       break; /* Less Paranoid */
83             case 'P': Paranoid++;       break; /* More Paranoid */
84             case 'q': Verbose--;        break; /* Quiet */
85             case 't': TarCmd = optarg;  break; /* archiver command */
86             case 'T': TmpDir = optarg;  break; /* set temporary directory */
87             case 'u': SetTime++;        break; /* Set timestamp on files */
88             case 'v': Verbose++;        break; /* Verbose */
89             case 'V': sscanf(optarg,"%d", &c); /* Verbose */
90                       Verbose += c;
91                       break;
92             case 'e':                           /* filter expressions */
93             case 'x':
94                 if (NULL == (nfilter =  Malloc(sizeof(struct CTM_Filter)))) {
95                         warnx("out of memory for expressions: \"%s\"", 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                         warnx("regular expression: \"%s\"", 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                 warnx("option '%c' requires an argument",optopt);
129                 stat++;
130                 break;
131             case '?':
132                 warnx("option '%c' not supported",optopt);
133                 stat++;
134                 break;
135             default:
136                 warnx("option '%c' not yet implemented",optopt);
137                 break;
138         }
139     }
140
141     if(stat) {
142         warnx("%d errors during option processing",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                 warnx("warning: %s not found", 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         warnx("exit(%d)",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
198 int
199 Proc(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) { warn("%s", p); return Exit_Garbage; }
214     } else {
215         p = 0;
216         f = fopen(filename,"r");
217     }
218     if(!f) {
219         warn("%s", 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;
232         FILE *f2;
233         int fd;
234
235         if (asprintf(&fn, "%s/CTMclient.XXXXXXXXXX", TmpDir) == -1) {
236             fprintf(stderr, "Cannot allocate memory\n");
237             fclose(f);
238             return Exit_Broke;
239         }
240         if ((fd = mkstemp(fn)) == -1 || (f2 = fdopen(fd, "w+")) == NULL) {
241             warn("%s", fn);
242             free(fn);
243             if (fd != -1)
244                 close(fd);
245             fclose(f);
246             return Exit_Broke;
247         }
248         unlink(fn);
249         if (Verbose > 0)
250             fprintf(stderr,"Writing tmp-file \"%s\"\n",fn);
251         free(fn);
252         while(EOF != (i=getc(f)))
253             if(EOF == putc(i,f2)) {
254                 fclose(f2);
255                 return Exit_Broke;
256             }
257         fclose(f);
258         f = f2;
259     }
260
261     if(!p)
262         rewind(f);
263
264     if((i=Pass1(f, applied)))
265         goto exit_and_close;
266
267     if(ListIt) {
268         i = Exit_Done;
269         goto exit_and_close;
270     }
271
272     if(!p) {
273         rewind(f);
274     } else {
275         pclose(f);
276         f = popen(p,"r");
277         if(!f) { warn("%s", p); return Exit_Broke; }
278     }
279
280     i=Pass2(f);
281
282     if(!p) {
283         rewind(f);
284     } else {
285         pclose(f);
286         f = popen(p,"r");
287         if(!f) { warn("%s", p); return Exit_Broke; }
288     }
289
290     if(i) {
291         if((!Force) || (i & ~Exit_Forcible))
292             goto exit_and_close;
293     }
294
295     if(CheckIt) {
296         if (Verbose > 0) 
297             fprintf(stderr,"All checks out ok.\n");
298         i = Exit_Done;
299         goto exit_and_close;
300     }
301    
302     /* backup files if requested */
303     if(BackupFile) {
304
305         i = PassB(f);
306
307         if(!p) {
308             rewind(f);
309         } else {
310             pclose(f);
311             f = popen(p,"r");
312             if(!f) { warn("%s", p); return Exit_Broke; }
313         }
314     }
315
316     i=Pass3(f);
317
318 exit_and_close:
319     if(!p)
320         fclose(f);
321     else
322         pclose(f);
323
324     if(i)
325         return i;
326
327     if (Verbose > 0)
328         fprintf(stderr,"All done ok\n");
329
330     return Exit_Done;
331 }