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)
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. */
15 Original Author: athan@morgan.com <Andrew C. Athan> 2/1/94
16 Modified By: vdemarco@bou.shl.com
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.
28 Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers)
30 wildcard [option value][option value]...
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
36 and value is a single-quote delimited value.
39 *.nib -f 'gunzipuntar' -t 'targzip' -m 'COPY'
48 WrapMergeMethod mergeMethod;
51 static WrapperEntry **wrap_list=NULL;
52 static WrapperEntry **wrap_saved_list=NULL;
54 static int wrap_size=0;
55 static int wrap_count=0;
56 static int wrap_tempcount=0;
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.
66 * Right now things seem to be working, but that's no guarantee there
67 * isn't a bug lurking somewhere in the murk.
70 static int wrap_saved_count=0;
72 static int wrap_saved_tempcount=0;
74 #define WRAPPER_GROW 8
76 void wrap_add_entry (WrapperEntry *e,int temp);
77 void wrap_kill (void);
78 void wrap_kill_temp (void);
79 void wrap_free_entry (WrapperEntry *e);
80 void wrap_free_entry_internal (WrapperEntry *e);
81 void wrap_restore_saved (void);
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;
91 if (wrap_setup_already_done != 0)
94 wrap_setup_already_done = 1;
97 if (!current_parsed_root->isremote)
102 file = xmalloc (strlen (current_parsed_root->directory)
103 + sizeof (CVSROOTADM)
104 + sizeof (CVSROOTADM_WRAPPER)
106 /* Then add entries found in repository, if it exists. */
107 (void) sprintf (file, "%s/%s/%s", current_parsed_root->directory, CVSROOTADM,
111 wrap_add_file(file,0);
116 /* Then add entries found in home dir, (if user has one) and file
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
126 char *file = strcat_filename_onto_homedir (homedir, CVSDOTWRAPPER);
129 wrap_add_file (file, 0);
134 /* FIXME: calling wrap_add() below implies that the CVSWRAPPERS
135 * environment variable contains exactly one "wrapper" -- a line
138 * FILENAME_PATTERN FLAG OPTS [ FLAG OPTS ...]
140 * This may disagree with the documentation, which states:
143 * A whitespace-separated list of file name patterns that CVS
144 * should treat as wrappers. *Note Wrappers::.
146 * Does this mean the environment variable can hold multiple
147 * wrappers lines? If so, a single call to wrap_add() is
151 /* Then add entries found in CVSWRAPPERS environment variable. */
152 wrap_add (getenv (WRAPPER_ENV), 0);
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). */
163 for (i = 0; i < wrap_count + wrap_tempcount; ++i)
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. */
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. */
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);
182 send_to_server ("kv", 0);
183 send_to_server ("'\012", 0);
186 #endif /* CLIENT_SUPPORT */
188 #if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT)
189 /* Output wrapper entries in the format of cvswrappers lines.
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.
196 * The entries are stored in `line', which is allocated here. Caller
199 * If first_call_p is nonzero, then start afresh. */
201 wrap_unparse_rcs_options (char **line, int first_call_p)
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). */
212 if (i >= wrap_count + wrap_tempcount) {
217 *line = xmalloc (strlen (wrap_list[i]->wildCard)
220 + (wrap_list[i]->rcsOption != NULL ?
221 strlen (wrap_list[i]->rcsOption) : 2)
223 + 1); /* leave room for '\0' */
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);
230 strcat (*line, "kv");
235 #endif /* SERVER_SUPPORT || CLIENT_SUPPORT */
238 * Remove fmt str specifier other than %% or %s. And allow
239 * only max_s %s specifiers
241 wrap_clean_fmt_str(char *fmt, int max_s)
244 if (fmt[0] == '%' && fmt[1])
249 if (fmt[1] == 's' && max_s > 0)
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"
267 wrap_add_file (const char *file, int temp)
271 size_t line_allocated = 0;
273 wrap_restore_saved ();
277 fp = CVS_FOPEN (file, "r");
280 if (!existence_error (errno))
281 error (0, errno, "cannot open %s", file);
284 while (getline (&line, &line_allocated, fp) >= 0)
285 wrap_add (line, temp);
289 error (0, errno, "cannot read %s", file);
290 if (fclose (fp) == EOF)
291 error (0, errno, "cannot close %s", file);
299 wrap_free_entry(wrap_list[--wrap_count]);
305 WrapperEntry **temps=wrap_list+wrap_count;
307 while(wrap_tempcount)
308 wrap_free_entry(temps[--wrap_tempcount]);
312 wrap_free_entry(WrapperEntry *e)
314 wrap_free_entry_internal(e);
319 wrap_free_entry_internal(WrapperEntry *e)
323 free (e->tocvsFilter);
324 if (e->fromcvsFilter)
325 free (e->fromcvsFilter);
331 wrap_restore_saved(void)
340 wrap_list=wrap_saved_list;
341 wrap_count=wrap_saved_count;
342 wrap_tempcount=wrap_saved_tempcount;
344 wrap_saved_list=NULL;
346 wrap_saved_tempcount=0;
350 wrap_add (char *line, int isTemp)
357 if (!line || line[0] == '#')
360 memset (&e, 0, sizeof(e));
362 /* Search for the wild card */
363 while (*line && isspace ((unsigned char) *line))
366 *line && !isspace ((unsigned char) *line);
375 e.wildCard=xstrdup(temp);
379 /* Search for the option */
380 while(*line && *line!='-')
389 /* Search for the filter commandline */
390 for(++line;*line && *line!='\'';++line);
394 for(temp=++line;*line && (*line!='\'' || line[-1]=='\\');++line)
397 /* This used to "break;" (ignore the option) if there was a
398 single character between the single quotes (I'm guessing
399 that was accidental). Now it "break;"s if there are no
400 characters. I'm not sure either behavior is particularly
401 necessary--the current options might not require ''
402 arguments, but surely some future option legitimately
403 might. Also I'm not sure that ignoring the option is a
404 swift way to handle syntax errors in general. */
412 /* Before this is reenabled, need to address the problem in
413 commit.c (see http://www.cvshome.org/docs/infowrapper.html). */
415 "-t/-f wrappers not supported by this version of CVS");
418 free(e.fromcvsFilter);
419 /* FIXME: error message should say where the bad value
421 e.fromcvsFilter=expand_path (temp, "<wrapper>", 0, 0);
422 if (!e.fromcvsFilter)
423 error (1, 0, "Correct above errors first");
426 /* Before this is reenabled, need to address the problem in
427 commit.c (see http://www.cvshome.org/docs/infowrapper.html). */
429 "-t/-f wrappers not supported by this version of CVS");
433 /* FIXME: error message should say where the bad value
435 e.tocvsFilter=expand_path (temp, "<wrapper>", 0, 0);
437 error (1, 0, "Correct above errors first");
440 if(*temp=='C' || *temp=='c')
441 e.mergeMethod=WRAP_COPY;
443 e.mergeMethod=WRAP_MERGE;
448 e.rcsOption = strcmp (temp, "kv") ? xstrdup (temp) : NULL;
458 wrap_add_entry(&e, isTemp);
462 wrap_add_entry(WrapperEntry *e, int temp)
465 if(wrap_count+wrap_tempcount>=wrap_size){
466 wrap_size += WRAPPER_GROW;
467 wrap_list = (WrapperEntry **) xrealloc ((char *) wrap_list,
469 sizeof (WrapperEntry *));
472 if(!temp && wrap_tempcount){
473 for(x=wrap_count+wrap_tempcount-1;x>=wrap_count;--x)
474 wrap_list[x+1]=wrap_list[x];
477 x=(temp ? wrap_count+(wrap_tempcount++):(wrap_count++));
478 wrap_list[x]=(WrapperEntry *)xmalloc(sizeof(WrapperEntry));
482 /* Return 1 if the given filename is a wrapper filename */
484 wrap_name_has (const char *name, WrapMergeHas has)
486 int x,count=wrap_count+wrap_tempcount;
490 if (CVS_FNMATCH (wrap_list[x]->wildCard, name, 0) == 0){
493 temp=wrap_list[x]->tocvsFilter;
496 temp=wrap_list[x]->fromcvsFilter;
499 temp = wrap_list[x]->rcsOption;
512 static WrapperEntry *wrap_matching_entry (const char *);
514 static WrapperEntry *
515 wrap_matching_entry (const char *name)
517 int x,count=wrap_count+wrap_tempcount;
520 if (CVS_FNMATCH (wrap_list[x]->wildCard, name, 0) == 0)
522 return (WrapperEntry *)NULL;
525 /* Return the RCS options for FILENAME in a newly malloc'd string. If
526 ASFLAG, then include "-k" at the beginning (e.g. "-kb"), otherwise
527 just give the option itself (e.g. "b"). */
529 wrap_rcsoption (const char *filename, int asflag)
531 WrapperEntry *e = wrap_matching_entry (filename);
534 if (e == NULL || e->rcsOption == NULL || (*e->rcsOption == '\0'))
537 buf = xmalloc (strlen (e->rcsOption) + 3);
541 strcat (buf, e->rcsOption);
545 strcpy (buf, e->rcsOption);
551 wrap_tocvs_process_file(const char *fileName)
553 WrapperEntry *e=wrap_matching_entry(fileName);
554 static char *buf = NULL;
557 if(e==NULL || e->tocvsFilter==NULL)
562 buf = cvs_temp_name ();
564 args = xmalloc (strlen (e->tocvsFilter)
568 wrap_clean_fmt_str(e->tocvsFilter, 2);
569 sprintf (args, e->tocvsFilter, fileName, buf);
571 run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY );
578 wrap_merge_is_copy (const char *fileName)
580 WrapperEntry *e=wrap_matching_entry(fileName);
581 if(e==NULL || e->mergeMethod==WRAP_MERGE)
588 wrap_fromcvs_process_file(const char *fileName)
591 WrapperEntry *e=wrap_matching_entry(fileName);
593 if(e==NULL || e->fromcvsFilter==NULL)
596 args = xmalloc (strlen (e->fromcvsFilter)
597 + strlen (fileName));
599 wrap_clean_fmt_str(e->fromcvsFilter, 1);
600 sprintf (args, e->fromcvsFilter, fileName);
602 run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL );