Home | History | Annotate | Line # | Download | only in src
      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 #include <sys/cdefs.h>
     11 __RCSID("$NetBSD: wrapper.c,v 1.2 2016/05/17 14:00:09 christos Exp $");
     12 
     13 #include "cvs.h"
     14 #include "getline.h"
     15 
     16 /*
     17   Original Author:  athan (at) morgan.com <Andrew C. Athan> 2/1/94
     18   Modified By:      vdemarco (at) bou.shl.com
     19 
     20   This package was written to support the NEXTSTEP concept of
     21   "wrappers."  These are essentially directories that are to be
     22   treated as "files."  This package allows such wrappers to be
     23   "processed" on the way in and out of CVS.  The intended use is to
     24   wrap up a wrapper into a single tar, such that that tar can be
     25   treated as a single binary file in CVS.  To solve the problem
     26   effectively, it was also necessary to be able to prevent rcsmerge
     27   application at appropriate times.
     28 
     29   ------------------
     30   Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers)
     31 
     32   wildcard	[option value][option value]...
     33 
     34   where option is one of
     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 (WrapperEntry *e,int temp);
     79 void wrap_kill (void);
     80 void wrap_kill_temp (void);
     81 void wrap_free_entry (WrapperEntry *e);
     82 void wrap_free_entry_internal (WrapperEntry *e);
     83 void wrap_restore_saved (void);
     84 
     85 void wrap_setup(void)
     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     if (!current_parsed_root->isremote)
     99     {
    100 	char *file;
    101 
    102 	/* Then add entries found in repository, if it exists.  */
    103 	file = Xasprintf ("%s/%s/%s", current_parsed_root->directory,
    104 			  CVSROOTADM, CVSROOTADM_WRAPPER);
    105 	if (isfile (file))
    106 	{
    107 	    wrap_add_file(file,0);
    108 	}
    109 	free (file);
    110     }
    111 
    112     /* Then add entries found in home dir, (if user has one) and file
    113        exists.  */
    114     homedir = get_homedir ();
    115     /* If we can't find a home directory, ignore ~/.cvswrappers.  This may
    116        make tracking down problems a bit of a pain, but on the other
    117        hand it might be obnoxious to complain when CVS will function
    118        just fine without .cvswrappers (and many users won't even know what
    119        .cvswrappers is).  */
    120     if (homedir != NULL)
    121     {
    122 	char *file = strcat_filename_onto_homedir (homedir, CVSDOTWRAPPER);
    123 	if (isfile (file))
    124 	{
    125 	    wrap_add_file (file, 0);
    126 	}
    127 	free (file);
    128     }
    129 
    130     /* FIXME: calling wrap_add() below implies that the CVSWRAPPERS
    131      * environment variable contains exactly one "wrapper" -- a line
    132      * of the form
    133      *
    134      *    FILENAME_PATTERN	FLAG  OPTS [ FLAG OPTS ...]
    135      *
    136      * This may disagree with the documentation, which states:
    137      *
    138      *   `$CVSWRAPPERS'
    139      *      A whitespace-separated list of file name patterns that CVS
    140      *      should treat as wrappers. *Note Wrappers::.
    141      *
    142      * Does this mean the environment variable can hold multiple
    143      * wrappers lines?  If so, a single call to wrap_add() is
    144      * insufficient.
    145      */
    146 
    147     /* Then add entries found in CVSWRAPPERS environment variable. */
    148     wrap_add (getenv (WRAPPER_ENV), 0);
    149 }
    150 
    151 #ifdef CLIENT_SUPPORT
    152 /* Send -W arguments for the wrappers to the server.  The command must
    153    be one that accepts them (e.g. update, import).  */
    154 void
    155 wrap_send (void)
    156 {
    157     int i;
    158 
    159     for (i = 0; i < wrap_count + wrap_tempcount; ++i)
    160     {
    161 	if (wrap_list[i]->tocvsFilter != NULL
    162 	    || wrap_list[i]->fromcvsFilter != NULL)
    163 	    /* For greater studliness we would print the offending option
    164 	       and (more importantly) where we found it.  */
    165 	    error (0, 0, "\
    166 -t and -f wrapper options are not supported remotely; ignored");
    167 	if (wrap_list[i]->mergeMethod == WRAP_COPY)
    168 	    /* For greater studliness we would print the offending option
    169 	       and (more importantly) where we found it.  */
    170 	    error (0, 0, "\
    171 -m wrapper option is not supported remotely; ignored");
    172 	send_to_server ("Argument -W\012Argument ", 0);
    173 	send_to_server (wrap_list[i]->wildCard, 0);
    174 	send_to_server (" -k '", 0);
    175 	if (wrap_list[i]->rcsOption != NULL)
    176 	    send_to_server (wrap_list[i]->rcsOption, 0);
    177 	else
    178 	    send_to_server ("kv", 0);
    179 	send_to_server ("'\012", 0);
    180     }
    181 }
    182 #endif /* CLIENT_SUPPORT */
    183 
    184 #if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT)
    185 /* Output wrapper entries in the format of cvswrappers lines.
    186  *
    187  * This is useful when one side of a client/server connection wants to
    188  * send its wrappers to the other; since the receiving side would like
    189  * to use wrap_add() to incorporate the wrapper, it's best if the
    190  * entry arrives in this format.
    191  *
    192  * The entries are stored in `line', which is allocated here.  Caller
    193  * can free() it.
    194  *
    195  * If first_call_p is nonzero, then start afresh.  */
    196 void
    197 wrap_unparse_rcs_options (char **line, int first_call_p)
    198 {
    199     /* FIXME-reentrancy: we should design a reentrant interface, like
    200        a callback which gets handed each wrapper (a multithreaded
    201        server being the most concrete reason for this, but the
    202        non-reentrant interface is fairly unnecessary/ugly).  */
    203     static int i;
    204 
    205     if (first_call_p)
    206         i = 0;
    207 
    208     if (i >= wrap_count + wrap_tempcount) {
    209         *line = NULL;
    210         return;
    211     }
    212 
    213     *line = Xasprintf ("%s -k '%s'",
    214 		       wrap_list[i]->wildCard,
    215 		       wrap_list[i]->rcsOption
    216 		       ? wrap_list[i]->rcsOption : "kv");
    217     ++i;
    218 }
    219 #endif /* SERVER_SUPPORT || CLIENT_SUPPORT */
    220 
    221 /*
    222  * Remove fmt str specifier other than %% or %s. And allow
    223  * only max_s %s specifiers
    224  */
    225 static void
    226 wrap_clean_fmt_str(char *fmt, int max_s)
    227 {
    228     while (*fmt) {
    229 	if (fmt[0] == '%' && fmt[1])
    230 	{
    231 	    if (fmt[1] == '%')
    232 		fmt++;
    233 	    else
    234 		if (fmt[1] == 's' && max_s > 0)
    235 		{
    236 		    max_s--;
    237 		    fmt++;
    238 		} else
    239 		    *fmt = ' ';
    240 	}
    241 	fmt++;
    242     }
    243 }
    244 
    245 /*
    246  * Open a file and read lines, feeding each line to a line parser. Arrange
    247  * for keeping a temporary list of wrappers at the end, if the "temp"
    248  * argument is set.
    249  */
    250 void
    251 wrap_add_file (const char *file, 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(void)
    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(void)
    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(WrapperEntry *e)
    297 {
    298     wrap_free_entry_internal(e);
    299     free(e);
    300 }
    301 
    302 void
    303 wrap_free_entry_internal(WrapperEntry *e)
    304 {
    305     free (e->wildCard);
    306     if (e->tocvsFilter)
    307 	free (e->tocvsFilter);
    308     if (e->fromcvsFilter)
    309 	free (e->fromcvsFilter);
    310     if (e->rcsOption)
    311 	free (e->rcsOption);
    312 }
    313 
    314 void
    315 wrap_restore_saved(void)
    316 {
    317     if(!wrap_saved_list)
    318 	return;
    319 
    320     wrap_kill();
    321 
    322     free(wrap_list);
    323 
    324     wrap_list=wrap_saved_list;
    325     wrap_count=wrap_saved_count;
    326     wrap_tempcount=wrap_saved_tempcount;
    327 
    328     wrap_saved_list=NULL;
    329     wrap_saved_count=0;
    330     wrap_saved_tempcount=0;
    331 }
    332 
    333 void
    334 wrap_add (char *line, int isTemp)
    335 {
    336     char *temp;
    337     char ctemp;
    338     WrapperEntry e;
    339     char opt;
    340 
    341     if (!line || line[0] == '#')
    342 	return;
    343 
    344     memset (&e, 0, sizeof(e));
    345 
    346 	/* Search for the wild card */
    347     while (*line && isspace ((unsigned char) *line))
    348 	++line;
    349     for (temp = line;
    350 	 *line && !isspace ((unsigned char) *line);
    351 	 ++line)
    352 	;
    353     if(temp==line)
    354 	return;
    355 
    356     ctemp=*line;
    357     *line='\0';
    358 
    359     e.wildCard=xstrdup(temp);
    360     *line=ctemp;
    361 
    362     while(*line){
    363 	    /* Search for the option */
    364 	while(*line && *line!='-')
    365 	    ++line;
    366 	if(!*line)
    367 	    break;
    368 	++line;
    369 	if(!*line)
    370 	    break;
    371 	opt=*line;
    372 
    373 	    /* Search for the filter commandline */
    374 	for(++line;*line && *line!='\'';++line);
    375 	if(!*line)
    376 	    break;
    377 
    378 	for(temp=++line;*line && (*line!='\'' || line[-1]=='\\');++line)
    379 	    ;
    380 
    381 	/* This used to "break;" (ignore the option) if there was a
    382 	   single character between the single quotes (I'm guessing
    383 	   that was accidental).  Now it "break;"s if there are no
    384 	   characters.  I'm not sure either behavior is particularly
    385 	   necessary--the current options might not require ''
    386 	   arguments, but surely some future option legitimately
    387 	   might.  Also I'm not sure that ignoring the option is a
    388 	   swift way to handle syntax errors in general.  */
    389 	if (line==temp)
    390 	    break;
    391 
    392 	ctemp=*line;
    393 	*line='\0';
    394 	switch(opt){
    395 	case 'f':
    396 	    /* Before this is reenabled, need to address the problem in
    397 	       commit.c (see
    398 	       <http://ximbiot.com/cvs/cvshome/docs/infowrapper.html>).  */
    399 	    error (1, 0,
    400 		   "-t/-f wrappers not supported by this version of CVS");
    401 
    402 	    if(e.fromcvsFilter)
    403 		free(e.fromcvsFilter);
    404 	    /* FIXME: error message should say where the bad value
    405 	       came from.  */
    406 	    e.fromcvsFilter =
    407 	      expand_path (temp, current_parsed_root->directory, false,
    408 			   "<wrapper>", 0);
    409             if (!e.fromcvsFilter)
    410 		error (1, 0, "Correct above errors first");
    411 	    break;
    412 	case 't':
    413 	    /* Before this is reenabled, need to address the problem in
    414 	       commit.c (see
    415 	       <http://ximbiot.com/cvs/cvshome/docs/infowrapper.html>).  */
    416 	    error (1, 0,
    417 		   "-t/-f wrappers not supported by this version of CVS");
    418 
    419 	    if(e.tocvsFilter)
    420 		free(e.tocvsFilter);
    421 	    /* FIXME: error message should say where the bad value
    422 	       came from.  */
    423 	    e.tocvsFilter = expand_path (temp, current_parsed_root->directory,
    424 					 false, "<wrapper>", 0);
    425             if (!e.tocvsFilter)
    426 		error (1, 0, "Correct above errors first");
    427 	    break;
    428 	case 'm':
    429 	    if(*temp=='C' || *temp=='c')
    430 		e.mergeMethod=WRAP_COPY;
    431 	    else
    432 		e.mergeMethod=WRAP_MERGE;
    433 	    break;
    434 	case 'k':
    435 	    if (e.rcsOption)
    436 		free (e.rcsOption);
    437 	    e.rcsOption = strcmp (temp, "kv") ? xstrdup (temp) : NULL;
    438 	    break;
    439 	default:
    440 	    break;
    441 	}
    442 	*line=ctemp;
    443 	if(!*line)break;
    444 	++line;
    445     }
    446 
    447     wrap_add_entry(&e, isTemp);
    448 }
    449 
    450 void
    451 wrap_add_entry (WrapperEntry *e, int temp)
    452 {
    453     int x;
    454     if (wrap_count + wrap_tempcount >= wrap_size)
    455     {
    456 	wrap_size += WRAPPER_GROW;
    457 	wrap_list = xnrealloc (wrap_list, wrap_size, sizeof (WrapperEntry *));
    458     }
    459 
    460     if (!temp && wrap_tempcount)
    461     {
    462 	for (x = wrap_count + wrap_tempcount - 1; x >= wrap_count; --x)
    463 	    wrap_list[x + 1] = wrap_list[x];
    464     }
    465 
    466     x = (temp ? wrap_count + (wrap_tempcount++) : (wrap_count++));
    467     wrap_list[x] = xmalloc (sizeof (WrapperEntry));
    468     *wrap_list[x] = *e;
    469 }
    470 
    471 /* Return 1 if the given filename is a wrapper filename */
    472 int
    473 wrap_name_has (const char *name, WrapMergeHas has)
    474 {
    475     int x,count=wrap_count+wrap_tempcount;
    476     char *temp;
    477 
    478     for(x=0;x<count;++x)
    479 	if (CVS_FNMATCH (wrap_list[x]->wildCard, name, 0) == 0){
    480 	    switch(has){
    481 	    case WRAP_TOCVS:
    482 		temp=wrap_list[x]->tocvsFilter;
    483 		break;
    484 	    case WRAP_FROMCVS:
    485 		temp=wrap_list[x]->fromcvsFilter;
    486 		break;
    487 	    case WRAP_RCSOPTION:
    488 		temp = wrap_list[x]->rcsOption;
    489 		break;
    490 	    default:
    491 	        abort ();
    492 	    }
    493 	    if(temp==NULL)
    494 		return (0);
    495 	    else
    496 		return (1);
    497 	}
    498     return (0);
    499 }
    500 
    501 static WrapperEntry *wrap_matching_entry (const char *);
    502 
    503 static WrapperEntry *
    504 wrap_matching_entry (const char *name)
    505 {
    506     int x,count=wrap_count+wrap_tempcount;
    507 
    508     for(x=0;x<count;++x)
    509 	if (CVS_FNMATCH (wrap_list[x]->wildCard, name, 0) == 0)
    510 	    return wrap_list[x];
    511     return NULL;
    512 }
    513 
    514 /* Return the RCS options for FILENAME in a newly malloc'd string.  If
    515    ASFLAG, then include "-k" at the beginning (e.g. "-kb"), otherwise
    516    just give the option itself (e.g. "b").  */
    517 char *
    518 wrap_rcsoption (const char *filename, int asflag)
    519 {
    520     WrapperEntry *e = wrap_matching_entry (filename);
    521 
    522     if (e == NULL || e->rcsOption == NULL || (*e->rcsOption == '\0'))
    523 	return NULL;
    524 
    525     return Xasprintf ("%s%s", asflag ? "-k" : "", e->rcsOption);
    526 }
    527 
    528 char *
    529 wrap_tocvs_process_file(const char *fileName)
    530 {
    531     WrapperEntry *e=wrap_matching_entry(fileName);
    532     static char *buf = NULL;
    533     char *args;
    534 
    535     if(e==NULL || e->tocvsFilter==NULL)
    536 	return NULL;
    537 
    538     if (buf != NULL)
    539 	free (buf);
    540     buf = cvs_temp_name ();
    541 
    542     wrap_clean_fmt_str (e->tocvsFilter, 2);
    543     args = Xasprintf (e->tocvsFilter, fileName, buf);
    544     run_setup (args);
    545     run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL | RUN_REALLY );
    546     free (args);
    547 
    548     return buf;
    549 }
    550 
    551 int
    552 wrap_merge_is_copy (const char *fileName)
    553 {
    554     WrapperEntry *e=wrap_matching_entry(fileName);
    555     if(e==NULL || e->mergeMethod==WRAP_MERGE)
    556 	return 0;
    557 
    558     return 1;
    559 }
    560 
    561 void
    562 wrap_fromcvs_process_file(const char *fileName)
    563 {
    564     char *args;
    565     WrapperEntry *e = wrap_matching_entry(fileName);
    566 
    567     if (e != NULL && e->fromcvsFilter != NULL)
    568     {
    569 	wrap_clean_fmt_str (e->fromcvsFilter, 1);
    570 	args = Xasprintf (e->fromcvsFilter, fileName);
    571 	run_setup (args);
    572 	run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
    573 	free (args);
    574     }
    575     return;
    576 }
    577