Home | History | Annotate | Line # | Download | only in src
      1 /* Implementation for "cvs watch add", "cvs watchers", and related commands
      2 
      3    This program is free software; you can redistribute it and/or modify
      4    it under the terms of the GNU General Public License as published by
      5    the Free Software Foundation; either version 2, or (at your option)
      6    any later version.
      7 
      8    This program is distributed in the hope that it will be useful,
      9    but WITHOUT ANY WARRANTY; without even the implied warranty of
     10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11    GNU General Public License for more details.  */
     12 #include <sys/cdefs.h>
     13 __RCSID("$NetBSD: watch.c,v 1.3 2016/05/17 14:00:09 christos Exp $");
     14 
     15 #include "cvs.h"
     16 #include "edit.h"
     17 #include "fileattr.h"
     18 #include "watch.h"
     19 
     20 const char *const watch_usage[] =
     21 {
     22     "Usage: %s %s {on|off|add|remove} [-lR] [-a <action>]... [<path>]...\n",
     23     "on/off: Turn on/off read-only checkouts of files.\n",
     24     "add/remove: Add or remove notification on actions.\n",
     25     "-l (on/off/add/remove): Local directory only, not recursive.\n",
     26     "-R (on/off/add/remove): Process directories recursively (default).\n",
     27     "-a (add/remove): Specify what actions, one of: `edit', `unedit',\n",
     28     "                 `commit', `all', or `none' (defaults to `all').\n",
     29     "(Specify the --help global option for a list of other help options.)\n",
     30     NULL
     31 };
     32 
     33 static struct addremove_args the_args;
     34 
     35 void
     36 watch_modify_watchers (const char *file, struct addremove_args *what)
     37 {
     38     char *curattr = fileattr_get0 (file, "_watchers");
     39     char *p;
     40     char *pend;
     41     char *nextp;
     42     char *who;
     43     int who_len;
     44     char *mycurattr;
     45     char *mynewattr;
     46     size_t mynewattr_size;
     47 
     48     int add_edit_pending;
     49     int add_unedit_pending;
     50     int add_commit_pending;
     51     int remove_edit_pending;
     52     int remove_unedit_pending;
     53     int remove_commit_pending;
     54     int add_tedit_pending;
     55     int add_tunedit_pending;
     56     int add_tcommit_pending;
     57 
     58     TRACE( TRACE_FUNCTION, "modify_watchers ( %s )", file );
     59 
     60     who = getcaller ();
     61     who_len = strlen (who);
     62 
     63     /* Look for current watcher types for this user.  */
     64     mycurattr = NULL;
     65     if (curattr != NULL)
     66     {
     67 	p = curattr;
     68 	while (1) {
     69 	    if (strncmp (who, p, who_len) == 0
     70 		&& p[who_len] == '>')
     71 	    {
     72 		/* Found this user.  */
     73 		mycurattr = p + who_len + 1;
     74 	    }
     75 	    p = strchr (p, ',');
     76 	    if (p == NULL)
     77 		break;
     78 	    ++p;
     79 	}
     80     }
     81     if (mycurattr != NULL)
     82     {
     83 	mycurattr = xstrdup (mycurattr);
     84 	p = strchr (mycurattr, ',');
     85 	if (p != NULL)
     86 	    *p = '\0';
     87     }
     88 
     89     /* Now copy mycurattr to mynewattr, making the requisite modifications.
     90        Note that we add a dummy '+' to the start of mynewattr, to reduce
     91        special cases (but then we strip it off when we are done).  */
     92 
     93     mynewattr_size = sizeof "+edit+unedit+commit+tedit+tunedit+tcommit";
     94     if (mycurattr != NULL)
     95 	mynewattr_size += strlen (mycurattr);
     96     mynewattr = xmalloc (mynewattr_size);
     97     mynewattr[0] = '\0';
     98 
     99     add_edit_pending = what->adding && what->edit;
    100     add_unedit_pending = what->adding && what->unedit;
    101     add_commit_pending = what->adding && what->commit;
    102     remove_edit_pending = !what->adding && what->edit;
    103     remove_unedit_pending = !what->adding && what->unedit;
    104     remove_commit_pending = !what->adding && what->commit;
    105     add_tedit_pending = what->add_tedit;
    106     add_tunedit_pending = what->add_tunedit;
    107     add_tcommit_pending = what->add_tcommit;
    108 
    109     /* Copy over existing watch types, except those to be removed.  */
    110     p = mycurattr;
    111     while (p != NULL)
    112     {
    113 	pend = strchr (p, '+');
    114 	if (pend == NULL)
    115 	{
    116 	    pend = p + strlen (p);
    117 	    nextp = NULL;
    118 	}
    119 	else
    120 	    nextp = pend + 1;
    121 
    122 	/* Process this item.  */
    123 	if (pend - p == 4 && strncmp ("edit", p, 4) == 0)
    124 	{
    125 	    if (!remove_edit_pending)
    126 		strcat (mynewattr, "+edit");
    127 	    add_edit_pending = 0;
    128 	}
    129 	else if (pend - p == 6 && strncmp ("unedit", p, 6) == 0)
    130 	{
    131 	    if (!remove_unedit_pending)
    132 		strcat (mynewattr, "+unedit");
    133 	    add_unedit_pending = 0;
    134 	}
    135 	else if (pend - p == 6 && strncmp ("commit", p, 6) == 0)
    136 	{
    137 	    if (!remove_commit_pending)
    138 		strcat (mynewattr, "+commit");
    139 	    add_commit_pending = 0;
    140 	}
    141 	else if (pend - p == 5 && strncmp ("tedit", p, 5) == 0)
    142 	{
    143 	    if (!what->remove_temp)
    144 		strcat (mynewattr, "+tedit");
    145 	    add_tedit_pending = 0;
    146 	}
    147 	else if (pend - p == 7 && strncmp ("tunedit", p, 7) == 0)
    148 	{
    149 	    if (!what->remove_temp)
    150 		strcat (mynewattr, "+tunedit");
    151 	    add_tunedit_pending = 0;
    152 	}
    153 	else if (pend - p == 7 && strncmp ("tcommit", p, 7) == 0)
    154 	{
    155 	    if (!what->remove_temp)
    156 		strcat (mynewattr, "+tcommit");
    157 	    add_tcommit_pending = 0;
    158 	}
    159 	else
    160 	{
    161 	    char *mp;
    162 
    163 	    /* Copy over any unrecognized watch types, for future
    164 	       expansion.  */
    165 	    mp = mynewattr + strlen (mynewattr);
    166 	    *mp++ = '+';
    167 	    strncpy (mp, p, pend - p);
    168 	    *(mp + (pend - p)) = '\0';
    169 	}
    170 
    171 	/* Set up for next item.  */
    172 	p = nextp;
    173     }
    174 
    175     /* Add in new watch types.  */
    176     if (add_edit_pending)
    177 	strcat (mynewattr, "+edit");
    178     if (add_unedit_pending)
    179 	strcat (mynewattr, "+unedit");
    180     if (add_commit_pending)
    181 	strcat (mynewattr, "+commit");
    182     if (add_tedit_pending)
    183 	strcat (mynewattr, "+tedit");
    184     if (add_tunedit_pending)
    185 	strcat (mynewattr, "+tunedit");
    186     if (add_tcommit_pending)
    187 	strcat (mynewattr, "+tcommit");
    188 
    189     {
    190 	char *curattr_new;
    191 
    192 	curattr_new =
    193 	  fileattr_modify (curattr,
    194 			   who,
    195 			   mynewattr[0] == '\0' ? NULL : mynewattr + 1,
    196 			   '>',
    197 			   ',');
    198 	/* If the attribute is unchanged, don't rewrite the attribute file.  */
    199 	if (!((curattr_new == NULL && curattr == NULL)
    200 	      || (curattr_new != NULL
    201 		  && curattr != NULL
    202 		  && strcmp (curattr_new, curattr) == 0)))
    203 	    fileattr_set (file,
    204 			  "_watchers",
    205 			  curattr_new);
    206 	if (curattr_new != NULL)
    207 	    free (curattr_new);
    208     }
    209 
    210     if (curattr != NULL)
    211 	free (curattr);
    212     if (mycurattr != NULL)
    213 	free (mycurattr);
    214     if (mynewattr != NULL)
    215 	free (mynewattr);
    216 }
    217 
    218 static int addremove_fileproc (void *callerdat,
    219 				      struct file_info *finfo);
    220 
    221 static int
    222 addremove_fileproc (void *callerdat, struct file_info *finfo)
    223 {
    224     watch_modify_watchers (finfo->file, &the_args);
    225     return 0;
    226 }
    227 
    228 static int addremove_filesdoneproc (void * callerdat, int err, const char * repository,
    229                                            const char *update_dir, List * entries)
    230 {
    231     int set_default = the_args.setting_default;
    232     int dir_check = 0;
    233 
    234     while ( !set_default && dir_check < the_args.num_dirs )
    235     {
    236 	/* If we are recursing, then just see if the first part of update_dir
    237 	   matches any of the specified directories. Otherwise, it must be an exact
    238 	   match. */
    239 	if ( the_args.local )
    240 	    set_default = strcmp( update_dir, the_args.dirs[ dir_check ] )==0;
    241 	else
    242 	    set_default = strncmp( update_dir, the_args.dirs[ dir_check ], strlen( the_args.dirs[ dir_check ] ) ) == 0;
    243 	dir_check++;
    244     }
    245 
    246     if (set_default)
    247 	watch_modify_watchers (NULL, &the_args);
    248     return err;
    249 }
    250 
    251 
    252 static int
    253 watch_addremove (int argc, char **argv)
    254 {
    255     int c;
    256     int err;
    257     int a_omitted;
    258     int arg_index;
    259     int max_dirs;
    260 
    261     a_omitted = 1;
    262     the_args.commit = 0;
    263     the_args.edit = 0;
    264     the_args.unedit = 0;
    265     the_args.num_dirs = 0;
    266     the_args.dirs = NULL;
    267     the_args.local = 0;
    268 
    269     getoptreset ();
    270     while ((c = getopt (argc, argv, "+lRa:")) != -1)
    271     {
    272 	switch (c)
    273 	{
    274 	    case 'l':
    275 		the_args.local = 1;
    276 		break;
    277 	    case 'R':
    278 		the_args.local = 0;
    279 		break;
    280 	    case 'a':
    281 		a_omitted = 0;
    282 		if (strcmp (optarg, "edit") == 0)
    283 		    the_args.edit = 1;
    284 		else if (strcmp (optarg, "unedit") == 0)
    285 		    the_args.unedit = 1;
    286 		else if (strcmp (optarg, "commit") == 0)
    287 		    the_args.commit = 1;
    288 		else if (strcmp (optarg, "all") == 0)
    289 		{
    290 		    the_args.edit = 1;
    291 		    the_args.unedit = 1;
    292 		    the_args.commit = 1;
    293 		}
    294 		else if (strcmp (optarg, "none") == 0)
    295 		{
    296 		    the_args.edit = 0;
    297 		    the_args.unedit = 0;
    298 		    the_args.commit = 0;
    299 		}
    300 		else
    301 		    usage (watch_usage);
    302 		break;
    303 	    case '?':
    304 	    default:
    305 		usage (watch_usage);
    306 		break;
    307 	}
    308     }
    309     argc -= optind;
    310     argv += optind;
    311 
    312     the_args.num_dirs = 0;
    313     max_dirs = 4; /* Arbitrary choice. */
    314     the_args.dirs = xmalloc( sizeof( const char * ) * max_dirs );
    315 
    316     TRACE (TRACE_FUNCTION, "watch_addremove (%d)", argc);
    317     for ( arg_index=0; arg_index<argc; ++arg_index )
    318     {
    319 	TRACE( TRACE_FUNCTION, "\t%s", argv[ arg_index ]);
    320 	if ( isdir( argv[ arg_index ] ) )
    321 	{
    322 	    if ( the_args.num_dirs >= max_dirs )
    323 	    {
    324 		max_dirs *= 2;
    325 		the_args.dirs = (const char ** )xrealloc( (void *)the_args.dirs, max_dirs );
    326 	    }
    327 	    the_args.dirs[ the_args.num_dirs++ ] = argv[ arg_index ];
    328 	}
    329     }
    330 
    331     if (a_omitted)
    332     {
    333 	the_args.edit = 1;
    334 	the_args.unedit = 1;
    335 	the_args.commit = 1;
    336     }
    337 
    338 #ifdef CLIENT_SUPPORT
    339     if (current_parsed_root->isremote)
    340     {
    341 	start_server ();
    342 	ign_setup ();
    343 
    344 	if (the_args.local)
    345 	    send_arg ("-l");
    346 	/* FIXME: copes poorly with "all" if server is extended to have
    347 	   new watch types and client is still running an old version.  */
    348 	if (the_args.edit)
    349 	    option_with_arg ("-a", "edit");
    350 	if (the_args.unedit)
    351 	    option_with_arg ("-a", "unedit");
    352 	if (the_args.commit)
    353 	    option_with_arg ("-a", "commit");
    354 	if (!the_args.edit && !the_args.unedit && !the_args.commit)
    355 	    option_with_arg ("-a", "none");
    356 	send_arg ("--");
    357 	send_files (argc, argv, the_args.local, 0, SEND_NO_CONTENTS);
    358 	send_file_names (argc, argv, SEND_EXPAND_WILD);
    359 	send_to_server (the_args.adding ?
    360                         "watch-add\012" : "watch-remove\012",
    361                         0);
    362 	return get_responses_and_close ();
    363     }
    364 #endif /* CLIENT_SUPPORT */
    365 
    366     the_args.setting_default = (argc <= 0);
    367 
    368     lock_tree_promotably (argc, argv, the_args.local, W_LOCAL, 0);
    369 
    370     err = start_recursion
    371 	(addremove_fileproc, addremove_filesdoneproc, NULL, NULL, NULL,
    372 	 argc, argv, the_args.local, W_LOCAL, 0, CVS_LOCK_WRITE,
    373 	 NULL, 1, NULL);
    374 
    375     Lock_Cleanup ();
    376     free( (void *)the_args.dirs );
    377     the_args.dirs = NULL;
    378 
    379     return err;
    380 }
    381 
    382 
    383 
    384 int
    385 watch_add (int argc, char **argv)
    386 {
    387     the_args.adding = 1;
    388     return watch_addremove (argc, argv);
    389 }
    390 
    391 int
    392 watch_remove (int argc, char **argv)
    393 {
    394     the_args.adding = 0;
    395     return watch_addremove (argc, argv);
    396 }
    397 
    398 int
    399 watch (int argc, char **argv)
    400 {
    401     if (argc <= 1)
    402 	usage (watch_usage);
    403     if (strcmp (argv[1], "on") == 0)
    404     {
    405 	--argc;
    406 	++argv;
    407 	return watch_on (argc, argv);
    408     }
    409     else if (strcmp (argv[1], "off") == 0)
    410     {
    411 	--argc;
    412 	++argv;
    413 	return watch_off (argc, argv);
    414     }
    415     else if (strcmp (argv[1], "add") == 0)
    416     {
    417 	--argc;
    418 	++argv;
    419 	return watch_add (argc, argv);
    420     }
    421     else if (strcmp (argv[1], "remove") == 0)
    422     {
    423 	--argc;
    424 	++argv;
    425 	return watch_remove (argc, argv);
    426     }
    427     else
    428 	usage (watch_usage);
    429     return 0;
    430 }
    431 
    432 static const char *const watchers_usage[] =
    433 {
    434     "Usage: %s %s [-lR] [<file>]...\n",
    435     "-l\tProcess this directory only (not recursive).\n",
    436     "-R\tProcess directories recursively (default).\n",
    437     "(Specify the --help global option for a list of other help options.)\n",
    438     NULL
    439 };
    440 
    441 static int watchers_fileproc (void *callerdat,
    442 				     struct file_info *finfo);
    443 
    444 static int
    445 watchers_fileproc (void *callerdat, struct file_info *finfo)
    446 {
    447     char *them;
    448     char *p;
    449 
    450     them = fileattr_get0 (finfo->file, "_watchers");
    451     if (them == NULL)
    452 	return 0;
    453 
    454     cvs_output (finfo->fullname, 0);
    455 
    456     p = them;
    457     while (1)
    458     {
    459 	cvs_output ("\t", 1);
    460 	while (*p != '>' && *p != '\0')
    461 	    cvs_output (p++, 1);
    462 	if (*p == '\0')
    463 	{
    464 	    /* Only happens if attribute is misformed.  */
    465 	    cvs_output ("\n", 1);
    466 	    break;
    467 	}
    468 	++p;
    469 	cvs_output ("\t", 1);
    470 	while (1)
    471 	{
    472 	    while (*p != '+' && *p != ',' && *p != '\0')
    473 		cvs_output (p++, 1);
    474 	    if (*p == '\0')
    475 	    {
    476 		cvs_output ("\n", 1);
    477 		goto out;
    478 	    }
    479 	    if (*p == ',')
    480 	    {
    481 		++p;
    482 		break;
    483 	    }
    484 	    ++p;
    485 	    cvs_output ("\t", 1);
    486 	}
    487 	cvs_output ("\n", 1);
    488     }
    489   out:;
    490     free (them);
    491     return 0;
    492 }
    493 
    494 int
    495 watchers (int argc, char **argv)
    496 {
    497     int local = 0;
    498     int c;
    499 
    500     if (argc == -1)
    501 	usage (watchers_usage);
    502 
    503     getoptreset ();
    504     while ((c = getopt (argc, argv, "+lR")) != -1)
    505     {
    506 	switch (c)
    507 	{
    508 	    case 'l':
    509 		local = 1;
    510 		break;
    511 	    case 'R':
    512 		local = 0;
    513 		break;
    514 	    case '?':
    515 	    default:
    516 		usage (watchers_usage);
    517 		break;
    518 	}
    519     }
    520     argc -= optind;
    521     argv += optind;
    522 
    523 #ifdef CLIENT_SUPPORT
    524     if (current_parsed_root->isremote)
    525     {
    526 	start_server ();
    527 	ign_setup ();
    528 
    529 	if (local)
    530 	    send_arg ("-l");
    531 	send_arg ("--");
    532 	send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
    533 	send_file_names (argc, argv, SEND_EXPAND_WILD);
    534 	send_to_server ("watchers\012", 0);
    535 	return get_responses_and_close ();
    536     }
    537 #endif /* CLIENT_SUPPORT */
    538 
    539     return start_recursion (watchers_fileproc, NULL, NULL,
    540 			    NULL, NULL, argc, argv, local, W_LOCAL, 0,
    541 			    CVS_LOCK_READ, NULL, 1, NULL);
    542 }
    543