xref: /netbsd/external/gpl2/xcvs/dist/src/wrapper.c (revision 3cd63638)
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