1 /* This program is free software; you can redistribute it and/or modify 2 it under the terms of the GNU General Public License as published by 3 the Free Software Foundation; either version 2, or (at your option) 4 any later version. 5 6 This program is distributed in the hope that it will be useful, 7 but WITHOUT ANY WARRANTY; without even the implied warranty of 8 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 GNU General Public License for more details. */ 10 11 #include "cvs.h" 12 #include "getline.h" 13 14 /* 15 Original Author: athan@morgan.com <Andrew C. Athan> 2/1/94 16 Modified By: vdemarco@bou.shl.com 17 18 This package was written to support the NEXTSTEP concept of 19 "wrappers." These are essentially directories that are to be 20 treated as "files." This package allows such wrappers to be 21 "processed" on the way in and out of CVS. The intended use is to 22 wrap up a wrapper into a single tar, such that that tar can be 23 treated as a single binary file in CVS. To solve the problem 24 effectively, it was also necessary to be able to prevent rcsmerge 25 application at appropriate times. 26 27 ------------------ 28 Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers) 29 30 wildcard [option value][option value]... 31 32 where option is one of 33 -f from cvs filter value: path to filter 34 -t to cvs filter value: path to filter 35 -m update methodology value: MERGE or COPY 36 -k default -k rcs option to use on import or add 37 38 and value is a single-quote delimited value. 39 40 E.g: 41 *.nib -f 'gunzipuntar' -t 'targzip' -m 'COPY' 42 */ 43 44 45 typedef struct { 46 char *wildCard; 47 char *tocvsFilter; 48 char *fromcvsFilter; 49 char *rcsOption; 50 WrapMergeMethod mergeMethod; 51 } WrapperEntry; 52 53 static WrapperEntry **wrap_list=NULL; 54 static WrapperEntry **wrap_saved_list=NULL; 55 56 static int wrap_size=0; 57 static int wrap_count=0; 58 static int wrap_tempcount=0; 59 60 /* FIXME: the relationship between wrap_count, wrap_tempcount, 61 * wrap_saved_count, and wrap_saved_tempcount is not entirely clear; 62 * it is certainly suspicious that wrap_saved_count is never set to a 63 * value other than zero! If the variable isn't being used, it should 64 * be removed. And in general, we should describe how temporary 65 * vs. permanent wrappers are implemented, and then make sure the 66 * implementation is actually doing that. 67 * 68 * Right now things seem to be working, but that's no guarantee there 69 * isn't a bug lurking somewhere in the murk. 70 */ 71 72 static int wrap_saved_count=0; 73 74 static int wrap_saved_tempcount=0; 75 76 #define WRAPPER_GROW 8 77 78 void wrap_add_entry PROTO((WrapperEntry *e,int temp)); 79 void wrap_kill PROTO((void)); 80 void wrap_kill_temp PROTO((void)); 81 void wrap_free_entry PROTO((WrapperEntry *e)); 82 void wrap_free_entry_internal PROTO((WrapperEntry *e)); 83 void wrap_restore_saved PROTO((void)); 84 85 void wrap_setup() 86 { 87 /* FIXME-reentrancy: if we do a multithreaded server, will need to 88 move this to a per-connection data structure, or better yet 89 think about a cleaner solution. */ 90 static int wrap_setup_already_done = 0; 91 char *homedir; 92 93 if (wrap_setup_already_done != 0) 94 return; 95 else 96 wrap_setup_already_done = 1; 97 98 #ifdef CLIENT_SUPPORT 99 if (!client_active) 100 #endif 101 { 102 char *file; 103 104 file = xmalloc (strlen (CVSroot_directory) 105 + sizeof (CVSROOTADM) 106 + sizeof (CVSROOTADM_WRAPPER) 107 + 10); 108 /* Then add entries found in repository, if it exists. */ 109 (void) sprintf (file, "%s/%s/%s", CVSroot_directory, CVSROOTADM, 110 CVSROOTADM_WRAPPER); 111 if (isfile (file)) 112 { 113 wrap_add_file(file,0); 114 } 115 free (file); 116 } 117 118 /* Then add entries found in home dir, (if user has one) and file 119 exists. */ 120 homedir = get_homedir (); 121 /* If we can't find a home directory, ignore ~/.cvswrappers. This may 122 make tracking down problems a bit of a pain, but on the other 123 hand it might be obnoxious to complain when CVS will function 124 just fine without .cvswrappers (and many users won't even know what 125 .cvswrappers is). */ 126 if (homedir != NULL) 127 { 128 char *file; 129 130 file = xmalloc (strlen (homedir) + sizeof (CVSDOTWRAPPER) + 10); 131 (void) sprintf (file, "%s/%s", homedir, CVSDOTWRAPPER); 132 if (isfile (file)) 133 { 134 wrap_add_file (file, 0); 135 } 136 free (file); 137 } 138 139 /* FIXME: calling wrap_add() below implies that the CVSWRAPPERS 140 * environment variable contains exactly one "wrapper" -- a line 141 * of the form 142 * 143 * FILENAME_PATTERN FLAG OPTS [ FLAG OPTS ...] 144 * 145 * This may disagree with the documentation, which states: 146 * 147 * `$CVSWRAPPERS' 148 * A whitespace-separated list of file name patterns that CVS 149 * should treat as wrappers. *Note Wrappers::. 150 * 151 * Does this mean the environment variable can hold multiple 152 * wrappers lines? If so, a single call to wrap_add() is 153 * insufficient. 154 */ 155 156 /* Then add entries found in CVSWRAPPERS environment variable. */ 157 wrap_add (getenv (WRAPPER_ENV), 0); 158 } 159 160 #ifdef CLIENT_SUPPORT 161 /* Send -W arguments for the wrappers to the server. The command must 162 be one that accepts them (e.g. update, import). */ 163 void 164 wrap_send () 165 { 166 int i; 167 168 for (i = 0; i < wrap_count + wrap_tempcount; ++i) 169 { 170 if (wrap_list[i]->tocvsFilter != NULL 171 || wrap_list[i]->fromcvsFilter != NULL) 172 /* For greater studliness we would print the offending option 173 and (more importantly) where we found it. */ 174 error (0, 0, "\ 175 -t and -f wrapper options are not supported remotely; ignored"); 176 if (wrap_list[i]->mergeMethod == WRAP_COPY) 177 /* For greater studliness we would print the offending option 178 and (more importantly) where we found it. */ 179 error (0, 0, "\ 180 -m wrapper option is not supported remotely; ignored"); 181 if (wrap_list[i]->rcsOption != NULL) 182 { 183 send_to_server ("Argument -W\012Argument ", 0); 184 send_to_server (wrap_list[i]->wildCard, 0); 185 send_to_server (" -k '", 0); 186 send_to_server (wrap_list[i]->rcsOption, 0); 187 send_to_server ("'\012", 0); 188 } 189 } 190 } 191 #endif /* CLIENT_SUPPORT */ 192 193 #if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) 194 /* Output wrapper entries in the format of cvswrappers lines. 195 * 196 * This is useful when one side of a client/server connection wants to 197 * send its wrappers to the other; since the receiving side would like 198 * to use wrap_add() to incorporate the wrapper, it's best if the 199 * entry arrives in this format. 200 * 201 * The entries are stored in `line', which is allocated here. Caller 202 * can free() it. 203 * 204 * If first_call_p is nonzero, then start afresh. */ 205 void 206 wrap_unparse_rcs_options (line, first_call_p) 207 char **line; 208 int first_call_p; 209 { 210 /* FIXME-reentrancy: we should design a reentrant interface, like 211 a callback which gets handed each wrapper (a multithreaded 212 server being the most concrete reason for this, but the 213 non-reentrant interface is fairly unnecessary/ugly). */ 214 static int i; 215 216 if (first_call_p) 217 i = 0; 218 219 for (; i < wrap_count + wrap_tempcount; ++i) 220 { 221 if (wrap_list[i]->rcsOption != NULL) 222 { 223 *line = xmalloc (strlen (wrap_list[i]->wildCard) 224 + strlen ("\t") 225 + strlen (" -k '") 226 + strlen (wrap_list[i]->rcsOption) 227 + strlen ("'") 228 + 1); /* leave room for '\0' */ 229 230 strcpy (*line, wrap_list[i]->wildCard); 231 strcat (*line, " -k '"); 232 strcat (*line, wrap_list[i]->rcsOption); 233 strcat (*line, "'"); 234 235 /* We're going to miss the increment because we return, so 236 do it by hand. */ 237 ++i; 238 239 return; 240 } 241 } 242 243 *line = NULL; 244 return; 245 } 246 #endif /* SERVER_SUPPORT || CLIENT_SUPPORT */ 247 248 /* 249 * Open a file and read lines, feeding each line to a line parser. Arrange 250 * for keeping a temporary list of wrappers at the end, if the "temp" 251 * argument is set. 252 */ 253 void 254 wrap_add_file (file, temp) 255 const char *file; 256 int temp; 257 { 258 FILE *fp; 259 char *line = NULL; 260 size_t line_allocated = 0; 261 262 wrap_restore_saved (); 263 wrap_kill_temp (); 264 265 /* Load the file. */ 266 fp = CVS_FOPEN (file, "r"); 267 if (fp == NULL) 268 { 269 if (!existence_error (errno)) 270 error (0, errno, "cannot open %s", file); 271 return; 272 } 273 while (getline (&line, &line_allocated, fp) >= 0) 274 wrap_add (line, temp); 275 if (line) 276 free (line); 277 if (ferror (fp)) 278 error (0, errno, "cannot read %s", file); 279 if (fclose (fp) == EOF) 280 error (0, errno, "cannot close %s", file); 281 } 282 283 void 284 wrap_kill() 285 { 286 wrap_kill_temp(); 287 while(wrap_count) 288 wrap_free_entry(wrap_list[--wrap_count]); 289 } 290 291 void 292 wrap_kill_temp() 293 { 294 WrapperEntry **temps=wrap_list+wrap_count; 295 296 while(wrap_tempcount) 297 wrap_free_entry(temps[--wrap_tempcount]); 298 } 299 300 void 301 wrap_free_entry(e) 302 WrapperEntry *e; 303 { 304 wrap_free_entry_internal(e); 305 free(e); 306 } 307 308 void 309 wrap_free_entry_internal(e) 310 WrapperEntry *e; 311 { 312 free (e->wildCard); 313 if (e->tocvsFilter) 314 free (e->tocvsFilter); 315 if (e->fromcvsFilter) 316 free (e->fromcvsFilter); 317 if (e->rcsOption) 318 free (e->rcsOption); 319 } 320 321 void 322 wrap_restore_saved() 323 { 324 if(!wrap_saved_list) 325 return; 326 327 wrap_kill(); 328 329 free(wrap_list); 330 331 wrap_list=wrap_saved_list; 332 wrap_count=wrap_saved_count; 333 wrap_tempcount=wrap_saved_tempcount; 334 335 wrap_saved_list=NULL; 336 wrap_saved_count=0; 337 wrap_saved_tempcount=0; 338 } 339 340 void 341 wrap_add (line, isTemp) 342 char *line; 343 int isTemp; 344 { 345 char *temp; 346 char ctemp; 347 WrapperEntry e; 348 char opt; 349 350 if (!line || line[0] == '#') 351 return; 352 353 memset (&e, 0, sizeof(e)); 354 355 /* Search for the wild card */ 356 while (*line && isspace ((unsigned char) *line)) 357 ++line; 358 for (temp = line; 359 *line && !isspace ((unsigned char) *line); 360 ++line) 361 ; 362 if(temp==line) 363 return; 364 365 ctemp=*line; 366 *line='\0'; 367 368 e.wildCard=xstrdup(temp); 369 *line=ctemp; 370 371 while(*line){ 372 /* Search for the option */ 373 while(*line && *line!='-') 374 ++line; 375 if(!*line) 376 break; 377 ++line; 378 if(!*line) 379 break; 380 opt=*line; 381 382 /* Search for the filter commandline */ 383 for(++line;*line && *line!='\'';++line); 384 if(!*line) 385 break; 386 387 for(temp=++line;*line && (*line!='\'' || line[-1]=='\\');++line) 388 ; 389 390 /* This used to "break;" (ignore the option) if there was a 391 single character between the single quotes (I'm guessing 392 that was accidental). Now it "break;"s if there are no 393 characters. I'm not sure either behavior is particularly 394 necessary--the current options might not require '' 395 arguments, but surely some future option legitimately 396 might. Also I'm not sure that ignoring the option is a 397 swift way to handle syntax errors in general. */ 398 if (line==temp) 399 break; 400 401 ctemp=*line; 402 *line='\0'; 403 switch(opt){ 404 case 'f': 405 /* Before this is reenabled, need to address the problem in 406 commit.c (see http://www.cyclic.com/cvs/dev-wrap.txt). */ 407 error (1, 0, 408 "-t/-f wrappers not supported by this version of CVS"); 409 410 if(e.fromcvsFilter) 411 free(e.fromcvsFilter); 412 /* FIXME: error message should say where the bad value 413 came from. */ 414 e.fromcvsFilter=expand_path (temp, "<wrapper>", 0); 415 if (!e.fromcvsFilter) 416 error (1, 0, "Correct above errors first"); 417 break; 418 case 't': 419 /* Before this is reenabled, need to address the problem in 420 commit.c (see http://www.cyclic.com/cvs/dev-wrap.txt). */ 421 error (1, 0, 422 "-t/-f wrappers not supported by this version of CVS"); 423 424 if(e.tocvsFilter) 425 free(e.tocvsFilter); 426 /* FIXME: error message should say where the bad value 427 came from. */ 428 e.tocvsFilter=expand_path (temp, "<wrapper>", 0); 429 if (!e.tocvsFilter) 430 error (1, 0, "Correct above errors first"); 431 break; 432 case 'm': 433 if(*temp=='C' || *temp=='c') 434 e.mergeMethod=WRAP_COPY; 435 else 436 e.mergeMethod=WRAP_MERGE; 437 break; 438 case 'k': 439 if (e.rcsOption) 440 free (e.rcsOption); 441 e.rcsOption = xstrdup (temp); 442 break; 443 default: 444 break; 445 } 446 *line=ctemp; 447 if(!*line)break; 448 ++line; 449 } 450 451 wrap_add_entry(&e, isTemp); 452 } 453 454 void 455 wrap_add_entry(e, temp) 456 WrapperEntry *e; 457 int temp; 458 { 459 int x; 460 if(wrap_count+wrap_tempcount>=wrap_size){ 461 wrap_size += WRAPPER_GROW; 462 wrap_list = (WrapperEntry **) xrealloc ((char *) wrap_list, 463 wrap_size * 464 sizeof (WrapperEntry *)); 465 } 466 467 if(!temp && wrap_tempcount){ 468 for(x=wrap_count+wrap_tempcount-1;x>=wrap_count;--x) 469 wrap_list[x+1]=wrap_list[x]; 470 } 471 472 x=(temp ? wrap_count+(wrap_tempcount++):(wrap_count++)); 473 wrap_list[x]=(WrapperEntry *)xmalloc(sizeof(WrapperEntry)); 474 wrap_list[x]->wildCard=e->wildCard; 475 wrap_list[x]->fromcvsFilter=e->fromcvsFilter; 476 wrap_list[x]->tocvsFilter=e->tocvsFilter; 477 wrap_list[x]->mergeMethod=e->mergeMethod; 478 wrap_list[x]->rcsOption = e->rcsOption; 479 } 480 481 /* Return 1 if the given filename is a wrapper filename */ 482 int 483 wrap_name_has (name,has) 484 const char *name; 485 WrapMergeHas has; 486 { 487 int x,count=wrap_count+wrap_tempcount; 488 char *temp; 489 490 for(x=0;x<count;++x) 491 if (CVS_FNMATCH (wrap_list[x]->wildCard, name, 0) == 0){ 492 switch(has){ 493 case WRAP_TOCVS: 494 temp=wrap_list[x]->tocvsFilter; 495 break; 496 case WRAP_FROMCVS: 497 temp=wrap_list[x]->fromcvsFilter; 498 break; 499 case WRAP_RCSOPTION: 500 temp = wrap_list[x]->rcsOption; 501 break; 502 default: 503 abort (); 504 } 505 if(temp==NULL) 506 return (0); 507 else 508 return (1); 509 } 510 return (0); 511 } 512 513 static WrapperEntry *wrap_matching_entry PROTO ((const char *)); 514 515 static WrapperEntry * 516 wrap_matching_entry (name) 517 const char *name; 518 { 519 int x,count=wrap_count+wrap_tempcount; 520 521 for(x=0;x<count;++x) 522 if (CVS_FNMATCH (wrap_list[x]->wildCard, name, 0) == 0) 523 return wrap_list[x]; 524 return (WrapperEntry *)NULL; 525 } 526 527 /* Return the RCS options for FILENAME in a newly malloc'd string. If 528 ASFLAG, then include "-k" at the beginning (e.g. "-kb"), otherwise 529 just give the option itself (e.g. "b"). */ 530 char * 531 wrap_rcsoption (filename, asflag) 532 const char *filename; 533 int asflag; 534 { 535 WrapperEntry *e = wrap_matching_entry (filename); 536 char *buf; 537 538 if (e == NULL || e->rcsOption == NULL || (*e->rcsOption == '\0')) 539 return NULL; 540 541 buf = xmalloc (strlen (e->rcsOption) + 3); 542 if (asflag) 543 { 544 strcpy (buf, "-k"); 545 strcat (buf, e->rcsOption); 546 } 547 else 548 { 549 strcpy (buf, e->rcsOption); 550 } 551 return buf; 552 } 553 554 char * 555 wrap_tocvs_process_file(fileName) 556 const char *fileName; 557 { 558 WrapperEntry *e=wrap_matching_entry(fileName); 559 static char *buf = NULL; 560 char *args; 561 562 if(e==NULL || e->tocvsFilter==NULL) 563 return NULL; 564 565 if (buf != NULL) 566 free (buf); 567 buf = cvs_temp_name (); 568 569 args = xmalloc (strlen (e->tocvsFilter) 570 + strlen (fileName) 571 + strlen (buf)); 572 /* FIXME: sprintf will blow up if the format string contains items other 573 than %s, or contains too many %s's. We should instead be parsing 574 e->tocvsFilter ourselves and giving a real error. */ 575 sprintf (args, e->tocvsFilter, fileName, buf); 576 run_setup (args); 577 run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY ); 578 free (args); 579 580 return buf; 581 } 582 583 int 584 wrap_merge_is_copy (fileName) 585 const char *fileName; 586 { 587 WrapperEntry *e=wrap_matching_entry(fileName); 588 if(e==NULL || e->mergeMethod==WRAP_MERGE) 589 return 0; 590 591 return 1; 592 } 593 594 void 595 wrap_fromcvs_process_file(fileName) 596 const char *fileName; 597 { 598 char *args; 599 WrapperEntry *e=wrap_matching_entry(fileName); 600 601 if(e==NULL || e->fromcvsFilter==NULL) 602 return; 603 604 args = xmalloc (strlen (e->fromcvsFilter) 605 + strlen (fileName)); 606 /* FIXME: sprintf will blow up if the format string contains items other 607 than %s, or contains too many %s's. We should instead be parsing 608 e->fromcvsFilter ourselves and giving a real error. */ 609 sprintf (args, e->fromcvsFilter, fileName); 610 run_setup (args); 611 run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL ); 612 free (args); 613 return; 614 } 615