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
wrap_setup()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 (!current_parsed_root->isremote)
100 #endif
101 {
102 char *file;
103
104 file = xmalloc (strlen (current_parsed_root->directory)
105 + sizeof (CVSROOTADM)
106 + sizeof (CVSROOTADM_WRAPPER)
107 + 3);
108 /* Then add entries found in repository, if it exists. */
109 (void) sprintf (file, "%s/%s/%s", current_parsed_root->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
wrap_send()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
wrap_unparse_rcs_options(line,first_call_p)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 * Remove fmt str specifier other than %% or %s. And allow
250 * only max_s %s specifiers
251 */
252 void
wrap_clean_fmt_str(char * fmt,int max_s)253 wrap_clean_fmt_str(char *fmt, int max_s)
254 {
255 while (*fmt) {
256 if (fmt[0] == '%' && fmt[1])
257 {
258 if (fmt[1] == '%')
259 fmt++;
260 else
261 if (fmt[1] == 's' && max_s > 0)
262 {
263 max_s--;
264 fmt++;
265 } else
266 *fmt = ' ';
267 }
268 fmt++;
269 }
270 }
271
272 /*
273 * Open a file and read lines, feeding each line to a line parser. Arrange
274 * for keeping a temporary list of wrappers at the end, if the "temp"
275 * argument is set.
276 */
277 void
wrap_add_file(file,temp)278 wrap_add_file (file, temp)
279 const char *file;
280 int temp;
281 {
282 FILE *fp;
283 char *line = NULL;
284 size_t line_allocated = 0;
285
286 wrap_restore_saved ();
287 wrap_kill_temp ();
288
289 /* Load the file. */
290 fp = CVS_FOPEN (file, "r");
291 if (fp == NULL)
292 {
293 if (!existence_error (errno))
294 error (0, errno, "cannot open %s", file);
295 return;
296 }
297 while (get_line (&line, &line_allocated, fp) >= 0)
298 wrap_add (line, temp);
299 if (line)
300 free (line);
301 if (ferror (fp))
302 error (0, errno, "cannot read %s", file);
303 if (fclose (fp) == EOF)
304 error (0, errno, "cannot close %s", file);
305 }
306
307 void
wrap_kill()308 wrap_kill()
309 {
310 wrap_kill_temp();
311 while(wrap_count)
312 wrap_free_entry(wrap_list[--wrap_count]);
313 }
314
315 void
wrap_kill_temp()316 wrap_kill_temp()
317 {
318 WrapperEntry **temps=wrap_list+wrap_count;
319
320 while(wrap_tempcount)
321 wrap_free_entry(temps[--wrap_tempcount]);
322 }
323
324 void
wrap_free_entry(e)325 wrap_free_entry(e)
326 WrapperEntry *e;
327 {
328 wrap_free_entry_internal(e);
329 free(e);
330 }
331
332 void
wrap_free_entry_internal(e)333 wrap_free_entry_internal(e)
334 WrapperEntry *e;
335 {
336 free (e->wildCard);
337 if (e->tocvsFilter)
338 free (e->tocvsFilter);
339 if (e->fromcvsFilter)
340 free (e->fromcvsFilter);
341 if (e->rcsOption)
342 free (e->rcsOption);
343 }
344
345 void
wrap_restore_saved()346 wrap_restore_saved()
347 {
348 if(!wrap_saved_list)
349 return;
350
351 wrap_kill();
352
353 free(wrap_list);
354
355 wrap_list=wrap_saved_list;
356 wrap_count=wrap_saved_count;
357 wrap_tempcount=wrap_saved_tempcount;
358
359 wrap_saved_list=NULL;
360 wrap_saved_count=0;
361 wrap_saved_tempcount=0;
362 }
363
364 void
wrap_add(line,isTemp)365 wrap_add (line, isTemp)
366 char *line;
367 int isTemp;
368 {
369 char *temp;
370 char ctemp;
371 WrapperEntry e;
372 char opt;
373
374 if (!line || line[0] == '#')
375 return;
376
377 memset (&e, 0, sizeof(e));
378
379 /* Search for the wild card */
380 while (*line && isspace ((unsigned char) *line))
381 ++line;
382 for (temp = line;
383 *line && !isspace ((unsigned char) *line);
384 ++line)
385 ;
386 if(temp==line)
387 return;
388
389 ctemp=*line;
390 *line='\0';
391
392 e.wildCard=xstrdup(temp);
393 *line=ctemp;
394
395 while(*line){
396 /* Search for the option */
397 while(*line && *line!='-')
398 ++line;
399 if(!*line)
400 break;
401 ++line;
402 if(!*line)
403 break;
404 opt=*line;
405
406 /* Search for the filter commandline */
407 for(++line;*line && *line!='\'';++line);
408 if(!*line)
409 break;
410
411 for(temp=++line;*line && (*line!='\'' || line[-1]=='\\');++line)
412 ;
413
414 /* This used to "break;" (ignore the option) if there was a
415 single character between the single quotes (I'm guessing
416 that was accidental). Now it "break;"s if there are no
417 characters. I'm not sure either behavior is particularly
418 necessary--the current options might not require ''
419 arguments, but surely some future option legitimately
420 might. Also I'm not sure that ignoring the option is a
421 swift way to handle syntax errors in general. */
422 if (line==temp)
423 break;
424
425 ctemp=*line;
426 *line='\0';
427 switch(opt){
428 case 'f':
429 /* Before this is reenabled, need to address the problem in
430 commit.c (see http://www.cyclic.com/cvs/dev-wrap.txt). */
431 error (1, 0,
432 "-t/-f wrappers not supported by this version of CVS");
433
434 if(e.fromcvsFilter)
435 free(e.fromcvsFilter);
436 /* FIXME: error message should say where the bad value
437 came from. */
438 e.fromcvsFilter=expand_path (temp, "<wrapper>", 0);
439 if (!e.fromcvsFilter)
440 error (1, 0, "Correct above errors first");
441 break;
442 case 't':
443 /* Before this is reenabled, need to address the problem in
444 commit.c (see http://www.cyclic.com/cvs/dev-wrap.txt). */
445 error (1, 0,
446 "-t/-f wrappers not supported by this version of CVS");
447
448 if(e.tocvsFilter)
449 free(e.tocvsFilter);
450 /* FIXME: error message should say where the bad value
451 came from. */
452 e.tocvsFilter=expand_path (temp, "<wrapper>", 0);
453 if (!e.tocvsFilter)
454 error (1, 0, "Correct above errors first");
455 break;
456 case 'm':
457 if(*temp=='C' || *temp=='c')
458 e.mergeMethod=WRAP_COPY;
459 else
460 e.mergeMethod=WRAP_MERGE;
461 break;
462 case 'k':
463 if (e.rcsOption)
464 free (e.rcsOption);
465 e.rcsOption = xstrdup (temp);
466 break;
467 default:
468 break;
469 }
470 *line=ctemp;
471 if(!*line)break;
472 ++line;
473 }
474
475 wrap_add_entry(&e, isTemp);
476 }
477
478 void
wrap_add_entry(e,temp)479 wrap_add_entry(e, temp)
480 WrapperEntry *e;
481 int temp;
482 {
483 int x;
484 if(wrap_count+wrap_tempcount>=wrap_size){
485 wrap_size += WRAPPER_GROW;
486 wrap_list = (WrapperEntry **) xrealloc ((char *) wrap_list,
487 wrap_size *
488 sizeof (WrapperEntry *));
489 }
490
491 if(!temp && wrap_tempcount){
492 for(x=wrap_count+wrap_tempcount-1;x>=wrap_count;--x)
493 wrap_list[x+1]=wrap_list[x];
494 }
495
496 x=(temp ? wrap_count+(wrap_tempcount++):(wrap_count++));
497 wrap_list[x]=(WrapperEntry *)xmalloc(sizeof(WrapperEntry));
498 wrap_list[x]->wildCard=e->wildCard;
499 wrap_list[x]->fromcvsFilter=e->fromcvsFilter;
500 wrap_list[x]->tocvsFilter=e->tocvsFilter;
501 wrap_list[x]->mergeMethod=e->mergeMethod;
502 wrap_list[x]->rcsOption = e->rcsOption;
503 }
504
505 /* Return 1 if the given filename is a wrapper filename */
506 int
wrap_name_has(name,has)507 wrap_name_has (name,has)
508 const char *name;
509 WrapMergeHas has;
510 {
511 int x,count=wrap_count+wrap_tempcount;
512 char *temp;
513
514 for(x=0;x<count;++x)
515 if (CVS_FNMATCH (wrap_list[x]->wildCard, name, 0) == 0){
516 switch(has){
517 case WRAP_TOCVS:
518 temp=wrap_list[x]->tocvsFilter;
519 break;
520 case WRAP_FROMCVS:
521 temp=wrap_list[x]->fromcvsFilter;
522 break;
523 case WRAP_RCSOPTION:
524 temp = wrap_list[x]->rcsOption;
525 break;
526 default:
527 abort ();
528 }
529 if(temp==NULL)
530 return (0);
531 else
532 return (1);
533 }
534 return (0);
535 }
536
537 static WrapperEntry *wrap_matching_entry PROTO ((const char *));
538
539 static WrapperEntry *
wrap_matching_entry(name)540 wrap_matching_entry (name)
541 const char *name;
542 {
543 int x,count=wrap_count+wrap_tempcount;
544
545 for(x=0;x<count;++x)
546 if (CVS_FNMATCH (wrap_list[x]->wildCard, name, 0) == 0)
547 return wrap_list[x];
548 return (WrapperEntry *)NULL;
549 }
550
551 /* Return the RCS options for FILENAME in a newly malloc'd string. If
552 ASFLAG, then include "-k" at the beginning (e.g. "-kb"), otherwise
553 just give the option itself (e.g. "b"). */
554 char *
wrap_rcsoption(filename,asflag)555 wrap_rcsoption (filename, asflag)
556 const char *filename;
557 int asflag;
558 {
559 WrapperEntry *e = wrap_matching_entry (filename);
560 char *buf;
561
562 if (e == NULL || e->rcsOption == NULL || (*e->rcsOption == '\0'))
563 return NULL;
564
565 buf = xmalloc (strlen (e->rcsOption) + 3);
566 if (asflag)
567 {
568 strcpy (buf, "-k");
569 strcat (buf, e->rcsOption);
570 }
571 else
572 {
573 strcpy (buf, e->rcsOption);
574 }
575 return buf;
576 }
577
578 char *
wrap_tocvs_process_file(fileName)579 wrap_tocvs_process_file(fileName)
580 const char *fileName;
581 {
582 WrapperEntry *e=wrap_matching_entry(fileName);
583 static char *buf = NULL;
584 char *args;
585
586 if(e==NULL || e->tocvsFilter==NULL)
587 return NULL;
588
589 if (buf != NULL)
590 free (buf);
591 buf = cvs_temp_name ();
592
593 args = xmalloc (strlen (e->tocvsFilter)
594 + strlen (fileName)
595 + strlen (buf));
596
597 wrap_clean_fmt_str(e->tocvsFilter, 2);
598 sprintf (args, e->tocvsFilter, fileName, buf);
599 run_setup (args);
600 run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY );
601 free (args);
602
603 return buf;
604 }
605
606 int
wrap_merge_is_copy(fileName)607 wrap_merge_is_copy (fileName)
608 const char *fileName;
609 {
610 WrapperEntry *e=wrap_matching_entry(fileName);
611 if(e==NULL || e->mergeMethod==WRAP_MERGE)
612 return 0;
613
614 return 1;
615 }
616
617 void
wrap_fromcvs_process_file(fileName)618 wrap_fromcvs_process_file(fileName)
619 const char *fileName;
620 {
621 char *args;
622 WrapperEntry *e=wrap_matching_entry(fileName);
623
624 if(e==NULL || e->fromcvsFilter==NULL)
625 return;
626
627 args = xmalloc (strlen (e->fromcvsFilter)
628 + strlen (fileName));
629
630 wrap_clean_fmt_str(e->fromcvsFilter, 1);
631 sprintf (args, e->fromcvsFilter, fileName);
632 run_setup (args);
633 run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL );
634 free (args);
635 return;
636 }
637