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 47extern int Proc(char *, unsigned applied); 48 49int 50main(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 198int 199Proc(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 318exit_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} 332