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
wrap_setup(void)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
wrap_send(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
wrap_unparse_rcs_options(char ** line,int first_call_p)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
wrap_clean_fmt_str(char * fmt,int max_s)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
wrap_add_file(const char * file,int temp)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
wrap_kill(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
wrap_kill_temp(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
wrap_free_entry(WrapperEntry * e)296 wrap_free_entry(WrapperEntry *e)
297 {
298 wrap_free_entry_internal(e);
299 free(e);
300 }
301
302 void
wrap_free_entry_internal(WrapperEntry * e)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
wrap_restore_saved(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
wrap_add(char * line,int isTemp)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
wrap_add_entry(WrapperEntry * e,int temp)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
wrap_name_has(const char * name,WrapMergeHas has)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 *
wrap_matching_entry(const char * name)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 *
wrap_rcsoption(const char * filename,int asflag)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 *
wrap_tocvs_process_file(const char * fileName)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
wrap_merge_is_copy(const char * fileName)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
wrap_fromcvs_process_file(const char * fileName)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