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 * ----------------------------------------------------------------------------
11 * This is the client program of 'CTM'. It will apply a CTM-patch to a
12 * collection of files.
14 * Options we'd like to see:
16 * -a Attempt best effort.
18 * -m <mail-addr> Email me instead.
19 * -r <name> Reconstruct file.
20 * -R <file> Read list of files to reconstruct.
24 * -B <file> Backup to tar-file.
25 * -t Tar command (default as in TARCMD).
26 * -c Check it out, don't do anything.
29 * -T <tmpdir>. Temporary files.
30 * -u Set all file modification times to the timestamp
32 * -V <level> Tell us more level = number of -v
33 * -k Keep files and directories that would have been removed.
36 * Options we don't actually use:
45 #define CTM_STATUS ".ctm_status"
47 extern int Proc(char *, unsigned applied);
50 main(int argc, char **argv)
56 struct CTM_Filter *nfilter = NULL; /* new filter */
67 LastFilter = FilterList = NULL;
68 TmpDir = getenv("TMPDIR");
70 TmpDir = strdup(_PATH_TMP);
74 while((c=getopt(argc,argv,"ab:B:cd:e:Fklm:pPqr:R:t:T:uV:vx:")) != -1) {
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 */
92 case 'e': /* filter expressions */
94 if (NULL == (nfilter = Malloc(sizeof(struct CTM_Filter)))) {
95 warnx("out of memory for expressions: \"%s\"", optarg);
100 (void) memset(nfilter, 0, sizeof(struct CTM_Filter));
103 regcomp(&nfilter->CompiledRegex, optarg, REG_NOSUB))) {
107 regerror(err, &nfilter->CompiledRegex, errmsg,
109 warnx("regular expression: \"%s\"", errmsg);
114 /* note whether the filter enables or disables on match */
116 (('e' == c) ? CTM_FILTER_ENABLE : CTM_FILTER_DISABLE);
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;
128 warnx("option '%c' requires an argument",optopt);
132 warnx("option '%c' not supported",optopt);
136 warnx("option '%c' not yet implemented",optopt);
142 warnx("%d errors during option processing",stat);
149 if (basedir == NULL) {
150 Buffer = (u_char *)Malloc(BUFSIZ + strlen(SUBSUFF) +1);
154 Buffer = (u_char *)Malloc(strlen(basedir)+ BUFSIZ + strlen(SUBSUFF) +1);
155 strcpy(Buffer, basedir);
156 CatPtr = Buffer + strlen(basedir);
157 if (CatPtr[-1] != '/') {
162 strcat(Buffer, CTM_STATUS);
167 if((statfile = fopen(Buffer, "r")) == NULL) {
169 warnx("warning: %s not found", Buffer);
171 fscanf(statfile, "%*s %u", &applied);
176 stat |= Proc("-", applied);
178 while(argc-- && stat == Exit_Done) {
179 stat |= Proc(*argv++, applied);
180 stat &= ~(Exit_Version | Exit_NoMatch);
183 if(stat == Exit_Done)
187 warnx("exit(%d)",stat);
190 for (nfilter = FilterList; nfilter; ) {
191 struct CTM_Filter *tmp = nfilter->Next;
199 Proc(char *filename, unsigned applied)
203 char *p = strrchr(filename,'.');
205 if(!strcmp(filename,"-")) {
208 } else if(p && (!strcmp(p,".gz") || !strcmp(p,".Z"))) {
209 p = alloca(20 + strlen(filename));
210 strcpy(p,"gunzip < ");
213 if(!f) { warn("%s", p); return Exit_Garbage; }
216 f = fopen(filename,"r");
219 warn("%s", filename);
224 fprintf(stderr,"Working on <%s>\n",filename);
227 FileName = String(filename);
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)) {
235 if (asprintf(&fn, "%s/CTMclient.XXXXXXXXXX", TmpDir) == -1) {
236 fprintf(stderr, "Cannot allocate memory\n");
240 if ((fd = mkstemp(fn)) == -1 || (f2 = fdopen(fd, "w+")) == NULL) {
250 fprintf(stderr,"Writing tmp-file \"%s\"\n",fn);
252 while(EOF != (i=getc(f)))
253 if(EOF == putc(i,f2)) {
264 if((i=Pass1(f, applied)))
277 if(!f) { warn("%s", p); return Exit_Broke; }
287 if(!f) { warn("%s", p); return Exit_Broke; }
291 if((!Force) || (i & ~Exit_Forcible))
297 fprintf(stderr,"All checks out ok.\n");
302 /* backup files if requested */
312 if(!f) { warn("%s", p); return Exit_Broke; }
328 fprintf(stderr,"All done ok\n");