2 * SPDX-License-Identifier: Beerware
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 * ----------------------------------------------------------------------------
13 * This is the client program of 'CTM'. It will apply a CTM-patch to a
14 * collection of files.
16 * Options we'd like to see:
18 * -a Attempt best effort.
20 * -m <mail-addr> Email me instead.
21 * -r <name> Reconstruct file.
22 * -R <file> Read list of files to reconstruct.
26 * -B <file> Backup to tar-file.
27 * -t Tar command (default as in TARCMD).
28 * -c Check it out, don't do anything.
31 * -T <tmpdir>. Temporary files.
32 * -u Set all file modification times to the timestamp
34 * -V <level> Tell us more level = number of -v
35 * -k Keep files and directories that would have been removed.
38 * Options we don't actually use:
47 #define CTM_STATUS ".ctm_status"
49 extern int Proc(char *, unsigned applied);
52 main(int argc, char **argv)
58 struct CTM_Filter *nfilter = NULL; /* new filter */
69 LastFilter = FilterList = NULL;
70 TmpDir = getenv("TMPDIR");
72 TmpDir = strdup(_PATH_TMP);
76 while((c=getopt(argc,argv,"ab:B:cd:e:Fklm:pPqr:R:t:T:uV:vx:")) != -1) {
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 */
94 case 'e': /* filter expressions */
96 if (NULL == (nfilter = Malloc(sizeof(struct CTM_Filter)))) {
97 warnx("out of memory for expressions: \"%s\"", optarg);
102 (void) memset(nfilter, 0, sizeof(struct CTM_Filter));
105 regcomp(&nfilter->CompiledRegex, optarg, REG_NOSUB))) {
109 regerror(err, &nfilter->CompiledRegex, errmsg,
111 warnx("regular expression: \"%s\"", errmsg);
116 /* note whether the filter enables or disables on match */
118 (('e' == c) ? CTM_FILTER_ENABLE : CTM_FILTER_DISABLE);
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;
130 warnx("option '%c' requires an argument",optopt);
134 warnx("option '%c' not supported",optopt);
138 warnx("option '%c' not yet implemented",optopt);
144 warnx("%d errors during option processing",stat);
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");
154 if (basedir == NULL) {
155 Buffer = (u_char *)Malloc(BUFSIZ + strlen(SUBSUFF) +1);
159 Buffer = (u_char *)Malloc(strlen(basedir)+ BUFSIZ + strlen(SUBSUFF) +1);
160 strcpy(Buffer, basedir);
161 CatPtr = Buffer + strlen(basedir);
162 if (CatPtr[-1] != '/') {
167 strcat(Buffer, CTM_STATUS);
172 if((statfile = fopen(Buffer, "r")) == NULL) {
174 warnx("warning: %s not found", Buffer);
176 fscanf(statfile, "%*s %u", &applied);
181 stat |= Proc("-", applied);
183 while(argc-- && stat == Exit_Done) {
184 stat |= Proc(*argv++, applied);
185 stat &= ~(Exit_Version | Exit_NoMatch);
188 if(stat == Exit_Done)
192 warnx("exit(%d)",stat);
195 for (nfilter = FilterList; nfilter; ) {
196 struct CTM_Filter *tmp = nfilter->Next;
204 Proc(char *filename, unsigned applied)
208 char *p = strrchr(filename,'.');
210 if(!strcmp(filename,"-")) {
213 } else if(p && (!strcmp(p,".gz") || !strcmp(p,".Z"))) {
214 p = alloca(20 + strlen(filename));
215 strcpy(p,"gunzip < ");
218 if(!f) { warn("%s", p); return Exit_Garbage; }
221 f = fopen(filename,"r");
224 warn("%s", filename);
229 fprintf(stderr,"Working on <%s>\n",filename);
232 FileName = String(filename);
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)) {
240 if (asprintf(&fn, "%s/CTMclient.XXXXXXXXXX", TmpDir) == -1) {
241 fprintf(stderr, "Cannot allocate memory\n");
245 if ((fd = mkstemp(fn)) == -1 || (f2 = fdopen(fd, "w+")) == NULL) {
255 fprintf(stderr,"Writing tmp-file \"%s\"\n",fn);
257 while(EOF != (i=getc(f)))
258 if(EOF == putc(i,f2)) {
269 if((i=Pass1(f, applied)))
282 if(!f) { warn("%s", p); return Exit_Broke; }
292 if(!f) { warn("%s", p); return Exit_Broke; }
296 if((!Force) || (i & ~Exit_Forcible))
302 fprintf(stderr,"All checks out ok.\n");
307 /* backup files if requested */
317 if(!f) { warn("%s", p); return Exit_Broke; }
333 fprintf(stderr,"All done ok\n");