1 #include "cvs.h" 2 3 /* 4 Original Author: athan@morgan.com <Andrew C. Athan> 2/1/94 5 Modified By: vdemarco@bou.shl.com 6 7 This package was written to support the NEXTSTEP concept of 8 "wrappers." These are essentially directories that are to be 9 treated as "files." This package allows such wrappers to be 10 "processed" on the way in and out of CVS. The intended use is to 11 wrap up a wrapper into a single tar, such that that tar can be 12 treated as a single binary file in CVS. To solve the problem 13 effectively, it was also necessary to be able to prevent rcsmerge 14 application at appropriate times. 15 16 ------------------ 17 Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers) 18 19 wildcard [option value][option value]... 20 21 where option is one of 22 -f from cvs filter value: path to filter 23 -t to cvs filter value: path to filter 24 -m update methodology value: MERGE or COPY 25 -k default -k rcs option to use on import or add 26 27 and value is a single-quote delimited value. 28 29 E.g: 30 *.nib -f 'gunzipuntar' -t 'targzip' -m 'COPY' 31 */ 32 33 34 typedef struct { 35 char *wildCard; 36 char *tocvsFilter; 37 char *fromcvsFilter; 38 char *rcsOption; 39 WrapMergeMethod mergeMethod; 40 } WrapperEntry; 41 42 static WrapperEntry **wrap_list=NULL; 43 static WrapperEntry **wrap_saved_list=NULL; 44 45 static int wrap_size=0; 46 static int wrap_count=0; 47 static int wrap_tempcount=0; 48 49 /* FIXME: wrap_saved_count is never set to any non-zero value. 50 wrap_name_has and wrap_matching_entry should be using 51 wrap_tempcount instead. I believe the consequence of this is that 52 .cvswrappers files are ignored (that was my own experience when I 53 tried to use one). If this bug is fixed, would be nice to write a 54 sanity.sh testcase for .cvswrappers files. */ 55 56 static int wrap_saved_count=0; 57 58 static int wrap_saved_tempcount=0; 59 60 #define WRAPPER_GROW 8 61 62 void wrap_add_entry PROTO((WrapperEntry *e,int temp)); 63 void wrap_kill PROTO((void)); 64 void wrap_kill_temp PROTO((void)); 65 void wrap_free_entry PROTO((WrapperEntry *e)); 66 void wrap_free_entry_internal PROTO((WrapperEntry *e)); 67 void wrap_restore_saved PROTO((void)); 68 69 void wrap_setup() 70 { 71 char file[PATH_MAX]; 72 struct passwd *pw; 73 74 #ifdef CLIENT_SUPPORT 75 if (!client_active) 76 #endif 77 { 78 /* Then add entries found in repository, if it exists. */ 79 (void) sprintf (file, "%s/%s/%s", CVSroot_directory, CVSROOTADM, 80 CVSROOTADM_WRAPPER); 81 if (isfile (file)) 82 { 83 wrap_add_file(file,0); 84 } 85 } 86 87 /* Then add entries found in home dir, (if user has one) and file 88 exists. (FIXME: I think this probably should be using 89 get_homedir, i.e. $HOME). */ 90 if ((pw = (struct passwd *) getpwuid (getuid ())) && pw->pw_dir) 91 { 92 (void) sprintf (file, "%s/%s", pw->pw_dir, CVSDOTWRAPPER); 93 if (isfile (file)) 94 { 95 wrap_add_file (file, 0); 96 } 97 } 98 99 /* Then add entries found in CVSWRAPPERS environment variable. */ 100 wrap_add (getenv (WRAPPER_ENV), 0); 101 } 102 103 #ifdef CLIENT_SUPPORT 104 /* Send -W arguments for the wrappers to the server. The command must 105 be one that accepts them (e.g. update, import). */ 106 void 107 wrap_send () 108 { 109 int i; 110 111 for (i = 0; i < wrap_count + wrap_tempcount; ++i) 112 { 113 if (wrap_list[i]->tocvsFilter != NULL 114 || wrap_list[i]->fromcvsFilter != NULL) 115 /* For greater studliness we would print the offending option 116 and (more importantly) where we found it. */ 117 error (0, 0, "\ 118 -t and -f wrapper options are not supported remotely; ignored"); 119 if (wrap_list[i]->mergeMethod == WRAP_COPY) 120 /* For greater studliness we would print the offending option 121 and (more importantly) where we found it. */ 122 error (0, 0, "\ 123 -m wrapper option is not supported remotely; ignored"); 124 if (wrap_list[i]->rcsOption != NULL) 125 { 126 send_to_server ("Argument -W\012Argument ", 0); 127 send_to_server (wrap_list[i]->wildCard, 0); 128 send_to_server (" -k '", 0); 129 send_to_server (wrap_list[i]->rcsOption, 0); 130 send_to_server ("'\012", 0); 131 } 132 } 133 } 134 #endif /* CLIENT_SUPPORT */ 135 136 /* 137 * Open a file and read lines, feeding each line to a line parser. Arrange 138 * for keeping a temporary list of wrappers at the end, if the "temp" 139 * argument is set. 140 */ 141 void 142 wrap_add_file (file, temp) 143 const char *file; 144 int temp; 145 { 146 FILE *fp; 147 char line[1024]; 148 149 wrap_restore_saved(); 150 wrap_kill_temp(); 151 152 /* load the file */ 153 if (!(fp = CVS_FOPEN (file, "r"))) 154 return; 155 while (fgets (line, sizeof (line), fp)) 156 wrap_add (line, temp); 157 (void) fclose (fp); 158 } 159 160 void 161 wrap_kill() 162 { 163 wrap_kill_temp(); 164 while(wrap_count) 165 wrap_free_entry(wrap_list[--wrap_count]); 166 } 167 168 void 169 wrap_kill_temp() 170 { 171 WrapperEntry **temps=wrap_list+wrap_count; 172 173 while(wrap_tempcount) 174 wrap_free_entry(temps[--wrap_tempcount]); 175 } 176 177 void 178 wrap_free_entry(e) 179 WrapperEntry *e; 180 { 181 wrap_free_entry_internal(e); 182 free(e); 183 } 184 185 void 186 wrap_free_entry_internal(e) 187 WrapperEntry *e; 188 { 189 free (e->wildCard); 190 if (e->tocvsFilter) 191 free (e->tocvsFilter); 192 if (e->fromcvsFilter) 193 free (e->fromcvsFilter); 194 if (e->rcsOption) 195 free (e->rcsOption); 196 } 197 198 void 199 wrap_restore_saved() 200 { 201 if(!wrap_saved_list) 202 return; 203 204 wrap_kill(); 205 206 free(wrap_list); 207 208 wrap_list=wrap_saved_list; 209 wrap_count=wrap_saved_count; 210 wrap_tempcount=wrap_saved_tempcount; 211 212 wrap_saved_list=NULL; 213 wrap_saved_count=0; 214 wrap_saved_tempcount=0; 215 } 216 217 void 218 wrap_add (line, isTemp) 219 char *line; 220 int isTemp; 221 { 222 char *temp; 223 char ctemp; 224 WrapperEntry e; 225 char opt; 226 227 if (!line || line[0] == '#') 228 return; 229 230 memset (&e, 0, sizeof(e)); 231 232 /* Search for the wild card */ 233 while(*line && isspace(*line)) 234 ++line; 235 for(temp=line;*line && !isspace(*line);++line) 236 ; 237 if(temp==line) 238 return; 239 240 ctemp=*line; 241 *line='\0'; 242 243 e.wildCard=xstrdup(temp); 244 *line=ctemp; 245 246 while(*line){ 247 /* Search for the option */ 248 while(*line && *line!='-') 249 ++line; 250 if(!*line) 251 break; 252 ++line; 253 if(!*line) 254 break; 255 opt=*line; 256 257 /* Search for the filter commandline */ 258 for(++line;*line && *line!='\'';++line); 259 if(!*line) 260 break; 261 262 for(temp=++line;*line && (*line!='\'' || line[-1]=='\\');++line) 263 ; 264 265 /* This used to "break;" (ignore the option) if there was a 266 single character between the single quotes (I'm guessing 267 that was accidental). Now it "break;"s if there are no 268 characters. I'm not sure either behavior is particularly 269 necessary--the current options might not require '' 270 arguments, but surely some future option legitimately 271 might. Also I'm not sure that ignoring the option is a 272 swift way to handle syntax errors in general. */ 273 if (line==temp) 274 break; 275 276 ctemp=*line; 277 *line='\0'; 278 switch(opt){ 279 case 'f': 280 if(e.fromcvsFilter) 281 free(e.fromcvsFilter); 282 /* FIXME: error message should say where the bad value 283 came from. */ 284 e.fromcvsFilter=expand_path (temp, "<wrapper>", 0); 285 if (!e.fromcvsFilter) 286 error (1, 0, "Correct above errors first"); 287 break; 288 case 't': 289 if(e.tocvsFilter) 290 free(e.tocvsFilter); 291 /* FIXME: error message should say where the bad value 292 came from. */ 293 e.tocvsFilter=expand_path (temp, "<wrapper>", 0); 294 if (!e.tocvsFilter) 295 error (1, 0, "Correct above errors first"); 296 break; 297 case 'm': 298 /* FIXME: look into whether this option is still relevant given 299 the 24 Jun 96 change to merge_file. */ 300 if(*temp=='C' || *temp=='c') 301 e.mergeMethod=WRAP_COPY; 302 else 303 e.mergeMethod=WRAP_MERGE; 304 break; 305 case 'k': 306 if (e.rcsOption) 307 free (e.rcsOption); 308 e.rcsOption = xstrdup (temp); 309 break; 310 default: 311 break; 312 } 313 *line=ctemp; 314 if(!*line)break; 315 ++line; 316 } 317 318 wrap_add_entry(&e, isTemp); 319 } 320 321 void 322 wrap_add_entry(e, temp) 323 WrapperEntry *e; 324 int temp; 325 { 326 int x; 327 if(wrap_count+wrap_tempcount>=wrap_size){ 328 wrap_size += WRAPPER_GROW; 329 wrap_list = (WrapperEntry **) xrealloc ((char *) wrap_list, 330 wrap_size * 331 sizeof (WrapperEntry *)); 332 } 333 334 if(!temp && wrap_tempcount){ 335 for(x=wrap_count+wrap_tempcount-1;x>=wrap_count;--x) 336 wrap_list[x+1]=wrap_list[x]; 337 } 338 339 x=(temp ? wrap_count+(wrap_tempcount++):(wrap_count++)); 340 wrap_list[x]=(WrapperEntry *)xmalloc(sizeof(WrapperEntry)); 341 wrap_list[x]->wildCard=e->wildCard; 342 wrap_list[x]->fromcvsFilter=e->fromcvsFilter; 343 wrap_list[x]->tocvsFilter=e->tocvsFilter; 344 wrap_list[x]->mergeMethod=e->mergeMethod; 345 wrap_list[x]->rcsOption = e->rcsOption; 346 } 347 348 /* Return 1 if the given filename is a wrapper filename */ 349 int 350 wrap_name_has (name,has) 351 const char *name; 352 WrapMergeHas has; 353 { 354 int x,count=wrap_count+wrap_saved_count; 355 char *temp; 356 357 for(x=0;x<count;++x) 358 if (fnmatch (wrap_list[x]->wildCard, name, 0) == 0){ 359 switch(has){ 360 case WRAP_TOCVS: 361 temp=wrap_list[x]->tocvsFilter; 362 break; 363 case WRAP_FROMCVS: 364 temp=wrap_list[x]->fromcvsFilter; 365 break; 366 case WRAP_RCSOPTION: 367 temp = wrap_list[x]->rcsOption; 368 break; 369 default: 370 abort (); 371 } 372 if(temp==NULL) 373 return (0); 374 else 375 return (1); 376 } 377 return (0); 378 } 379 380 WrapperEntry * 381 wrap_matching_entry (name) 382 const char *name; 383 { 384 int x,count=wrap_count+wrap_saved_count; 385 386 for(x=0;x<count;++x) 387 if (fnmatch (wrap_list[x]->wildCard, name, 0) == 0) 388 return wrap_list[x]; 389 return (WrapperEntry *)NULL; 390 } 391 392 /* Return the RCS options for FILENAME in a newly malloc'd string. If 393 ASFLAG, then include "-k" at the beginning (e.g. "-kb"), otherwise 394 just give the option itself (e.g. "b"). */ 395 char * 396 wrap_rcsoption (filename, asflag) 397 const char *filename; 398 int asflag; 399 { 400 WrapperEntry *e = wrap_matching_entry (filename); 401 char *buf; 402 403 if (e == NULL || e->rcsOption == NULL || (*e->rcsOption == '\0')) 404 return NULL; 405 406 buf = xmalloc (strlen (e->rcsOption) + 3); 407 if (asflag) 408 { 409 strcpy (buf, "-k"); 410 strcat (buf, e->rcsOption); 411 } 412 else 413 { 414 strcpy (buf, e->rcsOption); 415 } 416 return buf; 417 } 418 419 char * 420 wrap_tocvs_process_file(fileName) 421 const char *fileName; 422 { 423 WrapperEntry *e=wrap_matching_entry(fileName); 424 static char *buf = NULL; 425 426 if(e==NULL || e->tocvsFilter==NULL) 427 return NULL; 428 429 if (buf != NULL) 430 free (buf); 431 buf = cvs_temp_name (); 432 433 run_setup(e->tocvsFilter,fileName,buf); 434 run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY ); 435 436 return buf; 437 } 438 439 int 440 wrap_merge_is_copy (fileName) 441 const char *fileName; 442 { 443 WrapperEntry *e=wrap_matching_entry(fileName); 444 if(e==NULL || e->mergeMethod==WRAP_MERGE) 445 return 0; 446 447 return 1; 448 } 449 450 char * 451 wrap_fromcvs_process_file(fileName) 452 const char *fileName; 453 { 454 WrapperEntry *e=wrap_matching_entry(fileName); 455 static char buf[PATH_MAX]; 456 457 if(e==NULL || e->fromcvsFilter==NULL) 458 return NULL; 459 460 run_setup(e->fromcvsFilter,fileName); 461 run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL ); 462 return buf; 463 } 464