xref: /openbsd/gnu/usr.bin/cvs/src/wrapper.c (revision 07ea8d15)
1 #include "cvs.h"
2 
3 /*
4   Original Author:  athan@morgan.com <Andrew C. Athan> 2/1/94
5   Modified By:      vdemarco@bou.shl.com
6 
7   This package was written to support the NEXTSTEP concept of
8   "wrappers."  These are essentially directories that are to be
9   treated as "files."  This package allows such wrappers to be
10   "processed" on the way in and out of CVS.  The intended use is to
11   wrap up a wrapper into a single tar, such that that tar can be
12   treated as a single binary file in CVS.  To solve the problem
13   effectively, it was also necessary to be able to prevent rcsmerge
14   application at appropriate times.
15 
16   ------------------
17   Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers)
18 
19   wildcard	[option value][option value]...
20 
21   where option is one of
22   -f		from cvs filter		value: path to filter
23   -t		to cvs filter		value: path to filter
24   -m		update methodology	value: MERGE or COPY
25   -k		default -k rcs option to use on import or add
26 
27   and value is a single-quote delimited value.
28 
29   E.g:
30   *.nib		-f 'gunzipuntar' -t 'targzip' -m 'COPY'
31 */
32 
33 
34 typedef struct {
35     char *wildCard;
36     char *tocvsFilter;
37     char *fromcvsFilter;
38     char *rcsOption;
39     WrapMergeMethod mergeMethod;
40 } WrapperEntry;
41 
42 static WrapperEntry **wrap_list=NULL;
43 static WrapperEntry **wrap_saved_list=NULL;
44 
45 static int wrap_size=0;
46 static int wrap_count=0;
47 static int wrap_tempcount=0;
48 
49 /* FIXME: wrap_saved_count is never set to any non-zero value.
50    wrap_name_has and wrap_matching_entry should be using
51    wrap_tempcount instead.  I believe the consequence of this is that
52    .cvswrappers files are ignored (that was my own experience when I
53    tried to use one).  If this bug is fixed, would be nice to write a
54    sanity.sh testcase for .cvswrappers files.  */
55 
56 static int wrap_saved_count=0;
57 
58 static int wrap_saved_tempcount=0;
59 
60 #define WRAPPER_GROW	8
61 
62 void wrap_add_entry PROTO((WrapperEntry *e,int temp));
63 void wrap_kill PROTO((void));
64 void wrap_kill_temp PROTO((void));
65 void wrap_free_entry PROTO((WrapperEntry *e));
66 void wrap_free_entry_internal PROTO((WrapperEntry *e));
67 void wrap_restore_saved PROTO((void));
68 
69 void wrap_setup()
70 {
71     char file[PATH_MAX];
72     struct passwd *pw;
73 
74 #ifdef CLIENT_SUPPORT
75     if (!client_active)
76 #endif
77     {
78 	/* Then add entries found in repository, if it exists.  */
79 	(void) sprintf (file, "%s/%s/%s", CVSroot_directory, CVSROOTADM,
80 			CVSROOTADM_WRAPPER);
81 	if (isfile (file))
82 	{
83 	    wrap_add_file(file,0);
84 	}
85     }
86 
87     /* Then add entries found in home dir, (if user has one) and file
88        exists.  (FIXME: I think this probably should be using
89        get_homedir, i.e. $HOME).  */
90     if ((pw = (struct passwd *) getpwuid (getuid ())) && pw->pw_dir)
91     {
92 	(void) sprintf (file, "%s/%s", pw->pw_dir, CVSDOTWRAPPER);
93 	if (isfile (file))
94 	{
95 	    wrap_add_file (file, 0);
96 	}
97     }
98 
99     /* Then add entries found in CVSWRAPPERS environment variable. */
100     wrap_add (getenv (WRAPPER_ENV), 0);
101 }
102 
103 #ifdef CLIENT_SUPPORT
104 /* Send -W arguments for the wrappers to the server.  The command must
105    be one that accepts them (e.g. update, import).  */
106 void
107 wrap_send ()
108 {
109     int i;
110 
111     for (i = 0; i < wrap_count + wrap_tempcount; ++i)
112     {
113 	if (wrap_list[i]->tocvsFilter != NULL
114 	    || wrap_list[i]->fromcvsFilter != NULL)
115 	    /* For greater studliness we would print the offending option
116 	       and (more importantly) where we found it.  */
117 	    error (0, 0, "\
118 -t and -f wrapper options are not supported remotely; ignored");
119 	if (wrap_list[i]->mergeMethod == WRAP_COPY)
120 	    /* For greater studliness we would print the offending option
121 	       and (more importantly) where we found it.  */
122 	    error (0, 0, "\
123 -m wrapper option is not supported remotely; ignored");
124 	if (wrap_list[i]->rcsOption != NULL)
125 	{
126 	    send_to_server ("Argument -W\012Argument ", 0);
127 	    send_to_server (wrap_list[i]->wildCard, 0);
128 	    send_to_server (" -k '", 0);
129 	    send_to_server (wrap_list[i]->rcsOption, 0);
130 	    send_to_server ("'\012", 0);
131 	}
132     }
133 }
134 #endif /* CLIENT_SUPPORT */
135 
136 /*
137  * Open a file and read lines, feeding each line to a line parser. Arrange
138  * for keeping a temporary list of wrappers at the end, if the "temp"
139  * argument is set.
140  */
141 void
142 wrap_add_file (file, temp)
143     const char *file;
144     int temp;
145 {
146     FILE *fp;
147     char line[1024];
148 
149     wrap_restore_saved();
150     wrap_kill_temp();
151 
152 	/* load the file */
153     if (!(fp = CVS_FOPEN (file, "r")))
154 	return;
155     while (fgets (line, sizeof (line), fp))
156 	wrap_add (line, temp);
157     (void) fclose (fp);
158 }
159 
160 void
161 wrap_kill()
162 {
163     wrap_kill_temp();
164     while(wrap_count)
165 	wrap_free_entry(wrap_list[--wrap_count]);
166 }
167 
168 void
169 wrap_kill_temp()
170 {
171     WrapperEntry **temps=wrap_list+wrap_count;
172 
173     while(wrap_tempcount)
174 	wrap_free_entry(temps[--wrap_tempcount]);
175 }
176 
177 void
178 wrap_free_entry(e)
179      WrapperEntry *e;
180 {
181     wrap_free_entry_internal(e);
182     free(e);
183 }
184 
185 void
186 wrap_free_entry_internal(e)
187     WrapperEntry *e;
188 {
189     free (e->wildCard);
190     if (e->tocvsFilter)
191 	free (e->tocvsFilter);
192     if (e->fromcvsFilter)
193 	free (e->fromcvsFilter);
194     if (e->rcsOption)
195 	free (e->rcsOption);
196 }
197 
198 void
199 wrap_restore_saved()
200 {
201     if(!wrap_saved_list)
202 	return;
203 
204     wrap_kill();
205 
206     free(wrap_list);
207 
208     wrap_list=wrap_saved_list;
209     wrap_count=wrap_saved_count;
210     wrap_tempcount=wrap_saved_tempcount;
211 
212     wrap_saved_list=NULL;
213     wrap_saved_count=0;
214     wrap_saved_tempcount=0;
215 }
216 
217 void
218 wrap_add (line, isTemp)
219    char *line;
220    int         isTemp;
221 {
222     char *temp;
223     char ctemp;
224     WrapperEntry e;
225     char opt;
226 
227     if (!line || line[0] == '#')
228 	return;
229 
230     memset (&e, 0, sizeof(e));
231 
232 	/* Search for the wild card */
233     while(*line && isspace(*line))
234 	++line;
235     for(temp=line;*line && !isspace(*line);++line)
236 	;
237     if(temp==line)
238 	return;
239 
240     ctemp=*line;
241     *line='\0';
242 
243     e.wildCard=xstrdup(temp);
244     *line=ctemp;
245 
246     while(*line){
247 	    /* Search for the option */
248 	while(*line && *line!='-')
249 	    ++line;
250 	if(!*line)
251 	    break;
252 	++line;
253 	if(!*line)
254 	    break;
255 	opt=*line;
256 
257 	    /* Search for the filter commandline */
258 	for(++line;*line && *line!='\'';++line);
259 	if(!*line)
260 	    break;
261 
262 	for(temp=++line;*line && (*line!='\'' || line[-1]=='\\');++line)
263 	    ;
264 
265 	/* This used to "break;" (ignore the option) if there was a
266 	   single character between the single quotes (I'm guessing
267 	   that was accidental).  Now it "break;"s if there are no
268 	   characters.  I'm not sure either behavior is particularly
269 	   necessary--the current options might not require ''
270 	   arguments, but surely some future option legitimately
271 	   might.  Also I'm not sure that ignoring the option is a
272 	   swift way to handle syntax errors in general.  */
273 	if (line==temp)
274 	    break;
275 
276 	ctemp=*line;
277 	*line='\0';
278 	switch(opt){
279 	case 'f':
280 	    if(e.fromcvsFilter)
281 		free(e.fromcvsFilter);
282 	    /* FIXME: error message should say where the bad value
283 	       came from.  */
284 	    e.fromcvsFilter=expand_path (temp, "<wrapper>", 0);
285             if (!e.fromcvsFilter)
286 		error (1, 0, "Correct above errors first");
287 	    break;
288 	case 't':
289 	    if(e.tocvsFilter)
290 		free(e.tocvsFilter);
291 	    /* FIXME: error message should say where the bad value
292 	       came from.  */
293 	    e.tocvsFilter=expand_path (temp, "<wrapper>", 0);
294             if (!e.tocvsFilter)
295 		error (1, 0, "Correct above errors first");
296 	    break;
297 	case 'm':
298 	    /* FIXME: look into whether this option is still relevant given
299 	       the 24 Jun 96 change to merge_file.  */
300 	    if(*temp=='C' || *temp=='c')
301 		e.mergeMethod=WRAP_COPY;
302 	    else
303 		e.mergeMethod=WRAP_MERGE;
304 	    break;
305 	case 'k':
306 	    if (e.rcsOption)
307 		free (e.rcsOption);
308 	    e.rcsOption = xstrdup (temp);
309 	    break;
310 	default:
311 	    break;
312 	}
313 	*line=ctemp;
314 	if(!*line)break;
315 	++line;
316     }
317 
318     wrap_add_entry(&e, isTemp);
319 }
320 
321 void
322 wrap_add_entry(e, temp)
323     WrapperEntry *e;
324     int temp;
325 {
326     int x;
327     if(wrap_count+wrap_tempcount>=wrap_size){
328 	wrap_size += WRAPPER_GROW;
329 	wrap_list = (WrapperEntry **) xrealloc ((char *) wrap_list,
330 						wrap_size *
331 						sizeof (WrapperEntry *));
332     }
333 
334     if(!temp && wrap_tempcount){
335 	for(x=wrap_count+wrap_tempcount-1;x>=wrap_count;--x)
336 	    wrap_list[x+1]=wrap_list[x];
337     }
338 
339     x=(temp ? wrap_count+(wrap_tempcount++):(wrap_count++));
340     wrap_list[x]=(WrapperEntry *)xmalloc(sizeof(WrapperEntry));
341     wrap_list[x]->wildCard=e->wildCard;
342     wrap_list[x]->fromcvsFilter=e->fromcvsFilter;
343     wrap_list[x]->tocvsFilter=e->tocvsFilter;
344     wrap_list[x]->mergeMethod=e->mergeMethod;
345     wrap_list[x]->rcsOption = e->rcsOption;
346 }
347 
348 /* Return 1 if the given filename is a wrapper filename */
349 int
350 wrap_name_has (name,has)
351     const char   *name;
352     WrapMergeHas  has;
353 {
354     int x,count=wrap_count+wrap_saved_count;
355     char *temp;
356 
357     for(x=0;x<count;++x)
358 	if (fnmatch (wrap_list[x]->wildCard, name, 0) == 0){
359 	    switch(has){
360 	    case WRAP_TOCVS:
361 		temp=wrap_list[x]->tocvsFilter;
362 		break;
363 	    case WRAP_FROMCVS:
364 		temp=wrap_list[x]->fromcvsFilter;
365 		break;
366 	    case WRAP_RCSOPTION:
367 		temp = wrap_list[x]->rcsOption;
368 		break;
369 	    default:
370 	        abort ();
371 	    }
372 	    if(temp==NULL)
373 		return (0);
374 	    else
375 		return (1);
376 	}
377     return (0);
378 }
379 
380 WrapperEntry *
381 wrap_matching_entry (name)
382     const char *name;
383 {
384     int x,count=wrap_count+wrap_saved_count;
385 
386     for(x=0;x<count;++x)
387 	if (fnmatch (wrap_list[x]->wildCard, name, 0) == 0)
388 	    return wrap_list[x];
389     return (WrapperEntry *)NULL;
390 }
391 
392 /* Return the RCS options for FILENAME in a newly malloc'd string.  If
393    ASFLAG, then include "-k" at the beginning (e.g. "-kb"), otherwise
394    just give the option itself (e.g. "b").  */
395 char *
396 wrap_rcsoption (filename, asflag)
397     const char *filename;
398     int asflag;
399 {
400     WrapperEntry *e = wrap_matching_entry (filename);
401     char *buf;
402 
403     if (e == NULL || e->rcsOption == NULL || (*e->rcsOption == '\0'))
404 	return NULL;
405 
406     buf = xmalloc (strlen (e->rcsOption) + 3);
407     if (asflag)
408     {
409 	strcpy (buf, "-k");
410 	strcat (buf, e->rcsOption);
411     }
412     else
413     {
414 	strcpy (buf, e->rcsOption);
415     }
416     return buf;
417 }
418 
419 char *
420 wrap_tocvs_process_file(fileName)
421     const char *fileName;
422 {
423     WrapperEntry *e=wrap_matching_entry(fileName);
424     static char *buf = NULL;
425 
426     if(e==NULL || e->tocvsFilter==NULL)
427 	return NULL;
428 
429     if (buf != NULL)
430 	free (buf);
431     buf = cvs_temp_name ();
432 
433     run_setup(e->tocvsFilter,fileName,buf);
434     run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY );
435 
436     return buf;
437 }
438 
439 int
440 wrap_merge_is_copy (fileName)
441     const char *fileName;
442 {
443     WrapperEntry *e=wrap_matching_entry(fileName);
444     if(e==NULL || e->mergeMethod==WRAP_MERGE)
445 	return 0;
446 
447     return 1;
448 }
449 
450 char *
451 wrap_fromcvs_process_file(fileName)
452     const char *fileName;
453 {
454     WrapperEntry *e=wrap_matching_entry(fileName);
455     static char buf[PATH_MAX];
456 
457     if(e==NULL || e->fromcvsFilter==NULL)
458 	return NULL;
459 
460     run_setup(e->fromcvsFilter,fileName);
461     run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL );
462     return buf;
463 }
464