]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/cvs/src/wrapper.c
This commit was generated by cvs2svn to compensate for changes in r53564,
[FreeBSD/FreeBSD.git] / contrib / cvs / src / wrapper.c
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
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 (!client_active)
100 #endif
101     {
102         char *file;
103
104         file = xmalloc (strlen (CVSroot_directory)
105                         + sizeof (CVSROOTADM)
106                         + sizeof (CVSROOTADM_WRAPPER)
107                         + 10);
108         /* Then add entries found in repository, if it exists.  */
109         (void) sprintf (file, "%s/%s/%s", CVSroot_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 (homedir != NULL)
122     {
123         char *file;
124
125         file = xmalloc (strlen (homedir) + sizeof (CVSDOTWRAPPER) + 10);
126         (void) sprintf (file, "%s/%s", homedir, CVSDOTWRAPPER);
127         if (isfile (file))
128         {
129             wrap_add_file (file, 0);
130         }
131         free (file);
132     }
133
134     /* FIXME: calling wrap_add() below implies that the CVSWRAPPERS
135      * environment variable contains exactly one "wrapper" -- a line
136      * of the form
137      * 
138      *    FILENAME_PATTERN      FLAG  OPTS [ FLAG OPTS ...]
139      *
140      * This may disagree with the documentation, which states:
141      * 
142      *   `$CVSWRAPPERS'
143      *      A whitespace-separated list of file name patterns that CVS
144      *      should treat as wrappers. *Note Wrappers::.
145      *
146      * Does this mean the environment variable can hold multiple
147      * wrappers lines?  If so, a single call to wrap_add() is
148      * insufficient.
149      */
150
151     /* Then add entries found in CVSWRAPPERS environment variable. */
152     wrap_add (getenv (WRAPPER_ENV), 0);
153 }
154
155 #ifdef CLIENT_SUPPORT
156 /* Send -W arguments for the wrappers to the server.  The command must
157    be one that accepts them (e.g. update, import).  */
158 void
159 wrap_send ()
160 {
161     int i;
162
163     for (i = 0; i < wrap_count + wrap_tempcount; ++i)
164     {
165         if (wrap_list[i]->tocvsFilter != NULL
166             || wrap_list[i]->fromcvsFilter != NULL)
167             /* For greater studliness we would print the offending option
168                and (more importantly) where we found it.  */
169             error (0, 0, "\
170 -t and -f wrapper options are not supported remotely; ignored");
171         if (wrap_list[i]->mergeMethod == WRAP_COPY)
172             /* For greater studliness we would print the offending option
173                and (more importantly) where we found it.  */
174             error (0, 0, "\
175 -m wrapper option is not supported remotely; ignored");
176         if (wrap_list[i]->rcsOption != NULL)
177         {
178             send_to_server ("Argument -W\012Argument ", 0);
179             send_to_server (wrap_list[i]->wildCard, 0);
180             send_to_server (" -k '", 0);
181             send_to_server (wrap_list[i]->rcsOption, 0);
182             send_to_server ("'\012", 0);
183         }
184     }
185 }
186 #endif /* CLIENT_SUPPORT */
187
188 #if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT)
189 /* Output wrapper entries in the format of cvswrappers lines.
190  *
191  * This is useful when one side of a client/server connection wants to
192  * send its wrappers to the other; since the receiving side would like
193  * to use wrap_add() to incorporate the wrapper, it's best if the
194  * entry arrives in this format.
195  *
196  * The entries are stored in `line', which is allocated here.  Caller
197  * can free() it.
198  *
199  * If first_call_p is nonzero, then start afresh.  */
200 void
201 wrap_unparse_rcs_options (line, first_call_p)
202     char **line;
203     int first_call_p;
204 {
205     /* FIXME-reentrancy: we should design a reentrant interface, like
206        a callback which gets handed each wrapper (a multithreaded
207        server being the most concrete reason for this, but the
208        non-reentrant interface is fairly unnecessary/ugly).  */
209     static int i;
210
211     if (first_call_p)
212         i = 0;
213
214     for (; i < wrap_count + wrap_tempcount; ++i)
215     {
216         if (wrap_list[i]->rcsOption != NULL)
217         {
218             *line = xmalloc (strlen (wrap_list[i]->wildCard)
219                              + strlen ("\t")
220                              + strlen (" -k '")
221                              + strlen (wrap_list[i]->rcsOption)
222                              + strlen ("'")
223                              + 1);  /* leave room for '\0' */
224             
225             strcpy (*line, wrap_list[i]->wildCard);
226             strcat (*line, " -k '");
227             strcat (*line, wrap_list[i]->rcsOption);
228             strcat (*line, "'");
229
230             /* We're going to miss the increment because we return, so
231                do it by hand. */
232             ++i;
233
234             return;
235         }
236     }
237
238     *line = NULL;
239     return;
240 }
241 #endif /* SERVER_SUPPORT || CLIENT_SUPPORT */
242
243 /*
244  * Open a file and read lines, feeding each line to a line parser. Arrange
245  * for keeping a temporary list of wrappers at the end, if the "temp"
246  * argument is set.
247  */
248 void
249 wrap_add_file (file, temp)
250     const char *file;
251     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()
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()
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(e)
297      WrapperEntry *e;
298 {
299     wrap_free_entry_internal(e);
300     free(e);
301 }
302
303 void
304 wrap_free_entry_internal(e)
305     WrapperEntry *e;
306 {
307     free (e->wildCard);
308     if (e->tocvsFilter)
309         free (e->tocvsFilter);
310     if (e->fromcvsFilter)
311         free (e->fromcvsFilter);
312     if (e->rcsOption)
313         free (e->rcsOption);
314 }
315
316 void
317 wrap_restore_saved()
318 {
319     if(!wrap_saved_list)
320         return;
321
322     wrap_kill();
323
324     free(wrap_list);
325
326     wrap_list=wrap_saved_list;
327     wrap_count=wrap_saved_count;
328     wrap_tempcount=wrap_saved_tempcount;
329
330     wrap_saved_list=NULL;
331     wrap_saved_count=0;
332     wrap_saved_tempcount=0;
333 }
334
335 void
336 wrap_add (line, isTemp)
337    char *line;
338    int         isTemp;
339 {
340     char *temp;
341     char ctemp;
342     WrapperEntry e;
343     char opt;
344
345     if (!line || line[0] == '#')
346         return;
347
348     memset (&e, 0, sizeof(e));
349
350         /* Search for the wild card */
351     while(*line && isspace(*line))
352         ++line;
353     for(temp=line;*line && !isspace(*line);++line)
354         ;
355     if(temp==line)
356         return;
357
358     ctemp=*line;
359     *line='\0';
360
361     e.wildCard=xstrdup(temp);
362     *line=ctemp;
363
364     while(*line){
365             /* Search for the option */
366         while(*line && *line!='-')
367             ++line;
368         if(!*line)
369             break;
370         ++line;
371         if(!*line)
372             break;
373         opt=*line;
374
375             /* Search for the filter commandline */
376         for(++line;*line && *line!='\'';++line);
377         if(!*line)
378             break;
379
380         for(temp=++line;*line && (*line!='\'' || line[-1]=='\\');++line)
381             ;
382
383         /* This used to "break;" (ignore the option) if there was a
384            single character between the single quotes (I'm guessing
385            that was accidental).  Now it "break;"s if there are no
386            characters.  I'm not sure either behavior is particularly
387            necessary--the current options might not require ''
388            arguments, but surely some future option legitimately
389            might.  Also I'm not sure that ignoring the option is a
390            swift way to handle syntax errors in general.  */
391         if (line==temp)
392             break;
393
394         ctemp=*line;
395         *line='\0';
396         switch(opt){
397         case 'f':
398             /* Before this is reenabled, need to address the problem in
399                commit.c (see http://www.cyclic.com/cvs/dev-wrap.txt).  */
400             error (1, 0,
401                    "-t/-f wrappers not supported by this version of CVS");
402
403             if(e.fromcvsFilter)
404                 free(e.fromcvsFilter);
405             /* FIXME: error message should say where the bad value
406                came from.  */
407             e.fromcvsFilter=expand_path (temp, "<wrapper>", 0);
408             if (!e.fromcvsFilter)
409                 error (1, 0, "Correct above errors first");
410             break;
411         case 't':
412             /* Before this is reenabled, need to address the problem in
413                commit.c (see http://www.cyclic.com/cvs/dev-wrap.txt).  */
414             error (1, 0,
415                    "-t/-f wrappers not supported by this version of CVS");
416
417             if(e.tocvsFilter)
418                 free(e.tocvsFilter);
419             /* FIXME: error message should say where the bad value
420                came from.  */
421             e.tocvsFilter=expand_path (temp, "<wrapper>", 0);
422             if (!e.tocvsFilter)
423                 error (1, 0, "Correct above errors first");
424             break;
425         case 'm':
426             /* FIXME: look into whether this option is still relevant given
427                the 24 Jun 96 change to merge_file.  */
428             if(*temp=='C' || *temp=='c')
429                 e.mergeMethod=WRAP_COPY;
430             else
431                 e.mergeMethod=WRAP_MERGE;
432             break;
433         case 'k':
434             if (e.rcsOption)
435                 free (e.rcsOption);
436             e.rcsOption = xstrdup (temp);
437             break;
438         default:
439             break;
440         }
441         *line=ctemp;
442         if(!*line)break;
443         ++line;
444     }
445
446     wrap_add_entry(&e, isTemp);
447 }
448
449 void
450 wrap_add_entry(e, temp)
451     WrapperEntry *e;
452     int temp;
453 {
454     int x;
455     if(wrap_count+wrap_tempcount>=wrap_size){
456         wrap_size += WRAPPER_GROW;
457         wrap_list = (WrapperEntry **) xrealloc ((char *) wrap_list,
458                                                 wrap_size *
459                                                 sizeof (WrapperEntry *));
460     }
461
462     if(!temp && wrap_tempcount){
463         for(x=wrap_count+wrap_tempcount-1;x>=wrap_count;--x)
464             wrap_list[x+1]=wrap_list[x];
465     }
466
467     x=(temp ? wrap_count+(wrap_tempcount++):(wrap_count++));
468     wrap_list[x]=(WrapperEntry *)xmalloc(sizeof(WrapperEntry));
469     wrap_list[x]->wildCard=e->wildCard;
470     wrap_list[x]->fromcvsFilter=e->fromcvsFilter;
471     wrap_list[x]->tocvsFilter=e->tocvsFilter;
472     wrap_list[x]->mergeMethod=e->mergeMethod;
473     wrap_list[x]->rcsOption = e->rcsOption;
474 }
475
476 /* Return 1 if the given filename is a wrapper filename */
477 int
478 wrap_name_has (name,has)
479     const char   *name;
480     WrapMergeHas  has;
481 {
482     int x,count=wrap_count+wrap_tempcount;
483     char *temp;
484
485     for(x=0;x<count;++x)
486         if (CVS_FNMATCH (wrap_list[x]->wildCard, name, 0) == 0){
487             switch(has){
488             case WRAP_TOCVS:
489                 temp=wrap_list[x]->tocvsFilter;
490                 break;
491             case WRAP_FROMCVS:
492                 temp=wrap_list[x]->fromcvsFilter;
493                 break;
494             case WRAP_RCSOPTION:
495                 temp = wrap_list[x]->rcsOption;
496                 break;
497             default:
498                 abort ();
499             }
500             if(temp==NULL)
501                 return (0);
502             else
503                 return (1);
504         }
505     return (0);
506 }
507
508 static WrapperEntry *wrap_matching_entry PROTO ((const char *));
509
510 static WrapperEntry *
511 wrap_matching_entry (name)
512     const char *name;
513 {
514     int x,count=wrap_count+wrap_tempcount;
515
516     for(x=0;x<count;++x)
517         if (CVS_FNMATCH (wrap_list[x]->wildCard, name, 0) == 0)
518             return wrap_list[x];
519     return (WrapperEntry *)NULL;
520 }
521
522 /* Return the RCS options for FILENAME in a newly malloc'd string.  If
523    ASFLAG, then include "-k" at the beginning (e.g. "-kb"), otherwise
524    just give the option itself (e.g. "b").  */
525 char *
526 wrap_rcsoption (filename, asflag)
527     const char *filename;
528     int asflag;
529 {
530     WrapperEntry *e = wrap_matching_entry (filename);
531     char *buf;
532
533     if (e == NULL || e->rcsOption == NULL || (*e->rcsOption == '\0'))
534         return NULL;
535
536     buf = xmalloc (strlen (e->rcsOption) + 3);
537     if (asflag)
538     {
539         strcpy (buf, "-k");
540         strcat (buf, e->rcsOption);
541     }
542     else
543     {
544         strcpy (buf, e->rcsOption);
545     }
546     return buf;
547 }
548
549 char *
550 wrap_tocvs_process_file(fileName)
551     const char *fileName;
552 {
553     WrapperEntry *e=wrap_matching_entry(fileName);
554     static char *buf = NULL;
555     char *args;
556
557     if(e==NULL || e->tocvsFilter==NULL)
558         return NULL;
559
560     if (buf != NULL)
561         free (buf);
562     buf = cvs_temp_name ();
563
564     args = xmalloc (strlen (e->tocvsFilter)
565                     + strlen (fileName)
566                     + strlen (buf));
567     /* FIXME: sprintf will blow up if the format string contains items other
568        than %s, or contains too many %s's.  We should instead be parsing
569        e->tocvsFilter ourselves and giving a real error.  */
570     sprintf (args, e->tocvsFilter, fileName, buf);
571     run_setup (args);
572     run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY );
573     free (args);
574
575     return buf;
576 }
577
578 int
579 wrap_merge_is_copy (fileName)
580     const char *fileName;
581 {
582     WrapperEntry *e=wrap_matching_entry(fileName);
583     if(e==NULL || e->mergeMethod==WRAP_MERGE)
584         return 0;
585
586     return 1;
587 }
588
589 void
590 wrap_fromcvs_process_file(fileName)
591     const char *fileName;
592 {
593     char *args;
594     WrapperEntry *e=wrap_matching_entry(fileName);
595
596     if(e==NULL || e->fromcvsFilter==NULL)
597         return;
598
599     args = xmalloc (strlen (e->fromcvsFilter)
600                     + strlen (fileName));
601     /* FIXME: sprintf will blow up if the format string contains items other
602        than %s, or contains too many %s's.  We should instead be parsing
603        e->fromcvsFilter ourselves and giving a real error.  */
604     sprintf (args, e->fromcvsFilter, fileName);
605     run_setup (args);
606     run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL );
607     free (args);
608     return;
609 }