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