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