]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/cvs/src/wrapper.c
merge fix for boot-time hang on centos' xen
[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   -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 #ifdef CLIENT_SUPPORT
97     if (!current_parsed_root->isremote)
98 #endif
99     {
100         char *file;
101
102         file = xmalloc (strlen (current_parsed_root->directory)
103                         + sizeof (CVSROOTADM)
104                         + sizeof (CVSROOTADM_WRAPPER)
105                         + 3);
106         /* Then add entries found in repository, if it exists.  */
107         (void) sprintf (file, "%s/%s/%s", current_parsed_root->directory, CVSROOTADM,
108                         CVSROOTADM_WRAPPER);
109         if (isfile (file))
110         {
111             wrap_add_file(file,0);
112         }
113         free (file);
114     }
115
116     /* Then add entries found in home dir, (if user has one) and file
117        exists.  */
118     homedir = get_homedir ();
119     /* If we can't find a home directory, ignore ~/.cvswrappers.  This may
120        make tracking down problems a bit of a pain, but on the other
121        hand it might be obnoxious to complain when CVS will function
122        just fine without .cvswrappers (and many users won't even know what
123        .cvswrappers is).  */
124     if (homedir != NULL)
125     {
126         char *file = strcat_filename_onto_homedir (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         send_to_server ("Argument -W\012Argument ", 0);
177         send_to_server (wrap_list[i]->wildCard, 0);
178         send_to_server (" -k '", 0);
179         if (wrap_list[i]->rcsOption != NULL)
180             send_to_server (wrap_list[i]->rcsOption, 0);
181         else
182             send_to_server ("kv", 0);
183         send_to_server ("'\012", 0);
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     if (i >= wrap_count + wrap_tempcount) {
215         *line = NULL;
216         return;
217     }
218
219     *line = xmalloc (strlen (wrap_list[i]->wildCard)
220                      + strlen ("\t")
221                      + strlen (" -k '")
222                      + (wrap_list[i]->rcsOption != NULL ? 
223                            strlen (wrap_list[i]->rcsOption) : 2)
224                      + strlen ("'")
225                      + 1);  /* leave room for '\0' */
226
227     strcpy (*line, wrap_list[i]->wildCard);
228     strcat (*line, " -k '");
229     if (wrap_list[i]->rcsOption != NULL)
230         strcat (*line, wrap_list[i]->rcsOption);
231     else
232         strcat (*line, "kv");
233     strcat (*line, "'");
234
235     ++i;
236 }
237 #endif /* SERVER_SUPPORT || CLIENT_SUPPORT */
238
239 /*
240  * Remove fmt str specifier other than %% or %s. And allow
241  * only max_s %s specifiers
242  */
243 wrap_clean_fmt_str(char *fmt, int max_s)
244 {
245     while (*fmt) {
246         if (fmt[0] == '%' && fmt[1])
247         {
248             if (fmt[1] == '%') 
249                 fmt++;
250             else
251                 if (fmt[1] == 's' && max_s > 0)
252                 {
253                     max_s--;
254                     fmt++;
255                 } else 
256                     *fmt = ' ';
257         }
258         fmt++;
259     }
260     return;
261 }
262
263 /*
264  * Open a file and read lines, feeding each line to a line parser. Arrange
265  * for keeping a temporary list of wrappers at the end, if the "temp"
266  * argument is set.
267  */
268 void
269 wrap_add_file (file, temp)
270     const char *file;
271     int temp;
272 {
273     FILE *fp;
274     char *line = NULL;
275     size_t line_allocated = 0;
276
277     wrap_restore_saved ();
278     wrap_kill_temp ();
279
280     /* Load the file.  */
281     fp = CVS_FOPEN (file, "r");
282     if (fp == NULL)
283     {
284         if (!existence_error (errno))
285             error (0, errno, "cannot open %s", file);
286         return;
287     }
288     while (getline (&line, &line_allocated, fp) >= 0)
289         wrap_add (line, temp);
290     if (line)
291         free (line);
292     if (ferror (fp))
293         error (0, errno, "cannot read %s", file);
294     if (fclose (fp) == EOF)
295         error (0, errno, "cannot close %s", file);
296 }
297
298 void
299 wrap_kill()
300 {
301     wrap_kill_temp();
302     while(wrap_count)
303         wrap_free_entry(wrap_list[--wrap_count]);
304 }
305
306 void
307 wrap_kill_temp()
308 {
309     WrapperEntry **temps=wrap_list+wrap_count;
310
311     while(wrap_tempcount)
312         wrap_free_entry(temps[--wrap_tempcount]);
313 }
314
315 void
316 wrap_free_entry(e)
317      WrapperEntry *e;
318 {
319     wrap_free_entry_internal(e);
320     free(e);
321 }
322
323 void
324 wrap_free_entry_internal(e)
325     WrapperEntry *e;
326 {
327     free (e->wildCard);
328     if (e->tocvsFilter)
329         free (e->tocvsFilter);
330     if (e->fromcvsFilter)
331         free (e->fromcvsFilter);
332     if (e->rcsOption)
333         free (e->rcsOption);
334 }
335
336 void
337 wrap_restore_saved()
338 {
339     if(!wrap_saved_list)
340         return;
341
342     wrap_kill();
343
344     free(wrap_list);
345
346     wrap_list=wrap_saved_list;
347     wrap_count=wrap_saved_count;
348     wrap_tempcount=wrap_saved_tempcount;
349
350     wrap_saved_list=NULL;
351     wrap_saved_count=0;
352     wrap_saved_tempcount=0;
353 }
354
355 void
356 wrap_add (line, isTemp)
357    char *line;
358    int         isTemp;
359 {
360     char *temp;
361     char ctemp;
362     WrapperEntry e;
363     char opt;
364
365     if (!line || line[0] == '#')
366         return;
367
368     memset (&e, 0, sizeof(e));
369
370         /* Search for the wild card */
371     while (*line && isspace ((unsigned char) *line))
372         ++line;
373     for (temp = line;
374          *line && !isspace ((unsigned char) *line);
375          ++line)
376         ;
377     if(temp==line)
378         return;
379
380     ctemp=*line;
381     *line='\0';
382
383     e.wildCard=xstrdup(temp);
384     *line=ctemp;
385
386     while(*line){
387             /* Search for the option */
388         while(*line && *line!='-')
389             ++line;
390         if(!*line)
391             break;
392         ++line;
393         if(!*line)
394             break;
395         opt=*line;
396
397             /* Search for the filter commandline */
398         for(++line;*line && *line!='\'';++line);
399         if(!*line)
400             break;
401
402         for(temp=++line;*line && (*line!='\'' || line[-1]=='\\');++line)
403             ;
404
405         /* This used to "break;" (ignore the option) if there was a
406            single character between the single quotes (I'm guessing
407            that was accidental).  Now it "break;"s if there are no
408            characters.  I'm not sure either behavior is particularly
409            necessary--the current options might not require ''
410            arguments, but surely some future option legitimately
411            might.  Also I'm not sure that ignoring the option is a
412            swift way to handle syntax errors in general.  */
413         if (line==temp)
414             break;
415
416         ctemp=*line;
417         *line='\0';
418         switch(opt){
419         case 'f':
420             /* Before this is reenabled, need to address the problem in
421                commit.c (see http://www.cvshome.org/docs/infowrapper.html).  */
422             error (1, 0,
423                    "-t/-f wrappers not supported by this version of CVS");
424
425             if(e.fromcvsFilter)
426                 free(e.fromcvsFilter);
427             /* FIXME: error message should say where the bad value
428                came from.  */
429             e.fromcvsFilter=expand_path (temp, "<wrapper>", 0);
430             if (!e.fromcvsFilter)
431                 error (1, 0, "Correct above errors first");
432             break;
433         case 't':
434             /* Before this is reenabled, need to address the problem in
435                commit.c (see http://www.cvshome.org/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 }