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