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