]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/cvs/src/wrapper.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.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   -m            update methodology      value: MERGE or COPY
34   -k            default -k rcs option to use on import or add
35
36   and value is a single-quote delimited value.
37
38   E.g:
39   *.nib         -f 'gunzipuntar' -t 'targzip' -m 'COPY'
40 */
41
42
43 typedef struct {
44     char *wildCard;
45     char *tocvsFilter;
46     char *fromcvsFilter;
47     char *rcsOption;
48     WrapMergeMethod mergeMethod;
49 } WrapperEntry;
50
51 static WrapperEntry **wrap_list=NULL;
52 static WrapperEntry **wrap_saved_list=NULL;
53
54 static int wrap_size=0;
55 static int wrap_count=0;
56 static int wrap_tempcount=0;
57
58 /* FIXME: the relationship between wrap_count, wrap_tempcount,
59  * wrap_saved_count, and wrap_saved_tempcount is not entirely clear;
60  * it is certainly suspicious that wrap_saved_count is never set to a
61  * value other than zero!  If the variable isn't being used, it should
62  * be removed.  And in general, we should describe how temporary
63  * vs. permanent wrappers are implemented, and then make sure the
64  * implementation is actually doing that.
65  *
66  * Right now things seem to be working, but that's no guarantee there
67  * isn't a bug lurking somewhere in the murk.
68  */
69
70 static int wrap_saved_count=0;
71
72 static int wrap_saved_tempcount=0;
73
74 #define WRAPPER_GROW    8
75
76 void wrap_add_entry PROTO((WrapperEntry *e,int temp));
77 void wrap_kill PROTO((void));
78 void wrap_kill_temp PROTO((void));
79 void wrap_free_entry PROTO((WrapperEntry *e));
80 void wrap_free_entry_internal PROTO((WrapperEntry *e));
81 void wrap_restore_saved PROTO((void));
82
83 void wrap_setup()
84 {
85     /* FIXME-reentrancy: if we do a multithreaded server, will need to
86        move this to a per-connection data structure, or better yet
87        think about a cleaner solution.  */
88     static int wrap_setup_already_done = 0;
89     char *homedir;
90
91     if (wrap_setup_already_done != 0)
92         return;
93     else
94         wrap_setup_already_done = 1;
95
96     if (!current_parsed_root->isremote)
97     {
98         char *file;
99
100         file = xmalloc (strlen (current_parsed_root->directory)
101                         + sizeof (CVSROOTADM)
102                         + sizeof (CVSROOTADM_WRAPPER)
103                         + 3);
104         /* Then add entries found in repository, if it exists.  */
105         (void) sprintf (file, "%s/%s/%s", current_parsed_root->directory, CVSROOTADM,
106                         CVSROOTADM_WRAPPER);
107         if (isfile (file))
108         {
109             wrap_add_file(file,0);
110         }
111         free (file);
112     }
113
114     /* Then add entries found in home dir, (if user has one) and file
115        exists.  */
116     homedir = get_homedir ();
117     /* If we can't find a home directory, ignore ~/.cvswrappers.  This may
118        make tracking down problems a bit of a pain, but on the other
119        hand it might be obnoxious to complain when CVS will function
120        just fine without .cvswrappers (and many users won't even know what
121        .cvswrappers is).  */
122     if (homedir != NULL)
123     {
124         char *file = strcat_filename_onto_homedir (homedir, CVSDOTWRAPPER);
125         if (isfile (file))
126         {
127             wrap_add_file (file, 0);
128         }
129         free (file);
130     }
131
132     /* FIXME: calling wrap_add() below implies that the CVSWRAPPERS
133      * environment variable contains exactly one "wrapper" -- a line
134      * of the form
135      * 
136      *    FILENAME_PATTERN      FLAG  OPTS [ FLAG OPTS ...]
137      *
138      * This may disagree with the documentation, which states:
139      * 
140      *   `$CVSWRAPPERS'
141      *      A whitespace-separated list of file name patterns that CVS
142      *      should treat as wrappers. *Note Wrappers::.
143      *
144      * Does this mean the environment variable can hold multiple
145      * wrappers lines?  If so, a single call to wrap_add() is
146      * insufficient.
147      */
148
149     /* Then add entries found in CVSWRAPPERS environment variable. */
150     wrap_add (getenv (WRAPPER_ENV), 0);
151 }
152
153 #ifdef CLIENT_SUPPORT
154 /* Send -W arguments for the wrappers to the server.  The command must
155    be one that accepts them (e.g. update, import).  */
156 void
157 wrap_send ()
158 {
159     int i;
160
161     for (i = 0; i < wrap_count + wrap_tempcount; ++i)
162     {
163         if (wrap_list[i]->tocvsFilter != NULL
164             || wrap_list[i]->fromcvsFilter != NULL)
165             /* For greater studliness we would print the offending option
166                and (more importantly) where we found it.  */
167             error (0, 0, "\
168 -t and -f wrapper options are not supported remotely; ignored");
169         if (wrap_list[i]->mergeMethod == WRAP_COPY)
170             /* For greater studliness we would print the offending option
171                and (more importantly) where we found it.  */
172             error (0, 0, "\
173 -m wrapper option is not supported remotely; ignored");
174         send_to_server ("Argument -W\012Argument ", 0);
175         send_to_server (wrap_list[i]->wildCard, 0);
176         send_to_server (" -k '", 0);
177         if (wrap_list[i]->rcsOption != NULL)
178             send_to_server (wrap_list[i]->rcsOption, 0);
179         else
180             send_to_server ("kv", 0);
181         send_to_server ("'\012", 0);
182     }
183 }
184 #endif /* CLIENT_SUPPORT */
185
186 #if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT)
187 /* Output wrapper entries in the format of cvswrappers lines.
188  *
189  * This is useful when one side of a client/server connection wants to
190  * send its wrappers to the other; since the receiving side would like
191  * to use wrap_add() to incorporate the wrapper, it's best if the
192  * entry arrives in this format.
193  *
194  * The entries are stored in `line', which is allocated here.  Caller
195  * can free() it.
196  *
197  * If first_call_p is nonzero, then start afresh.  */
198 void
199 wrap_unparse_rcs_options (line, first_call_p)
200     char **line;
201     int first_call_p;
202 {
203     /* FIXME-reentrancy: we should design a reentrant interface, like
204        a callback which gets handed each wrapper (a multithreaded
205        server being the most concrete reason for this, but the
206        non-reentrant interface is fairly unnecessary/ugly).  */
207     static int i;
208
209     if (first_call_p)
210         i = 0;
211
212     if (i >= wrap_count + wrap_tempcount) {
213         *line = NULL;
214         return;
215     }
216
217     *line = xmalloc (strlen (wrap_list[i]->wildCard)
218                      + strlen ("\t")
219                      + strlen (" -k '")
220                      + (wrap_list[i]->rcsOption != NULL ? 
221                            strlen (wrap_list[i]->rcsOption) : 2)
222                      + strlen ("'")
223                      + 1);  /* leave room for '\0' */
224
225     strcpy (*line, wrap_list[i]->wildCard);
226     strcat (*line, " -k '");
227     if (wrap_list[i]->rcsOption != NULL)
228         strcat (*line, wrap_list[i]->rcsOption);
229     else
230         strcat (*line, "kv");
231     strcat (*line, "'");
232
233     ++i;
234 }
235 #endif /* SERVER_SUPPORT || CLIENT_SUPPORT */
236
237 /*
238  * Remove fmt str specifier other than %% or %s. And allow
239  * only max_s %s specifiers
240  */
241 void
242 wrap_clean_fmt_str(char *fmt, int max_s)
243 {
244     while (*fmt) {
245         if (fmt[0] == '%' && fmt[1])
246         {
247             if (fmt[1] == '%') 
248                 fmt++;
249             else
250                 if (fmt[1] == 's' && max_s > 0)
251                 {
252                     max_s--;
253                     fmt++;
254                 } else 
255                     *fmt = ' ';
256         }
257         fmt++;
258     }
259 }
260
261 /*
262  * Open a file and read lines, feeding each line to a line parser. Arrange
263  * for keeping a temporary list of wrappers at the end, if the "temp"
264  * argument is set.
265  */
266 void
267 wrap_add_file (file, temp)
268     const char *file;
269     int temp;
270 {
271     FILE *fp;
272     char *line = NULL;
273     size_t line_allocated = 0;
274
275     wrap_restore_saved ();
276     wrap_kill_temp ();
277
278     /* Load the file.  */
279     fp = CVS_FOPEN (file, "r");
280     if (fp == NULL)
281     {
282         if (!existence_error (errno))
283             error (0, errno, "cannot open %s", file);
284         return;
285     }
286     while (getline (&line, &line_allocated, fp) >= 0)
287         wrap_add (line, temp);
288     if (line)
289         free (line);
290     if (ferror (fp))
291         error (0, errno, "cannot read %s", file);
292     if (fclose (fp) == EOF)
293         error (0, errno, "cannot close %s", file);
294 }
295
296 void
297 wrap_kill()
298 {
299     wrap_kill_temp();
300     while(wrap_count)
301         wrap_free_entry(wrap_list[--wrap_count]);
302 }
303
304 void
305 wrap_kill_temp()
306 {
307     WrapperEntry **temps=wrap_list+wrap_count;
308
309     while(wrap_tempcount)
310         wrap_free_entry(temps[--wrap_tempcount]);
311 }
312
313 void
314 wrap_free_entry(e)
315      WrapperEntry *e;
316 {
317     wrap_free_entry_internal(e);
318     free(e);
319 }
320
321 void
322 wrap_free_entry_internal(e)
323     WrapperEntry *e;
324 {
325     free (e->wildCard);
326     if (e->tocvsFilter)
327         free (e->tocvsFilter);
328     if (e->fromcvsFilter)
329         free (e->fromcvsFilter);
330     if (e->rcsOption)
331         free (e->rcsOption);
332 }
333
334 void
335 wrap_restore_saved()
336 {
337     if(!wrap_saved_list)
338         return;
339
340     wrap_kill();
341
342     free(wrap_list);
343
344     wrap_list=wrap_saved_list;
345     wrap_count=wrap_saved_count;
346     wrap_tempcount=wrap_saved_tempcount;
347
348     wrap_saved_list=NULL;
349     wrap_saved_count=0;
350     wrap_saved_tempcount=0;
351 }
352
353 void
354 wrap_add (line, isTemp)
355    char *line;
356    int         isTemp;
357 {
358     char *temp;
359     char ctemp;
360     WrapperEntry e;
361     char opt;
362
363     if (!line || line[0] == '#')
364         return;
365
366     memset (&e, 0, sizeof(e));
367
368         /* Search for the wild card */
369     while (*line && isspace ((unsigned char) *line))
370         ++line;
371     for (temp = line;
372          *line && !isspace ((unsigned char) *line);
373          ++line)
374         ;
375     if(temp==line)
376         return;
377
378     ctemp=*line;
379     *line='\0';
380
381     e.wildCard=xstrdup(temp);
382     *line=ctemp;
383
384     while(*line){
385             /* Search for the option */
386         while(*line && *line!='-')
387             ++line;
388         if(!*line)
389             break;
390         ++line;
391         if(!*line)
392             break;
393         opt=*line;
394
395             /* Search for the filter commandline */
396         for(++line;*line && *line!='\'';++line);
397         if(!*line)
398             break;
399
400         for(temp=++line;*line && (*line!='\'' || line[-1]=='\\');++line)
401             ;
402
403         /* This used to "break;" (ignore the option) if there was a
404            single character between the single quotes (I'm guessing
405            that was accidental).  Now it "break;"s if there are no
406            characters.  I'm not sure either behavior is particularly
407            necessary--the current options might not require ''
408            arguments, but surely some future option legitimately
409            might.  Also I'm not sure that ignoring the option is a
410            swift way to handle syntax errors in general.  */
411         if (line==temp)
412             break;
413
414         ctemp=*line;
415         *line='\0';
416         switch(opt){
417         case 'f':
418             /* Before this is reenabled, need to address the problem in
419                commit.c (see
420                http://ximbiot.com/cvs/cvshome/docs/infowrapper.html).  */
421             error (1, 0,
422                    "-t/-f wrappers not supported by this version of CVS");
423
424             if(e.fromcvsFilter)
425                 free(e.fromcvsFilter);
426             /* FIXME: error message should say where the bad value
427                came from.  */
428             e.fromcvsFilter=expand_path (temp, "<wrapper>", 0);
429             if (!e.fromcvsFilter)
430                 error (1, 0, "Correct above errors first");
431             break;
432         case 't':
433             /* Before this is reenabled, need to address the problem in
434                commit.c (see
435                http://ximbiot.com/cvs/cvshome/docs/infowrapper.html).  */
436             error (1, 0,
437                    "-t/-f wrappers not supported by this version of CVS");
438
439             if(e.tocvsFilter)
440                 free(e.tocvsFilter);
441             /* FIXME: error message should say where the bad value
442                came from.  */
443             e.tocvsFilter=expand_path (temp, "<wrapper>", 0);
444             if (!e.tocvsFilter)
445                 error (1, 0, "Correct above errors first");
446             break;
447         case 'm':
448             if(*temp=='C' || *temp=='c')
449                 e.mergeMethod=WRAP_COPY;
450             else
451                 e.mergeMethod=WRAP_MERGE;
452             break;
453         case 'k':
454             if (e.rcsOption)
455                 free (e.rcsOption);
456             e.rcsOption = strcmp (temp, "kv") ? xstrdup (temp) : NULL;
457             break;
458         default:
459             break;
460         }
461         *line=ctemp;
462         if(!*line)break;
463         ++line;
464     }
465
466     wrap_add_entry(&e, isTemp);
467 }
468
469 void
470 wrap_add_entry(e, temp)
471     WrapperEntry *e;
472     int temp;
473 {
474     int x;
475     if(wrap_count+wrap_tempcount>=wrap_size){
476         wrap_size += WRAPPER_GROW;
477         wrap_list = (WrapperEntry **) xrealloc ((char *) wrap_list,
478                                                 wrap_size *
479                                                 sizeof (WrapperEntry *));
480     }
481
482     if(!temp && wrap_tempcount){
483         for(x=wrap_count+wrap_tempcount-1;x>=wrap_count;--x)
484             wrap_list[x+1]=wrap_list[x];
485     }
486
487     x=(temp ? wrap_count+(wrap_tempcount++):(wrap_count++));
488     wrap_list[x]=(WrapperEntry *)xmalloc(sizeof(WrapperEntry));
489     *wrap_list[x]=*e;
490 }
491
492 /* Return 1 if the given filename is a wrapper filename */
493 int
494 wrap_name_has (name,has)
495     const char   *name;
496     WrapMergeHas  has;
497 {
498     int x,count=wrap_count+wrap_tempcount;
499     char *temp;
500
501     for(x=0;x<count;++x)
502         if (CVS_FNMATCH (wrap_list[x]->wildCard, name, 0) == 0){
503             switch(has){
504             case WRAP_TOCVS:
505                 temp=wrap_list[x]->tocvsFilter;
506                 break;
507             case WRAP_FROMCVS:
508                 temp=wrap_list[x]->fromcvsFilter;
509                 break;
510             case WRAP_RCSOPTION:
511                 temp = wrap_list[x]->rcsOption;
512                 break;
513             default:
514                 abort ();
515             }
516             if(temp==NULL)
517                 return (0);
518             else
519                 return (1);
520         }
521     return (0);
522 }
523
524 static WrapperEntry *wrap_matching_entry PROTO ((const char *));
525
526 static WrapperEntry *
527 wrap_matching_entry (name)
528     const char *name;
529 {
530     int x,count=wrap_count+wrap_tempcount;
531
532     for(x=0;x<count;++x)
533         if (CVS_FNMATCH (wrap_list[x]->wildCard, name, 0) == 0)
534             return wrap_list[x];
535     return (WrapperEntry *)NULL;
536 }
537
538 /* Return the RCS options for FILENAME in a newly malloc'd string.  If
539    ASFLAG, then include "-k" at the beginning (e.g. "-kb"), otherwise
540    just give the option itself (e.g. "b").  */
541 char *
542 wrap_rcsoption (filename, asflag)
543     const char *filename;
544     int asflag;
545 {
546     WrapperEntry *e = wrap_matching_entry (filename);
547     char *buf;
548
549     if (e == NULL || e->rcsOption == NULL || (*e->rcsOption == '\0'))
550         return NULL;
551
552     buf = xmalloc (strlen (e->rcsOption) + 3);
553     if (asflag)
554     {
555         strcpy (buf, "-k");
556         strcat (buf, e->rcsOption);
557     }
558     else
559     {
560         strcpy (buf, e->rcsOption);
561     }
562     return buf;
563 }
564
565 char *
566 wrap_tocvs_process_file(fileName)
567     const char *fileName;
568 {
569     WrapperEntry *e=wrap_matching_entry(fileName);
570     static char *buf = NULL;
571     char *args;
572
573     if(e==NULL || e->tocvsFilter==NULL)
574         return NULL;
575
576     if (buf != NULL)
577         free (buf);
578     buf = cvs_temp_name ();
579
580     args = xmalloc (strlen (e->tocvsFilter)
581                     + strlen (fileName)
582                     + strlen (buf));
583
584     wrap_clean_fmt_str(e->tocvsFilter, 2);
585     sprintf (args, e->tocvsFilter, fileName, buf);
586     run_setup (args);
587     run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY );
588     free (args);
589
590     return buf;
591 }
592
593 int
594 wrap_merge_is_copy (fileName)
595     const char *fileName;
596 {
597     WrapperEntry *e=wrap_matching_entry(fileName);
598     if(e==NULL || e->mergeMethod==WRAP_MERGE)
599         return 0;
600
601     return 1;
602 }
603
604 void
605 wrap_fromcvs_process_file(fileName)
606     const char *fileName;
607 {
608     char *args;
609     WrapperEntry *e=wrap_matching_entry(fileName);
610
611     if(e==NULL || e->fromcvsFilter==NULL)
612         return;
613
614     args = xmalloc (strlen (e->fromcvsFilter)
615                     + strlen (fileName));
616
617     wrap_clean_fmt_str(e->fromcvsFilter, 1);
618     sprintf (args, e->fromcvsFilter, fileName);
619     run_setup (args);
620     run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL );
621     free (args);
622     return;
623 }