xref: /openbsd/gnu/usr.bin/cvs/src/patch.c (revision f9bbbf45)
11e72d8d2Sderaadt /*
21e72d8d2Sderaadt  * Copyright (c) 1992, Brian Berliner and Jeff Polk
31e72d8d2Sderaadt  * Copyright (c) 1989-1992, Brian Berliner
41e72d8d2Sderaadt  *
51e72d8d2Sderaadt  * You may distribute under the terms of the GNU General Public License as
6b2b690deStholo  * specified in the README file that comes with the CVS source distribution.
71e72d8d2Sderaadt  *
81e72d8d2Sderaadt  * Patch
91e72d8d2Sderaadt  *
101e72d8d2Sderaadt  * Create a Larry Wall format "patch" file between a previous release and the
111e72d8d2Sderaadt  * current head of a module, or between two releases.  Can specify the
121e72d8d2Sderaadt  * release as either a date or a revision number.
131e72d8d2Sderaadt  */
141e72d8d2Sderaadt 
1579822b2aStholo #include <assert.h>
161e72d8d2Sderaadt #include "cvs.h"
1713571821Stholo #include "getline.h"
181e72d8d2Sderaadt 
191e72d8d2Sderaadt static RETSIGTYPE patch_cleanup PROTO((void));
207e4d5a85Stholo static Dtype patch_dirproc PROTO ((void *callerdat, char *dir,
217e4d5a85Stholo 				   char *repos, char *update_dir,
227e4d5a85Stholo 				   List *entries));
237e4d5a85Stholo static int patch_fileproc PROTO ((void *callerdat, struct file_info *finfo));
24bde78045Stholo static int patch_proc PROTO((int argc, char **argv, char *xwhere,
251e72d8d2Sderaadt 		       char *mwhere, char *mfile, int shorten,
261e72d8d2Sderaadt 		       int local_specified, char *mname, char *msg));
271e72d8d2Sderaadt 
281e72d8d2Sderaadt static int force_tag_match = 1;
291e72d8d2Sderaadt static int patch_short = 0;
301e72d8d2Sderaadt static int toptwo_diffs = 0;
311e72d8d2Sderaadt static int local = 0;
321e72d8d2Sderaadt static char *options = NULL;
331e72d8d2Sderaadt static char *rev1 = NULL;
347e4d5a85Stholo static int rev1_validated = 0;
351e72d8d2Sderaadt static char *rev2 = NULL;
367e4d5a85Stholo static int rev2_validated = 0;
371e72d8d2Sderaadt static char *date1 = NULL;
381e72d8d2Sderaadt static char *date2 = NULL;
397e4d5a85Stholo static char *tmpfile1 = NULL;
407e4d5a85Stholo static char *tmpfile2 = NULL;
417e4d5a85Stholo static char *tmpfile3 = NULL;
421e72d8d2Sderaadt static int unidiff = 0;
431e72d8d2Sderaadt 
441e72d8d2Sderaadt static const char *const patch_usage[] =
451e72d8d2Sderaadt {
46066e68faStholo     "Usage: %s %s [-flR] [-c|-u] [-s|-t] [-V %%d]\n",
471e72d8d2Sderaadt     "    -r rev|-D date [-r rev2 | -D date2] modules...\n",
481e72d8d2Sderaadt     "\t-f\tForce a head revision match if tag/date not found.\n",
491e72d8d2Sderaadt     "\t-l\tLocal directory only, not recursive\n",
50066e68faStholo     "\t-R\tProcess directories recursively.\n",
511e72d8d2Sderaadt     "\t-c\tContext diffs (default)\n",
521e72d8d2Sderaadt     "\t-u\tUnidiff format.\n",
531e72d8d2Sderaadt     "\t-s\tShort patch - one liner per file.\n",
541e72d8d2Sderaadt     "\t-t\tTop two diffs - last change made to the file.\n",
551e72d8d2Sderaadt     "\t-D date\tDate.\n",
561e72d8d2Sderaadt     "\t-r rev\tRevision - symbolic or numeric.\n",
571e72d8d2Sderaadt     "\t-V vers\tUse RCS Version \"vers\" for keyword expansion.\n",
58b2b690deStholo     "(Specify the --help global option for a list of other help options)\n",
591e72d8d2Sderaadt     NULL
601e72d8d2Sderaadt };
611e72d8d2Sderaadt 
621e72d8d2Sderaadt int
patch(argc,argv)631e72d8d2Sderaadt patch (argc, argv)
641e72d8d2Sderaadt     int argc;
651e72d8d2Sderaadt     char **argv;
661e72d8d2Sderaadt {
671e72d8d2Sderaadt     register int i;
681e72d8d2Sderaadt     int c;
691e72d8d2Sderaadt     int err = 0;
701e72d8d2Sderaadt     DBM *db;
711e72d8d2Sderaadt 
721e72d8d2Sderaadt     if (argc == -1)
731e72d8d2Sderaadt 	usage (patch_usage);
741e72d8d2Sderaadt 
75066e68faStholo     optind = 0;
76172be96aStholo     while ((c = getopt (argc, argv, "+V:k:cuftsQqlRD:r:")) != -1)
771e72d8d2Sderaadt     {
781e72d8d2Sderaadt 	switch (c)
791e72d8d2Sderaadt 	{
801e72d8d2Sderaadt 	    case 'Q':
811e72d8d2Sderaadt 	    case 'q':
821e72d8d2Sderaadt #ifdef SERVER_SUPPORT
831e72d8d2Sderaadt 		/* The CVS 1.5 client sends these options (in addition to
841e72d8d2Sderaadt 		   Global_option requests), so we must ignore them.  */
851e72d8d2Sderaadt 		if (!server_active)
861e72d8d2Sderaadt #endif
871e72d8d2Sderaadt 		    error (1, 0,
881e72d8d2Sderaadt 			   "-q or -Q must be specified before \"%s\"",
891e72d8d2Sderaadt 			   command_name);
901e72d8d2Sderaadt 		break;
911e72d8d2Sderaadt 	    case 'f':
921e72d8d2Sderaadt 		force_tag_match = 0;
931e72d8d2Sderaadt 		break;
941e72d8d2Sderaadt 	    case 'l':
951e72d8d2Sderaadt 		local = 1;
961e72d8d2Sderaadt 		break;
971e72d8d2Sderaadt 	    case 'R':
981e72d8d2Sderaadt 		local = 0;
991e72d8d2Sderaadt 		break;
1001e72d8d2Sderaadt 	    case 't':
1011e72d8d2Sderaadt 		toptwo_diffs = 1;
1021e72d8d2Sderaadt 		break;
1031e72d8d2Sderaadt 	    case 's':
1041e72d8d2Sderaadt 		patch_short = 1;
1051e72d8d2Sderaadt 		break;
1061e72d8d2Sderaadt 	    case 'D':
1071e72d8d2Sderaadt 		if (rev2 != NULL || date2 != NULL)
1081e72d8d2Sderaadt 		    error (1, 0,
1091e72d8d2Sderaadt 		       "no more than two revisions/dates can be specified");
1101e72d8d2Sderaadt 		if (rev1 != NULL || date1 != NULL)
1111e72d8d2Sderaadt 		    date2 = Make_Date (optarg);
1121e72d8d2Sderaadt 		else
1131e72d8d2Sderaadt 		    date1 = Make_Date (optarg);
1141e72d8d2Sderaadt 		break;
1151e72d8d2Sderaadt 	    case 'r':
1161e72d8d2Sderaadt 		if (rev2 != NULL || date2 != NULL)
1171e72d8d2Sderaadt 		    error (1, 0,
1181e72d8d2Sderaadt 		       "no more than two revisions/dates can be specified");
1191e72d8d2Sderaadt 		if (rev1 != NULL || date1 != NULL)
1201e72d8d2Sderaadt 		    rev2 = optarg;
1211e72d8d2Sderaadt 		else
1221e72d8d2Sderaadt 		    rev1 = optarg;
1231e72d8d2Sderaadt 		break;
1241e72d8d2Sderaadt 	    case 'k':
1251e72d8d2Sderaadt 		if (options)
1261e72d8d2Sderaadt 		    free (options);
1271e72d8d2Sderaadt 		options = RCS_check_kflag (optarg);
1281e72d8d2Sderaadt 		break;
1291e72d8d2Sderaadt 	    case 'V':
130c56b20e6Stholo 		/* This option is pretty seriously broken:
131c56b20e6Stholo 		   1.  It is not clear what it does (does it change keyword
132c56b20e6Stholo 		   expansion behavior?  If so, how?  Or does it have
133c56b20e6Stholo 		   something to do with what version of RCS we are using?
134c56b20e6Stholo 		   Or the format we write RCS files in?).
135c56b20e6Stholo 		   2.  Because both it and -k use the options variable,
136c56b20e6Stholo 		   specifying both -V and -k doesn't work.
137c56b20e6Stholo 		   3.  At least as of CVS 1.9, it doesn't work (failed
138c56b20e6Stholo 		   assertion in RCS_checkout where it asserts that options
139c56b20e6Stholo 		   starts with -k).  Few people seem to be complaining.
140c56b20e6Stholo 		   In the future (perhaps the near future), I have in mind
141c56b20e6Stholo 		   removing it entirely, and updating NEWS and cvs.texinfo,
142c56b20e6Stholo 		   but in case it is a good idea to give people more time
143c56b20e6Stholo 		   to complain if they would miss it, I'll just add this
144c56b20e6Stholo 		   quick and dirty error message for now.  */
145c56b20e6Stholo 		error (1, 0,
146c56b20e6Stholo 		       "the -V option is obsolete and should not be used");
147c56b20e6Stholo #if 0
1481e72d8d2Sderaadt 		if (atoi (optarg) <= 0)
1491e72d8d2Sderaadt 		    error (1, 0, "must specify a version number to -V");
1501e72d8d2Sderaadt 		if (options)
1511e72d8d2Sderaadt 		    free (options);
1521e72d8d2Sderaadt 		options = xmalloc (strlen (optarg) + 1 + 2);	/* for the -V */
1531e72d8d2Sderaadt 		(void) sprintf (options, "-V%s", optarg);
154c56b20e6Stholo #endif
1551e72d8d2Sderaadt 		break;
1561e72d8d2Sderaadt 	    case 'u':
1571e72d8d2Sderaadt 		unidiff = 1;		/* Unidiff */
1581e72d8d2Sderaadt 		break;
1591e72d8d2Sderaadt 	    case 'c':			/* Context diff */
1601e72d8d2Sderaadt 		unidiff = 0;
1611e72d8d2Sderaadt 		break;
1621e72d8d2Sderaadt 	    case '?':
1631e72d8d2Sderaadt 	    default:
1641e72d8d2Sderaadt 		usage (patch_usage);
1651e72d8d2Sderaadt 		break;
1661e72d8d2Sderaadt 	}
1671e72d8d2Sderaadt     }
1681e72d8d2Sderaadt     argc -= optind;
1691e72d8d2Sderaadt     argv += optind;
1701e72d8d2Sderaadt 
1711e72d8d2Sderaadt     /* Sanity checks */
1721e72d8d2Sderaadt     if (argc < 1)
1731e72d8d2Sderaadt 	usage (patch_usage);
1741e72d8d2Sderaadt 
1751e72d8d2Sderaadt     if (toptwo_diffs && patch_short)
1761e72d8d2Sderaadt 	error (1, 0, "-t and -s options are mutually exclusive");
1771e72d8d2Sderaadt     if (toptwo_diffs && (date1 != NULL || date2 != NULL ||
1781e72d8d2Sderaadt 			 rev1 != NULL || rev2 != NULL))
1791e72d8d2Sderaadt 	error (1, 0, "must not specify revisions/dates with -t option!");
1801e72d8d2Sderaadt 
1811e72d8d2Sderaadt     if (!toptwo_diffs && (date1 == NULL && date2 == NULL &&
1821e72d8d2Sderaadt 			  rev1 == NULL && rev2 == NULL))
1831e72d8d2Sderaadt 	error (1, 0, "must specify at least one revision/date!");
1841e72d8d2Sderaadt     if (date1 != NULL && date2 != NULL)
1851e72d8d2Sderaadt 	if (RCS_datecmp (date1, date2) >= 0)
1861e72d8d2Sderaadt 	    error (1, 0, "second date must come after first date!");
1871e72d8d2Sderaadt 
1881e72d8d2Sderaadt     /* if options is NULL, make it a NULL string */
1891e72d8d2Sderaadt     if (options == NULL)
1901e72d8d2Sderaadt 	options = xstrdup ("");
1911e72d8d2Sderaadt 
1921e72d8d2Sderaadt #ifdef CLIENT_SUPPORT
19379822b2aStholo     if (current_parsed_root->isremote)
1941e72d8d2Sderaadt     {
1951e72d8d2Sderaadt 	/* We're the client side.  Fire up the remote server.  */
1961e72d8d2Sderaadt 	start_server ();
1971e72d8d2Sderaadt 
1981e72d8d2Sderaadt 	ign_setup ();
1991e72d8d2Sderaadt 
2001e72d8d2Sderaadt 	if (local)
2011e72d8d2Sderaadt 	    send_arg("-l");
20228393fc3Sniklas 	if (!force_tag_match)
2031e72d8d2Sderaadt 	    send_arg("-f");
2041e72d8d2Sderaadt 	if (toptwo_diffs)
2051e72d8d2Sderaadt 	    send_arg("-t");
2061e72d8d2Sderaadt 	if (patch_short)
2071e72d8d2Sderaadt 	    send_arg("-s");
2081e72d8d2Sderaadt 	if (unidiff)
2091e72d8d2Sderaadt 	    send_arg("-u");
2101e72d8d2Sderaadt 
2111e72d8d2Sderaadt 	if (rev1)
2121e72d8d2Sderaadt 	    option_with_arg ("-r", rev1);
2131e72d8d2Sderaadt 	if (date1)
2141e72d8d2Sderaadt 	    client_senddate (date1);
2151e72d8d2Sderaadt 	if (rev2)
2161e72d8d2Sderaadt 	    option_with_arg ("-r", rev2);
2171e72d8d2Sderaadt 	if (date2)
2181e72d8d2Sderaadt 	    client_senddate (date2);
2191e72d8d2Sderaadt 	if (options[0] != '\0')
2201e72d8d2Sderaadt 	    send_arg (options);
2211e72d8d2Sderaadt 
2221e72d8d2Sderaadt 	{
2231e72d8d2Sderaadt 	    int i;
2241e72d8d2Sderaadt 	    for (i = 0; i < argc; ++i)
2251e72d8d2Sderaadt 		send_arg (argv[i]);
2261e72d8d2Sderaadt 	}
2271e72d8d2Sderaadt 
22813571821Stholo 	send_to_server ("rdiff\012", 0);
2291e72d8d2Sderaadt         return get_responses_and_close ();
2301e72d8d2Sderaadt     }
2311e72d8d2Sderaadt #endif
2321e72d8d2Sderaadt 
2331e72d8d2Sderaadt     /* clean up if we get a signal */
234bde78045Stholo #ifdef SIGABRT
235bde78045Stholo     (void) SIG_register (SIGABRT, patch_cleanup);
236bde78045Stholo #endif
2371e72d8d2Sderaadt #ifdef SIGHUP
2381e72d8d2Sderaadt     (void) SIG_register (SIGHUP, patch_cleanup);
2391e72d8d2Sderaadt #endif
2401e72d8d2Sderaadt #ifdef SIGINT
2411e72d8d2Sderaadt     (void) SIG_register (SIGINT, patch_cleanup);
2421e72d8d2Sderaadt #endif
2431e72d8d2Sderaadt #ifdef SIGQUIT
2441e72d8d2Sderaadt     (void) SIG_register (SIGQUIT, patch_cleanup);
2451e72d8d2Sderaadt #endif
2461e72d8d2Sderaadt #ifdef SIGPIPE
2471e72d8d2Sderaadt     (void) SIG_register (SIGPIPE, patch_cleanup);
2481e72d8d2Sderaadt #endif
2491e72d8d2Sderaadt #ifdef SIGTERM
2501e72d8d2Sderaadt     (void) SIG_register (SIGTERM, patch_cleanup);
2511e72d8d2Sderaadt #endif
2521e72d8d2Sderaadt 
2531e72d8d2Sderaadt     db = open_module ();
2541e72d8d2Sderaadt     for (i = 0; i < argc; i++)
2551e72d8d2Sderaadt 	err += do_module (db, argv[i], PATCH, "Patching", patch_proc,
25679822b2aStholo 			  (char *) NULL, 0, 0, 0, 0, (char *) NULL);
2571e72d8d2Sderaadt     close_module (db);
2581e72d8d2Sderaadt     free (options);
2591e72d8d2Sderaadt     patch_cleanup ();
2601e72d8d2Sderaadt     return (err);
2611e72d8d2Sderaadt }
2621e72d8d2Sderaadt 
2631e72d8d2Sderaadt /*
2641e72d8d2Sderaadt  * callback proc for doing the real work of patching
2651e72d8d2Sderaadt  */
2661e72d8d2Sderaadt /* ARGSUSED */
2671e72d8d2Sderaadt static int
patch_proc(argc,argv,xwhere,mwhere,mfile,shorten,local_specified,mname,msg)268bde78045Stholo patch_proc (argc, argv, xwhere, mwhere, mfile, shorten, local_specified,
2691e72d8d2Sderaadt 	    mname, msg)
270bde78045Stholo     int argc;
2711e72d8d2Sderaadt     char **argv;
2721e72d8d2Sderaadt     char *xwhere;
2731e72d8d2Sderaadt     char *mwhere;
2741e72d8d2Sderaadt     char *mfile;
2751e72d8d2Sderaadt     int shorten;
2761e72d8d2Sderaadt     int local_specified;
2771e72d8d2Sderaadt     char *mname;
2781e72d8d2Sderaadt     char *msg;
2791e72d8d2Sderaadt {
280bde78045Stholo     char *myargv[2];
2811e72d8d2Sderaadt     int err = 0;
2821e72d8d2Sderaadt     int which;
283c56b20e6Stholo     char *repository;
284c56b20e6Stholo     char *where;
2851e72d8d2Sderaadt 
28679822b2aStholo     repository = xmalloc (strlen (current_parsed_root->directory) + strlen (argv[0])
28779822b2aStholo 			  + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2);
28879822b2aStholo     (void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]);
28979822b2aStholo     where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile) + 1)
29079822b2aStholo 		     + 1);
2911e72d8d2Sderaadt     (void) strcpy (where, argv[0]);
2921e72d8d2Sderaadt 
2931e72d8d2Sderaadt     /* if mfile isn't null, we need to set up to do only part of the module */
2941e72d8d2Sderaadt     if (mfile != NULL)
2951e72d8d2Sderaadt     {
2961e72d8d2Sderaadt 	char *cp;
297c56b20e6Stholo 	char *path;
2981e72d8d2Sderaadt 
2991e72d8d2Sderaadt 	/* if the portion of the module is a path, put the dir part on repos */
3001e72d8d2Sderaadt 	if ((cp = strrchr (mfile, '/')) != NULL)
3011e72d8d2Sderaadt 	{
3021e72d8d2Sderaadt 	    *cp = '\0';
3031e72d8d2Sderaadt 	    (void) strcat (repository, "/");
3041e72d8d2Sderaadt 	    (void) strcat (repository, mfile);
3051e72d8d2Sderaadt 	    (void) strcat (where, "/");
3061e72d8d2Sderaadt 	    (void) strcat (where, mfile);
3071e72d8d2Sderaadt 	    mfile = cp + 1;
3081e72d8d2Sderaadt 	}
3091e72d8d2Sderaadt 
3101e72d8d2Sderaadt 	/* take care of the rest */
31179822b2aStholo 	path = xmalloc (strlen (repository) + strlen (mfile) + 2);
3121e72d8d2Sderaadt 	(void) sprintf (path, "%s/%s", repository, mfile);
3131e72d8d2Sderaadt 	if (isdir (path))
3141e72d8d2Sderaadt 	{
3151e72d8d2Sderaadt 	    /* directory means repository gets the dir tacked on */
3161e72d8d2Sderaadt 	    (void) strcpy (repository, path);
3171e72d8d2Sderaadt 	    (void) strcat (where, "/");
3181e72d8d2Sderaadt 	    (void) strcat (where, mfile);
3191e72d8d2Sderaadt 	}
3201e72d8d2Sderaadt 	else
3211e72d8d2Sderaadt 	{
322bde78045Stholo 	    myargv[0] = argv[0];
323bde78045Stholo 	    myargv[1] = mfile;
324bde78045Stholo 	    argc = 2;
325bde78045Stholo 	    argv = myargv;
3261e72d8d2Sderaadt 	}
327c56b20e6Stholo 	free (path);
3281e72d8d2Sderaadt     }
3291e72d8d2Sderaadt 
3301e72d8d2Sderaadt     /* cd to the starting repository */
3317e4d5a85Stholo     if ( CVS_CHDIR (repository) < 0)
3321e72d8d2Sderaadt     {
3331e72d8d2Sderaadt 	error (0, errno, "cannot chdir to %s", repository);
334c56b20e6Stholo 	free (repository);
3351e72d8d2Sderaadt 	return (1);
3361e72d8d2Sderaadt     }
337c56b20e6Stholo     free (repository);
3381e72d8d2Sderaadt 
3391e72d8d2Sderaadt     if (force_tag_match)
3401e72d8d2Sderaadt 	which = W_REPOS | W_ATTIC;
3411e72d8d2Sderaadt     else
3421e72d8d2Sderaadt 	which = W_REPOS;
3431e72d8d2Sderaadt 
34413571821Stholo     if (rev1 != NULL && !rev1_validated)
34513571821Stholo     {
346bde78045Stholo 	tag_check_valid (rev1, argc - 1, argv + 1, local, 0, NULL);
34713571821Stholo 	rev1_validated = 1;
34813571821Stholo     }
34913571821Stholo     if (rev2 != NULL && !rev2_validated)
35013571821Stholo     {
351bde78045Stholo 	tag_check_valid (rev2, argc - 1, argv + 1, local, 0, NULL);
35213571821Stholo 	rev2_validated = 1;
35313571821Stholo     }
35413571821Stholo 
3551e72d8d2Sderaadt     /* start the recursion processor */
35613571821Stholo     err = start_recursion (patch_fileproc, (FILESDONEPROC) NULL, patch_dirproc,
3577e4d5a85Stholo 			   (DIRLEAVEPROC) NULL, NULL,
358bde78045Stholo 			   argc - 1, argv + 1, local,
3597e4d5a85Stholo 			   which, 0, 1, where, 1);
360c56b20e6Stholo     free (where);
3611e72d8d2Sderaadt 
3621e72d8d2Sderaadt     return (err);
3631e72d8d2Sderaadt }
3641e72d8d2Sderaadt 
3651e72d8d2Sderaadt /*
3661e72d8d2Sderaadt  * Called to examine a particular RCS file, as appropriate with the options
3671e72d8d2Sderaadt  * that were set above.
3681e72d8d2Sderaadt  */
3691e72d8d2Sderaadt /* ARGSUSED */
3701e72d8d2Sderaadt static int
patch_fileproc(callerdat,finfo)3717e4d5a85Stholo patch_fileproc (callerdat, finfo)
3727e4d5a85Stholo     void *callerdat;
37345381119Stholo     struct file_info *finfo;
3741e72d8d2Sderaadt {
3751e72d8d2Sderaadt     struct utimbuf t;
3761e72d8d2Sderaadt     char *vers_tag, *vers_head;
377c56b20e6Stholo     char *rcs = NULL;
378420bf761Sotto     char *rcs_orig = NULL;
3791e72d8d2Sderaadt     RCSNode *rcsfile;
3801e72d8d2Sderaadt     FILE *fp1, *fp2, *fp3;
3811e72d8d2Sderaadt     int ret = 0;
3821e72d8d2Sderaadt     int isattic = 0;
3831e72d8d2Sderaadt     int retcode = 0;
384c56b20e6Stholo     char *file1;
385c56b20e6Stholo     char *file2;
386c56b20e6Stholo     char *strippath;
38713571821Stholo     char *line1, *line2;
38813571821Stholo     size_t line1_chars_allocated;
38913571821Stholo     size_t line2_chars_allocated;
3909d9224ffStholo     char *cp1, *cp2;
3911e72d8d2Sderaadt     FILE *fp;
392b2b690deStholo     int line_length;
3931e72d8d2Sderaadt 
394ae5e8a9bStholo     line1 = NULL;
395ae5e8a9bStholo     line1_chars_allocated = 0;
396ae5e8a9bStholo     line2 = NULL;
397ae5e8a9bStholo     line2_chars_allocated = 0;
398ae5e8a9bStholo 
3991e72d8d2Sderaadt     /* find the parsed rcs file */
4009d9224ffStholo     if ((rcsfile = finfo->rcs) == NULL)
401c56b20e6Stholo     {
402c56b20e6Stholo 	ret = 1;
403c56b20e6Stholo 	goto out2;
404c56b20e6Stholo     }
4051e72d8d2Sderaadt     if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
4061e72d8d2Sderaadt 	isattic = 1;
4071e72d8d2Sderaadt 
408420bf761Sotto     rcs_orig = rcs = xmalloc (strlen (finfo->file) + sizeof (RCSEXT) + 5);
40945381119Stholo     (void) sprintf (rcs, "%s%s", finfo->file, RCSEXT);
4101e72d8d2Sderaadt 
4111e72d8d2Sderaadt     /* if vers_head is NULL, may have been removed from the release */
4121e72d8d2Sderaadt     if (isattic && rev2 == NULL && date2 == NULL)
4131e72d8d2Sderaadt 	vers_head = NULL;
4141e72d8d2Sderaadt     else
4157e4d5a85Stholo     {
4167e4d5a85Stholo 	vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match,
4177e4d5a85Stholo 				    (int *) NULL);
4187e4d5a85Stholo 	if (vers_head != NULL && RCS_isdead (rcsfile, vers_head))
4197e4d5a85Stholo 	{
4207e4d5a85Stholo 	    free (vers_head);
4217e4d5a85Stholo 	    vers_head = NULL;
4227e4d5a85Stholo 	}
4237e4d5a85Stholo     }
4241e72d8d2Sderaadt 
4251e72d8d2Sderaadt     if (toptwo_diffs)
4261e72d8d2Sderaadt     {
4271e72d8d2Sderaadt 	if (vers_head == NULL)
428c56b20e6Stholo 	{
429c56b20e6Stholo 	    ret = 1;
430c56b20e6Stholo 	    goto out2;
431c56b20e6Stholo 	}
4321e72d8d2Sderaadt 
4331e72d8d2Sderaadt 	if (!date1)
434c56b20e6Stholo 	    date1 = xmalloc (MAXDATELEN);
4351e72d8d2Sderaadt 	*date1 = '\0';
4361e72d8d2Sderaadt 	if (RCS_getrevtime (rcsfile, vers_head, date1, 1) == -1)
4371e72d8d2Sderaadt 	{
4381e72d8d2Sderaadt 	    if (!really_quiet)
4391e72d8d2Sderaadt 		error (0, 0, "cannot find date in rcs file %s revision %s",
4401e72d8d2Sderaadt 		       rcs, vers_head);
441c56b20e6Stholo 	    ret = 1;
442c56b20e6Stholo 	    goto out2;
4431e72d8d2Sderaadt 	}
4441e72d8d2Sderaadt     }
4457e4d5a85Stholo     vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match,
4467e4d5a85Stholo 			       (int *) NULL);
4477e4d5a85Stholo     if (vers_tag != NULL && RCS_isdead (rcsfile, vers_tag))
4487e4d5a85Stholo     {
4497e4d5a85Stholo         free (vers_tag);
4507e4d5a85Stholo 	vers_tag = NULL;
4517e4d5a85Stholo     }
4521e72d8d2Sderaadt 
4537e4d5a85Stholo     if (vers_tag == NULL && vers_head == NULL)
454c56b20e6Stholo     {
455c56b20e6Stholo 	/* Nothing known about specified revs.  */
456c56b20e6Stholo 	ret = 0;
457c56b20e6Stholo 	goto out2;
458c56b20e6Stholo     }
4591e72d8d2Sderaadt 
4601e72d8d2Sderaadt     if (vers_tag && vers_head && strcmp (vers_head, vers_tag) == 0)
461c56b20e6Stholo     {
462c56b20e6Stholo 	/* Not changed between releases.  */
463c56b20e6Stholo 	ret = 0;
464c56b20e6Stholo 	goto out2;
465c56b20e6Stholo     }
4661e72d8d2Sderaadt 
4671e72d8d2Sderaadt     if (patch_short)
4681e72d8d2Sderaadt     {
469b2b690deStholo 	cvs_output ("File ", 0);
470b2b690deStholo 	cvs_output (finfo->fullname, 0);
4711e72d8d2Sderaadt 	if (vers_tag == NULL)
472b2b690deStholo 	{
473b2b690deStholo 	    cvs_output (" is new; current revision ", 0);
474b2b690deStholo 	    cvs_output (vers_head, 0);
475b2b690deStholo 	    cvs_output ("\n", 1);
476b2b690deStholo 	}
4771e72d8d2Sderaadt 	else if (vers_head == NULL)
4781e72d8d2Sderaadt 	{
479b2b690deStholo 	    cvs_output (" is removed; not included in ", 0);
4801e72d8d2Sderaadt 	    if (rev2 != NULL)
481b2b690deStholo 	    {
482b2b690deStholo 		cvs_output ("release tag ", 0);
483b2b690deStholo 		cvs_output (rev2, 0);
484b2b690deStholo 	    }
4851e72d8d2Sderaadt 	    else if (date2 != NULL)
486b2b690deStholo 	    {
487b2b690deStholo 		cvs_output ("release date ", 0);
488b2b690deStholo 		cvs_output (date2, 0);
4891e72d8d2Sderaadt 	    }
4901e72d8d2Sderaadt 	    else
491b2b690deStholo 		cvs_output ("current release", 0);
492b2b690deStholo 	    cvs_output ("\n", 1);
493b2b690deStholo 	}
494b2b690deStholo 	else
495b2b690deStholo 	{
496b2b690deStholo 	    cvs_output (" changed from revision ", 0);
497b2b690deStholo 	    cvs_output (vers_tag, 0);
498b2b690deStholo 	    cvs_output (" to ", 0);
499b2b690deStholo 	    cvs_output (vers_head, 0);
500b2b690deStholo 	    cvs_output ("\n", 1);
501b2b690deStholo 	}
502c56b20e6Stholo 	ret = 0;
503c56b20e6Stholo 	goto out2;
5041e72d8d2Sderaadt     }
50578c87a5cStholo 
50678c87a5cStholo     /* Create 3 empty files.  I'm not really sure there is any advantage
50779822b2aStholo      * to doing so now rather than just waiting until later.
50879822b2aStholo      *
50979822b2aStholo      * There is - cvs_temp_file opens the file so that it can guarantee that
51079822b2aStholo      * we have exclusive write access to the file.  Unfortunately we spoil that
51179822b2aStholo      * by closing it and reopening it again.  Of course any better solution
51279822b2aStholo      * requires that the RCS functions accept open file pointers rather than
51379822b2aStholo      * simple file names.
51479822b2aStholo      */
51579822b2aStholo     if ((fp1 = cvs_temp_file (&tmpfile1)) == NULL)
5161e72d8d2Sderaadt     {
51778c87a5cStholo 	error (0, errno, "cannot create temporary file %s", tmpfile1);
5181e72d8d2Sderaadt 	ret = 1;
5191e72d8d2Sderaadt 	goto out;
5201e72d8d2Sderaadt     }
52178c87a5cStholo     else
52278c87a5cStholo 	if (fclose (fp1) < 0)
52378c87a5cStholo 	    error (0, errno, "warning: cannot close %s", tmpfile1);
52479822b2aStholo     if ((fp2 = cvs_temp_file (&tmpfile2)) == NULL)
52578c87a5cStholo     {
52678c87a5cStholo 	error (0, errno, "cannot create temporary file %s", tmpfile2);
52778c87a5cStholo 	ret = 1;
52878c87a5cStholo 	goto out;
52978c87a5cStholo     }
53078c87a5cStholo     else
53178c87a5cStholo 	if (fclose (fp2) < 0)
53278c87a5cStholo 	    error (0, errno, "warning: cannot close %s", tmpfile2);
53379822b2aStholo     if ((fp3 = cvs_temp_file (&tmpfile3)) == NULL)
53478c87a5cStholo     {
53578c87a5cStholo 	error (0, errno, "cannot create temporary file %s", tmpfile3);
53678c87a5cStholo 	ret = 1;
53778c87a5cStholo 	goto out;
53878c87a5cStholo     }
53978c87a5cStholo     else
54078c87a5cStholo 	if (fclose (fp3) < 0)
54178c87a5cStholo 	    error (0, errno, "warning: cannot close %s", tmpfile3);
54278c87a5cStholo 
5431e72d8d2Sderaadt     if (vers_tag != NULL)
5441e72d8d2Sderaadt     {
5457e4d5a85Stholo 	retcode = RCS_checkout (rcsfile, (char *) NULL, vers_tag,
546172be96aStholo 				rev1, options, tmpfile1,
547172be96aStholo 				(RCSCHECKOUTPROC) NULL, (void *) NULL);
54845381119Stholo 	if (retcode != 0)
5491e72d8d2Sderaadt 	{
55078c87a5cStholo 	    error (0, 0,
55178c87a5cStholo 		   "cannot check out revision %s of %s", vers_tag, rcs);
5521e72d8d2Sderaadt 	    ret = 1;
5531e72d8d2Sderaadt 	    goto out;
5541e72d8d2Sderaadt 	}
5551e72d8d2Sderaadt 	memset ((char *) &t, 0, sizeof (t));
5561e72d8d2Sderaadt 	if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_tag,
5571e72d8d2Sderaadt 						    (char *) 0, 0)) != -1)
558066e68faStholo 	    /* I believe this timestamp only affects the dates in our diffs,
559066e68faStholo 	       and therefore should be on the server, not the client.  */
5601e72d8d2Sderaadt 	    (void) utime (tmpfile1, &t);
5611e72d8d2Sderaadt     }
5621e72d8d2Sderaadt     else if (toptwo_diffs)
5631e72d8d2Sderaadt     {
5641e72d8d2Sderaadt 	ret = 1;
5651e72d8d2Sderaadt 	goto out;
5661e72d8d2Sderaadt     }
5671e72d8d2Sderaadt     if (vers_head != NULL)
5681e72d8d2Sderaadt     {
5697e4d5a85Stholo 	retcode = RCS_checkout (rcsfile, (char *) NULL, vers_head,
570172be96aStholo 				rev2, options, tmpfile2,
571172be96aStholo 				(RCSCHECKOUTPROC) NULL, (void *) NULL);
57245381119Stholo 	if (retcode != 0)
5731e72d8d2Sderaadt 	{
57478c87a5cStholo 	    error (0, 0,
57578c87a5cStholo 		   "cannot check out revision %s of %s", vers_head, rcs);
5761e72d8d2Sderaadt 	    ret = 1;
5771e72d8d2Sderaadt 	    goto out;
5781e72d8d2Sderaadt 	}
5791e72d8d2Sderaadt 	if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_head,
5801e72d8d2Sderaadt 						    (char *) 0, 0)) != -1)
581066e68faStholo 	    /* I believe this timestamp only affects the dates in our diffs,
582066e68faStholo 	       and therefore should be on the server, not the client.  */
5831e72d8d2Sderaadt 	    (void) utime (tmpfile2, &t);
5841e72d8d2Sderaadt     }
58513571821Stholo 
58679822b2aStholo     switch (diff_exec (tmpfile1, tmpfile2, NULL, NULL, unidiff ? "-u" : "-c", tmpfile3))
5871e72d8d2Sderaadt     {
5881e72d8d2Sderaadt 	case -1:			/* fork/wait failure */
5891e72d8d2Sderaadt 	    error (1, errno, "fork for diff failed on %s", rcs);
5901e72d8d2Sderaadt 	    break;
5911e72d8d2Sderaadt 	case 0:				/* nothing to do */
5921e72d8d2Sderaadt 	    break;
5931e72d8d2Sderaadt 	case 1:
5941e72d8d2Sderaadt 	    /*
5951e72d8d2Sderaadt 	     * The two revisions are really different, so read the first two
5961e72d8d2Sderaadt 	     * lines of the diff output file, and munge them to include more
5971e72d8d2Sderaadt 	     * reasonable file names that "patch" will understand.
5981e72d8d2Sderaadt 	     */
5991e72d8d2Sderaadt 
6001e72d8d2Sderaadt 	    /* Output an "Index:" line for patch to use */
601b2b690deStholo 	    cvs_output ("Index: ", 0);
602b2b690deStholo 	    cvs_output (finfo->fullname, 0);
603b2b690deStholo 	    cvs_output ("\n", 1);
6041e72d8d2Sderaadt 
6051e72d8d2Sderaadt 	    fp = open_file (tmpfile3, "r");
606*f9bbbf45Sfgsch 	    if (get_line (&line1, &line1_chars_allocated, fp) < 0 ||
607*f9bbbf45Sfgsch 		get_line (&line2, &line2_chars_allocated, fp) < 0)
6081e72d8d2Sderaadt 	    {
60978c87a5cStholo 		if (feof (fp))
61078c87a5cStholo 		    error (0, 0, "\
61178c87a5cStholo failed to read diff file header %s for %s: end of file", tmpfile3, rcs);
61278c87a5cStholo 		else
61378c87a5cStholo 		    error (0, errno,
61478c87a5cStholo 			   "failed to read diff file header %s for %s",
6151e72d8d2Sderaadt 			   tmpfile3, rcs);
6161e72d8d2Sderaadt 		ret = 1;
61778c87a5cStholo 		if (fclose (fp) < 0)
61878c87a5cStholo 		    error (0, errno, "error closing %s", tmpfile3);
6191e72d8d2Sderaadt 		goto out;
6201e72d8d2Sderaadt 	    }
6211e72d8d2Sderaadt 	    if (!unidiff)
6221e72d8d2Sderaadt 	    {
6231e72d8d2Sderaadt 		if (strncmp (line1, "*** ", 4) != 0 ||
6241e72d8d2Sderaadt 		    strncmp (line2, "--- ", 4) != 0 ||
6251e72d8d2Sderaadt 		    (cp1 = strchr (line1, '\t')) == NULL ||
6261e72d8d2Sderaadt 		    (cp2 = strchr (line2, '\t')) == NULL)
6271e72d8d2Sderaadt 		{
6281e72d8d2Sderaadt 		    error (0, 0, "invalid diff header for %s", rcs);
6291e72d8d2Sderaadt 		    ret = 1;
63078c87a5cStholo 		    if (fclose (fp) < 0)
63178c87a5cStholo 			error (0, errno, "error closing %s", tmpfile3);
6321e72d8d2Sderaadt 		    goto out;
6331e72d8d2Sderaadt 		}
6341e72d8d2Sderaadt 	    }
6351e72d8d2Sderaadt 	    else
6361e72d8d2Sderaadt 	    {
6371e72d8d2Sderaadt 		if (strncmp (line1, "--- ", 4) != 0 ||
6381e72d8d2Sderaadt 		    strncmp (line2, "+++ ", 4) != 0 ||
6391e72d8d2Sderaadt 		    (cp1 = strchr (line1, '\t')) == NULL ||
6401e72d8d2Sderaadt 		    (cp2 = strchr  (line2, '\t')) == NULL)
6411e72d8d2Sderaadt 		{
6421e72d8d2Sderaadt 		    error (0, 0, "invalid unidiff header for %s", rcs);
6431e72d8d2Sderaadt 		    ret = 1;
64478c87a5cStholo 		    if (fclose (fp) < 0)
64578c87a5cStholo 			error (0, errno, "error closing %s", tmpfile3);
6461e72d8d2Sderaadt 		    goto out;
6471e72d8d2Sderaadt 		}
6481e72d8d2Sderaadt 	    }
64979822b2aStholo 	    assert (current_parsed_root != NULL);
65079822b2aStholo 	    assert (current_parsed_root->directory != NULL);
651c56b20e6Stholo 	    {
65279822b2aStholo 		strippath = xmalloc (strlen (current_parsed_root->directory) + 2);
65379822b2aStholo 		(void) sprintf (strippath, "%s/", current_parsed_root->directory);
654c56b20e6Stholo 	    }
65579822b2aStholo 	    /*else
65679822b2aStholo 		strippath = xstrdup (REPOS_STRIP); */
6571e72d8d2Sderaadt 	    if (strncmp (rcs, strippath, strlen (strippath)) == 0)
6581e72d8d2Sderaadt 		rcs += strlen (strippath);
659c56b20e6Stholo 	    free (strippath);
6601e72d8d2Sderaadt 	    if (vers_tag != NULL)
6611e72d8d2Sderaadt 	    {
662c56b20e6Stholo 		file1 = xmalloc (strlen (finfo->fullname)
663c56b20e6Stholo 				 + strlen (vers_tag)
664c56b20e6Stholo 				 + 10);
6659d9224ffStholo 		(void) sprintf (file1, "%s:%s", finfo->fullname, vers_tag);
6661e72d8d2Sderaadt 	    }
6671e72d8d2Sderaadt 	    else
6681e72d8d2Sderaadt 	    {
669c56b20e6Stholo 		file1 = xstrdup (DEVNULL);
6701e72d8d2Sderaadt 	    }
671c56b20e6Stholo 	    file2 = xmalloc (strlen (finfo->fullname)
672c56b20e6Stholo 			     + (vers_head != NULL ? strlen (vers_head) : 10)
673c56b20e6Stholo 			     + 10);
6749d9224ffStholo 	    (void) sprintf (file2, "%s:%s", finfo->fullname,
6751e72d8d2Sderaadt 			    vers_head ? vers_head : "removed");
6769d9224ffStholo 
677b2b690deStholo 	    /* Note that the string "diff" is specified by POSIX (for -c)
678b2b690deStholo 	       and is part of the diff output format, not the name of a
679b2b690deStholo 	       program.  */
6801e72d8d2Sderaadt 	    if (unidiff)
6811e72d8d2Sderaadt 	    {
682b2b690deStholo 		cvs_output ("diff -u ", 0);
683b2b690deStholo 		cvs_output (file1, 0);
684b2b690deStholo 		cvs_output (" ", 1);
685b2b690deStholo 		cvs_output (file2, 0);
686b2b690deStholo 		cvs_output ("\n", 1);
687b2b690deStholo 
688b2b690deStholo 		cvs_output ("--- ", 0);
689b2b690deStholo 		cvs_output (file1, 0);
690b2b690deStholo 		cvs_output (cp1, 0);
691b2b690deStholo 		cvs_output ("+++ ", 0);
6921e72d8d2Sderaadt 	    }
6931e72d8d2Sderaadt 	    else
6941e72d8d2Sderaadt 	    {
695b2b690deStholo 		cvs_output ("diff -c ", 0);
696b2b690deStholo 		cvs_output (file1, 0);
697b2b690deStholo 		cvs_output (" ", 1);
698b2b690deStholo 		cvs_output (file2, 0);
699b2b690deStholo 		cvs_output ("\n", 1);
700b2b690deStholo 
701b2b690deStholo 		cvs_output ("*** ", 0);
702b2b690deStholo 		cvs_output (file1, 0);
703b2b690deStholo 		cvs_output (cp1, 0);
704b2b690deStholo 		cvs_output ("--- ", 0);
7051e72d8d2Sderaadt 	    }
7061e72d8d2Sderaadt 
707b2b690deStholo 	    cvs_output (finfo->fullname, 0);
708b2b690deStholo 	    cvs_output (cp2, 0);
709b2b690deStholo 
71013571821Stholo 	    /* spew the rest of the diff out */
711b2b690deStholo 	    while ((line_length
712*f9bbbf45Sfgsch 		    = get_line (&line1, &line1_chars_allocated, fp))
713b2b690deStholo 		   >= 0)
714b2b690deStholo 		cvs_output (line1, 0);
715b2b690deStholo 	    if (line_length < 0 && !feof (fp))
716b2b690deStholo 		error (0, errno, "cannot read %s", tmpfile3);
717b2b690deStholo 
718b2b690deStholo 	    if (fclose (fp) < 0)
719b2b690deStholo 		error (0, errno, "cannot close %s", tmpfile3);
720c56b20e6Stholo 	    free (file1);
721c56b20e6Stholo 	    free (file2);
7221e72d8d2Sderaadt 	    break;
7231e72d8d2Sderaadt 	default:
7249d9224ffStholo 	    error (0, 0, "diff failed for %s", finfo->fullname);
7251e72d8d2Sderaadt     }
7261e72d8d2Sderaadt   out:
72713571821Stholo     if (line1)
72813571821Stholo         free (line1);
72913571821Stholo     if (line2)
73013571821Stholo         free (line2);
731b2b690deStholo     if (CVS_UNLINK (tmpfile1) < 0)
732b2b690deStholo 	error (0, errno, "cannot unlink %s", tmpfile1);
733b2b690deStholo     if (CVS_UNLINK (tmpfile2) < 0)
734b2b690deStholo 	error (0, errno, "cannot unlink %s", tmpfile2);
735b2b690deStholo     if (CVS_UNLINK (tmpfile3) < 0)
736b2b690deStholo 	error (0, errno, "cannot unlink %s", tmpfile3);
7377e4d5a85Stholo     free (tmpfile1);
7387e4d5a85Stholo     free (tmpfile2);
7397e4d5a85Stholo     free (tmpfile3);
7407e4d5a85Stholo     tmpfile1 = tmpfile2 = tmpfile3 = NULL;
741c56b20e6Stholo 
742c56b20e6Stholo  out2:
743bde78045Stholo     if (vers_tag != NULL)
744bde78045Stholo 	free (vers_tag);
745bde78045Stholo     if (vers_head != NULL)
746bde78045Stholo 	free (vers_head);
747420bf761Sotto     if (rcs_orig)
748420bf761Sotto 	free (rcs_orig);
7491e72d8d2Sderaadt     return (ret);
7501e72d8d2Sderaadt }
7511e72d8d2Sderaadt 
7521e72d8d2Sderaadt /*
7531e72d8d2Sderaadt  * Print a warm fuzzy message
7541e72d8d2Sderaadt  */
7551e72d8d2Sderaadt /* ARGSUSED */
7561e72d8d2Sderaadt static Dtype
patch_dirproc(callerdat,dir,repos,update_dir,entries)7577e4d5a85Stholo patch_dirproc (callerdat, dir, repos, update_dir, entries)
7587e4d5a85Stholo     void *callerdat;
7591e72d8d2Sderaadt     char *dir;
7601e72d8d2Sderaadt     char *repos;
7611e72d8d2Sderaadt     char *update_dir;
7627e4d5a85Stholo     List *entries;
7631e72d8d2Sderaadt {
7641e72d8d2Sderaadt     if (!quiet)
7651e72d8d2Sderaadt 	error (0, 0, "Diffing %s", update_dir);
7661e72d8d2Sderaadt     return (R_PROCESS);
7671e72d8d2Sderaadt }
7681e72d8d2Sderaadt 
7691e72d8d2Sderaadt /*
7701e72d8d2Sderaadt  * Clean up temporary files
7711e72d8d2Sderaadt  */
7721e72d8d2Sderaadt static RETSIGTYPE
patch_cleanup()7731e72d8d2Sderaadt patch_cleanup ()
7741e72d8d2Sderaadt {
7759fe7c2c3Stholo     /* Note that the checks for existence_error are because we are
7769fe7c2c3Stholo        called from a signal handler, without SIG_begincrsect, so
7779fe7c2c3Stholo        we don't know whether the files got created.  */
7789fe7c2c3Stholo 
7797e4d5a85Stholo     if (tmpfile1 != NULL)
7807e4d5a85Stholo     {
7819fe7c2c3Stholo 	if (unlink_file (tmpfile1) < 0
7829fe7c2c3Stholo 	    && !existence_error (errno))
7839fe7c2c3Stholo 	    error (0, errno, "cannot remove %s", tmpfile1);
7847e4d5a85Stholo 	free (tmpfile1);
7857e4d5a85Stholo     }
7867e4d5a85Stholo     if (tmpfile2 != NULL)
7877e4d5a85Stholo     {
7889fe7c2c3Stholo 	if (unlink_file (tmpfile2) < 0
7899fe7c2c3Stholo 	    && !existence_error (errno))
7909fe7c2c3Stholo 	    error (0, errno, "cannot remove %s", tmpfile2);
7917e4d5a85Stholo 	free (tmpfile2);
7927e4d5a85Stholo     }
7937e4d5a85Stholo     if (tmpfile3 != NULL)
7947e4d5a85Stholo     {
7959fe7c2c3Stholo 	if (unlink_file (tmpfile3) < 0
7969fe7c2c3Stholo 	    && !existence_error (errno))
7979fe7c2c3Stholo 	    error (0, errno, "cannot remove %s", tmpfile3);
7987e4d5a85Stholo 	free (tmpfile3);
7997e4d5a85Stholo     }
8007e4d5a85Stholo     tmpfile1 = tmpfile2 = tmpfile3 = NULL;
8011e72d8d2Sderaadt }
802