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 #include <sys/cdefs.h> 11 __RCSID("$NetBSD: wrapper.c,v 1.2 2016/05/17 14:00:09 christos Exp $"); 12 13 #include "cvs.h" 14 #include "getline.h" 15 16 /* 17 Original Author: athan@morgan.com <Andrew C. Athan> 2/1/94 18 Modified By: vdemarco@bou.shl.com 19 20 This package was written to support the NEXTSTEP concept of 21 "wrappers." These are essentially directories that are to be 22 treated as "files." This package allows such wrappers to be 23 "processed" on the way in and out of CVS. The intended use is to 24 wrap up a wrapper into a single tar, such that that tar can be 25 treated as a single binary file in CVS. To solve the problem 26 effectively, it was also necessary to be able to prevent rcsmerge 27 application at appropriate times. 28 29 ------------------ 30 Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers) 31 32 wildcard [option value][option value]... 33 34 where option is one of 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 (WrapperEntry *e,int temp); 79 void wrap_kill (void); 80 void wrap_kill_temp (void); 81 void wrap_free_entry (WrapperEntry *e); 82 void wrap_free_entry_internal (WrapperEntry *e); 83 void wrap_restore_saved (void); 84 85 void wrap_setup(void) 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 if (!current_parsed_root->isremote) 99 { 100 char *file; 101 102 /* Then add entries found in repository, if it exists. */ 103 file = Xasprintf ("%s/%s/%s", current_parsed_root->directory, 104 CVSROOTADM, CVSROOTADM_WRAPPER); 105 if (isfile (file)) 106 { 107 wrap_add_file(file,0); 108 } 109 free (file); 110 } 111 112 /* Then add entries found in home dir, (if user has one) and file 113 exists. */ 114 homedir = get_homedir (); 115 /* If we can't find a home directory, ignore ~/.cvswrappers. This may 116 make tracking down problems a bit of a pain, but on the other 117 hand it might be obnoxious to complain when CVS will function 118 just fine without .cvswrappers (and many users won't even know what 119 .cvswrappers is). */ 120 if (homedir != NULL) 121 { 122 char *file = strcat_filename_onto_homedir (homedir, CVSDOTWRAPPER); 123 if (isfile (file)) 124 { 125 wrap_add_file (file, 0); 126 } 127 free (file); 128 } 129 130 /* FIXME: calling wrap_add() below implies that the CVSWRAPPERS 131 * environment variable contains exactly one "wrapper" -- a line 132 * of the form 133 * 134 * FILENAME_PATTERN FLAG OPTS [ FLAG OPTS ...] 135 * 136 * This may disagree with the documentation, which states: 137 * 138 * `$CVSWRAPPERS' 139 * A whitespace-separated list of file name patterns that CVS 140 * should treat as wrappers. *Note Wrappers::. 141 * 142 * Does this mean the environment variable can hold multiple 143 * wrappers lines? If so, a single call to wrap_add() is 144 * insufficient. 145 */ 146 147 /* Then add entries found in CVSWRAPPERS environment variable. */ 148 wrap_add (getenv (WRAPPER_ENV), 0); 149 } 150 151 #ifdef CLIENT_SUPPORT 152 /* Send -W arguments for the wrappers to the server. The command must 153 be one that accepts them (e.g. update, import). */ 154 void 155 wrap_send (void) 156 { 157 int i; 158 159 for (i = 0; i < wrap_count + wrap_tempcount; ++i) 160 { 161 if (wrap_list[i]->tocvsFilter != NULL 162 || wrap_list[i]->fromcvsFilter != NULL) 163 /* For greater studliness we would print the offending option 164 and (more importantly) where we found it. */ 165 error (0, 0, "\ 166 -t and -f wrapper options are not supported remotely; ignored"); 167 if (wrap_list[i]->mergeMethod == WRAP_COPY) 168 /* For greater studliness we would print the offending option 169 and (more importantly) where we found it. */ 170 error (0, 0, "\ 171 -m wrapper option is not supported remotely; ignored"); 172 send_to_server ("Argument -W\012Argument ", 0); 173 send_to_server (wrap_list[i]->wildCard, 0); 174 send_to_server (" -k '", 0); 175 if (wrap_list[i]->rcsOption != NULL) 176 send_to_server (wrap_list[i]->rcsOption, 0); 177 else 178 send_to_server ("kv", 0); 179 send_to_server ("'\012", 0); 180 } 181 } 182 #endif /* CLIENT_SUPPORT */ 183 184 #if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) 185 /* Output wrapper entries in the format of cvswrappers lines. 186 * 187 * This is useful when one side of a client/server connection wants to 188 * send its wrappers to the other; since the receiving side would like 189 * to use wrap_add() to incorporate the wrapper, it's best if the 190 * entry arrives in this format. 191 * 192 * The entries are stored in `line', which is allocated here. Caller 193 * can free() it. 194 * 195 * If first_call_p is nonzero, then start afresh. */ 196 void 197 wrap_unparse_rcs_options (char **line, int first_call_p) 198 { 199 /* FIXME-reentrancy: we should design a reentrant interface, like 200 a callback which gets handed each wrapper (a multithreaded 201 server being the most concrete reason for this, but the 202 non-reentrant interface is fairly unnecessary/ugly). */ 203 static int i; 204 205 if (first_call_p) 206 i = 0; 207 208 if (i >= wrap_count + wrap_tempcount) { 209 *line = NULL; 210 return; 211 } 212 213 *line = Xasprintf ("%s -k '%s'", 214 wrap_list[i]->wildCard, 215 wrap_list[i]->rcsOption 216 ? wrap_list[i]->rcsOption : "kv"); 217 ++i; 218 } 219 #endif /* SERVER_SUPPORT || CLIENT_SUPPORT */ 220 221 /* 222 * Remove fmt str specifier other than %% or %s. And allow 223 * only max_s %s specifiers 224 */ 225 static void 226 wrap_clean_fmt_str(char *fmt, int max_s) 227 { 228 while (*fmt) { 229 if (fmt[0] == '%' && fmt[1]) 230 { 231 if (fmt[1] == '%') 232 fmt++; 233 else 234 if (fmt[1] == 's' && max_s > 0) 235 { 236 max_s--; 237 fmt++; 238 } else 239 *fmt = ' '; 240 } 241 fmt++; 242 } 243 } 244 245 /* 246 * Open a file and read lines, feeding each line to a line parser. Arrange 247 * for keeping a temporary list of wrappers at the end, if the "temp" 248 * argument is set. 249 */ 250 void 251 wrap_add_file (const char *file, int temp) 252 { 253 FILE *fp; 254 char *line = NULL; 255 size_t line_allocated = 0; 256 257 wrap_restore_saved (); 258 wrap_kill_temp (); 259 260 /* Load the file. */ 261 fp = CVS_FOPEN (file, "r"); 262 if (fp == NULL) 263 { 264 if (!existence_error (errno)) 265 error (0, errno, "cannot open %s", file); 266 return; 267 } 268 while (getline (&line, &line_allocated, fp) >= 0) 269 wrap_add (line, temp); 270 if (line) 271 free (line); 272 if (ferror (fp)) 273 error (0, errno, "cannot read %s", file); 274 if (fclose (fp) == EOF) 275 error (0, errno, "cannot close %s", file); 276 } 277 278 void 279 wrap_kill(void) 280 { 281 wrap_kill_temp(); 282 while(wrap_count) 283 wrap_free_entry(wrap_list[--wrap_count]); 284 } 285 286 void 287 wrap_kill_temp(void) 288 { 289 WrapperEntry **temps=wrap_list+wrap_count; 290 291 while(wrap_tempcount) 292 wrap_free_entry(temps[--wrap_tempcount]); 293 } 294 295 void 296 wrap_free_entry(WrapperEntry *e) 297 { 298 wrap_free_entry_internal(e); 299 free(e); 300 } 301 302 void 303 wrap_free_entry_internal(WrapperEntry *e) 304 { 305 free (e->wildCard); 306 if (e->tocvsFilter) 307 free (e->tocvsFilter); 308 if (e->fromcvsFilter) 309 free (e->fromcvsFilter); 310 if (e->rcsOption) 311 free (e->rcsOption); 312 } 313 314 void 315 wrap_restore_saved(void) 316 { 317 if(!wrap_saved_list) 318 return; 319 320 wrap_kill(); 321 322 free(wrap_list); 323 324 wrap_list=wrap_saved_list; 325 wrap_count=wrap_saved_count; 326 wrap_tempcount=wrap_saved_tempcount; 327 328 wrap_saved_list=NULL; 329 wrap_saved_count=0; 330 wrap_saved_tempcount=0; 331 } 332 333 void 334 wrap_add (char *line, int isTemp) 335 { 336 char *temp; 337 char ctemp; 338 WrapperEntry e; 339 char opt; 340 341 if (!line || line[0] == '#') 342 return; 343 344 memset (&e, 0, sizeof(e)); 345 346 /* Search for the wild card */ 347 while (*line && isspace ((unsigned char) *line)) 348 ++line; 349 for (temp = line; 350 *line && !isspace ((unsigned char) *line); 351 ++line) 352 ; 353 if(temp==line) 354 return; 355 356 ctemp=*line; 357 *line='\0'; 358 359 e.wildCard=xstrdup(temp); 360 *line=ctemp; 361 362 while(*line){ 363 /* Search for the option */ 364 while(*line && *line!='-') 365 ++line; 366 if(!*line) 367 break; 368 ++line; 369 if(!*line) 370 break; 371 opt=*line; 372 373 /* Search for the filter commandline */ 374 for(++line;*line && *line!='\'';++line); 375 if(!*line) 376 break; 377 378 for(temp=++line;*line && (*line!='\'' || line[-1]=='\\');++line) 379 ; 380 381 /* This used to "break;" (ignore the option) if there was a 382 single character between the single quotes (I'm guessing 383 that was accidental). Now it "break;"s if there are no 384 characters. I'm not sure either behavior is particularly 385 necessary--the current options might not require '' 386 arguments, but surely some future option legitimately 387 might. Also I'm not sure that ignoring the option is a 388 swift way to handle syntax errors in general. */ 389 if (line==temp) 390 break; 391 392 ctemp=*line; 393 *line='\0'; 394 switch(opt){ 395 case 'f': 396 /* Before this is reenabled, need to address the problem in 397 commit.c (see 398 <http://ximbiot.com/cvs/cvshome/docs/infowrapper.html>). */ 399 error (1, 0, 400 "-t/-f wrappers not supported by this version of CVS"); 401 402 if(e.fromcvsFilter) 403 free(e.fromcvsFilter); 404 /* FIXME: error message should say where the bad value 405 came from. */ 406 e.fromcvsFilter = 407 expand_path (temp, current_parsed_root->directory, false, 408 "<wrapper>", 0); 409 if (!e.fromcvsFilter) 410 error (1, 0, "Correct above errors first"); 411 break; 412 case 't': 413 /* Before this is reenabled, need to address the problem in 414 commit.c (see 415 <http://ximbiot.com/cvs/cvshome/docs/infowrapper.html>). */ 416 error (1, 0, 417 "-t/-f wrappers not supported by this version of CVS"); 418 419 if(e.tocvsFilter) 420 free(e.tocvsFilter); 421 /* FIXME: error message should say where the bad value 422 came from. */ 423 e.tocvsFilter = expand_path (temp, current_parsed_root->directory, 424 false, "<wrapper>", 0); 425 if (!e.tocvsFilter) 426 error (1, 0, "Correct above errors first"); 427 break; 428 case 'm': 429 if(*temp=='C' || *temp=='c') 430 e.mergeMethod=WRAP_COPY; 431 else 432 e.mergeMethod=WRAP_MERGE; 433 break; 434 case 'k': 435 if (e.rcsOption) 436 free (e.rcsOption); 437 e.rcsOption = strcmp (temp, "kv") ? xstrdup (temp) : NULL; 438 break; 439 default: 440 break; 441 } 442 *line=ctemp; 443 if(!*line)break; 444 ++line; 445 } 446 447 wrap_add_entry(&e, isTemp); 448 } 449 450 void 451 wrap_add_entry (WrapperEntry *e, int temp) 452 { 453 int x; 454 if (wrap_count + wrap_tempcount >= wrap_size) 455 { 456 wrap_size += WRAPPER_GROW; 457 wrap_list = xnrealloc (wrap_list, wrap_size, sizeof (WrapperEntry *)); 458 } 459 460 if (!temp && wrap_tempcount) 461 { 462 for (x = wrap_count + wrap_tempcount - 1; x >= wrap_count; --x) 463 wrap_list[x + 1] = wrap_list[x]; 464 } 465 466 x = (temp ? wrap_count + (wrap_tempcount++) : (wrap_count++)); 467 wrap_list[x] = xmalloc (sizeof (WrapperEntry)); 468 *wrap_list[x] = *e; 469 } 470 471 /* Return 1 if the given filename is a wrapper filename */ 472 int 473 wrap_name_has (const char *name, WrapMergeHas has) 474 { 475 int x,count=wrap_count+wrap_tempcount; 476 char *temp; 477 478 for(x=0;x<count;++x) 479 if (CVS_FNMATCH (wrap_list[x]->wildCard, name, 0) == 0){ 480 switch(has){ 481 case WRAP_TOCVS: 482 temp=wrap_list[x]->tocvsFilter; 483 break; 484 case WRAP_FROMCVS: 485 temp=wrap_list[x]->fromcvsFilter; 486 break; 487 case WRAP_RCSOPTION: 488 temp = wrap_list[x]->rcsOption; 489 break; 490 default: 491 abort (); 492 } 493 if(temp==NULL) 494 return (0); 495 else 496 return (1); 497 } 498 return (0); 499 } 500 501 static WrapperEntry *wrap_matching_entry (const char *); 502 503 static WrapperEntry * 504 wrap_matching_entry (const char *name) 505 { 506 int x,count=wrap_count+wrap_tempcount; 507 508 for(x=0;x<count;++x) 509 if (CVS_FNMATCH (wrap_list[x]->wildCard, name, 0) == 0) 510 return wrap_list[x]; 511 return NULL; 512 } 513 514 /* Return the RCS options for FILENAME in a newly malloc'd string. If 515 ASFLAG, then include "-k" at the beginning (e.g. "-kb"), otherwise 516 just give the option itself (e.g. "b"). */ 517 char * 518 wrap_rcsoption (const char *filename, int asflag) 519 { 520 WrapperEntry *e = wrap_matching_entry (filename); 521 522 if (e == NULL || e->rcsOption == NULL || (*e->rcsOption == '\0')) 523 return NULL; 524 525 return Xasprintf ("%s%s", asflag ? "-k" : "", e->rcsOption); 526 } 527 528 char * 529 wrap_tocvs_process_file(const char *fileName) 530 { 531 WrapperEntry *e=wrap_matching_entry(fileName); 532 static char *buf = NULL; 533 char *args; 534 535 if(e==NULL || e->tocvsFilter==NULL) 536 return NULL; 537 538 if (buf != NULL) 539 free (buf); 540 buf = cvs_temp_name (); 541 542 wrap_clean_fmt_str (e->tocvsFilter, 2); 543 args = Xasprintf (e->tocvsFilter, fileName, buf); 544 run_setup (args); 545 run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL | RUN_REALLY ); 546 free (args); 547 548 return buf; 549 } 550 551 int 552 wrap_merge_is_copy (const char *fileName) 553 { 554 WrapperEntry *e=wrap_matching_entry(fileName); 555 if(e==NULL || e->mergeMethod==WRAP_MERGE) 556 return 0; 557 558 return 1; 559 } 560 561 void 562 wrap_fromcvs_process_file(const char *fileName) 563 { 564 char *args; 565 WrapperEntry *e = wrap_matching_entry(fileName); 566 567 if (e != NULL && e->fromcvsFilter != NULL) 568 { 569 wrap_clean_fmt_str (e->fromcvsFilter, 1); 570 args = Xasprintf (e->fromcvsFilter, fileName); 571 run_setup (args); 572 run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); 573 free (args); 574 } 575 return; 576 } 577