This is diff.c in view mode; [Download] [Up]
/* * Copyright (c) 1992, Brian Berliner and Jeff Polk * Copyright (c) 1989-1992, Brian Berliner * * You may distribute under the terms of the GNU General Public License as * specified in the README file that comes with the CVS 1.4 kit. * * Difference * * Run diff against versions in the repository. Options that are specified are * passed on directly to "rcsdiff". * * Without any file arguments, runs diff against all the currently modified * files. */ #include "cvs.h" #ifndef lint static const char rcsid[] = "$CVSid: @(#)diff.c 1.61 94/10/22 $"; USE(rcsid) #endif static Dtype diff_dirproc PROTO((char *dir, char *pos_repos, char *update_dir)); static int diff_filesdoneproc PROTO((int err, char *repos, char *update_dir)); static int diff_dirleaveproc PROTO((char *dir, int err, char *update_dir)); static int diff_file_nodiff PROTO((char *file, char *repository, List *entries, List *srcfiles, Vers_TS *vers)); static int diff_fileproc PROTO((char *file, char *update_dir, char *repository, List * entries, List * srcfiles)); static void diff_mark_errors PROTO((int err)); static char *diff_rev1, *diff_rev2; static char *diff_date1, *diff_date2; static char *use_rev1, *use_rev2; #ifdef SERVER_SUPPORT /* Revision of the user file, if it is unchanged from something in the repository and we want to use that fact. */ static char *user_file_rev; #endif static char *options; static char opts[PATH_MAX]; static int diff_errors; static int empty_files = 0; static const char *const diff_usage[] = { "Usage: %s %s [-lN] [rcsdiff-options]\n", #ifdef CVS_DIFFDATE " [[-r rev1 | -D date1] [-r rev2 | -D date2]] [files...] \n", #else " [-r rev1 [-r rev2]] [files...] \n", #endif "\t-l\tLocal directory only, not recursive\n", "\t-D d1\tDiff revision for date against working file.\n", "\t-D d2\tDiff rev1/date1 against date2.\n", "\t-N\tinclude diffs for added and removed files.\n", "\t-r rev1\tDiff revision for rev1 against working file.\n", "\t-r rev2\tDiff rev1/date1 against rev2.\n", NULL }; int diff (argc, argv) int argc; char **argv; { char tmp[50]; int c, err = 0; int local = 0; int which; if (argc == -1) usage (diff_usage); /* * Note that we catch all the valid arguments here, so that we can * intercept the -r arguments for doing revision diffs; and -l/-R for a * non-recursive/recursive diff. */ #ifdef SERVER_SUPPORT /* Need to be able to do this command more than once (according to the protocol spec, even if the current client doesn't use it). */ opts[0] = '\0'; #endif optind = 1; while ((c = getopt (argc, argv, "abcdefhilnpqtuw0123456789BHNQRTC:D:F:I:L:V:k:r:")) != -1) { switch (c) { case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'h': case 'i': case 'n': case 'p': case 't': case 'u': case 'w': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'B': case 'H': case 'T': case 'Q': (void) sprintf (tmp, " -%c", (char) c); (void) strcat (opts, tmp); if (c == 'Q') { quiet = 1; really_quiet = 1; c = 'q'; } break; case 'C': case 'F': case 'I': case 'L': case 'V': #ifndef CVS_DIFFDATE case 'D': #endif (void) sprintf (tmp, " -%c%s", (char) c, optarg); (void) strcat (opts, tmp); break; case 'R': local = 0; break; case 'l': local = 1; break; case 'q': quiet = 1; break; case 'k': if (options) free (options); options = RCS_check_kflag (optarg); break; case 'r': if (diff_rev2 != NULL || diff_date2 != NULL) error (1, 0, "no more than two revisions/dates can be specified"); if (diff_rev1 != NULL || diff_date1 != NULL) diff_rev2 = optarg; else diff_rev1 = optarg; break; #ifdef CVS_DIFFDATE case 'D': if (diff_rev2 != NULL || diff_date2 != NULL) error (1, 0, "no more than two revisions/dates can be specified"); if (diff_rev1 != NULL || diff_date1 != NULL) diff_date2 = Make_Date (optarg); else diff_date1 = Make_Date (optarg); break; #endif case 'N': empty_files = 1; break; case '?': default: usage (diff_usage); break; } } argc -= optind; argv += optind; /* make sure options is non-null */ if (!options) options = xstrdup (""); #ifdef CLIENT_SUPPORT if (client_active) { /* We're the client side. Fire up the remote server. */ start_server (); ign_setup (); if (local) send_arg("-l"); if (empty_files) send_arg("-N"); send_option_string (opts); if (diff_rev1) option_with_arg ("-r", diff_rev1); if (diff_date1) client_senddate (diff_date1); if (diff_rev2) option_with_arg ("-r", diff_rev2); if (diff_date2) client_senddate (diff_date2); #if 0 /* FIXME: We shouldn't have to send current files to diff two revs, but it doesn't work yet and I haven't debugged it. So send the files -- it's slower but it works. gnu@cygnus.com Apr94 */ /* Send the current files unless diffing two revs from the archive */ if (diff_rev2 == NULL && diff_date2 == NULL) send_files (argc, argv, local); else send_file_names (argc, argv); #else send_files (argc, argv, local, 0); #endif if (fprintf (to_server, "diff\n") < 0) error (1, errno, "writing to server"); err = get_responses_and_close (); free (options); return (err); } #endif which = W_LOCAL; if (diff_rev2 != NULL || diff_date2 != NULL) which |= W_REPOS | W_ATTIC; wrap_setup (); /* start the recursion processor */ err = start_recursion (diff_fileproc, diff_filesdoneproc, diff_dirproc, diff_dirleaveproc, argc, argv, local, which, 0, 1, (char *) NULL, 1, 0); /* clean up */ free (options); return (err); } /* * Do a file diff */ /* ARGSUSED */ static int diff_fileproc (file, update_dir, repository, entries, srcfiles) char *file; char *update_dir; char *repository; List *entries; List *srcfiles; { int status, err = 2; /* 2 == trouble, like rcsdiff */ Vers_TS *vers; enum { DIFF_ERROR, DIFF_ADDED, DIFF_REMOVED, DIFF_NEITHER } empty_file = DIFF_NEITHER; char tmp[L_tmpnam+1]; char *tocvsPath; char fname[PATH_MAX]; #ifdef SERVER_SUPPORT user_file_rev = 0; #endif vers = Version_TS (repository, (char *) NULL, (char *) NULL, (char *) NULL, file, 1, 0, entries, srcfiles); if (diff_rev2 != NULL || diff_date2 != NULL) { /* Skip all the following checks regarding the user file; we're not using it. */ } else if (vers->vn_user == NULL) { error (0, 0, "I know nothing about %s", file); freevers_ts (&vers); diff_mark_errors (err); return (err); } else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0') { if (empty_files) empty_file = DIFF_ADDED; else { error (0, 0, "%s is a new entry, no comparison available", file); freevers_ts (&vers); diff_mark_errors (err); return (err); } } else if (vers->vn_user[0] == '-') { if (empty_files) empty_file = DIFF_REMOVED; else { error (0, 0, "%s was removed, no comparison available", file); freevers_ts (&vers); diff_mark_errors (err); return (err); } } else { if (vers->vn_rcs == NULL && vers->srcfile == NULL) { error (0, 0, "cannot find revision control file for %s", file); freevers_ts (&vers); diff_mark_errors (err); return (err); } else { if (vers->ts_user == NULL) { error (0, 0, "cannot find %s", file); freevers_ts (&vers); diff_mark_errors (err); return (err); } #ifdef SERVER_SUPPORT else if (!strcmp (vers->ts_user, vers->ts_rcs)) { /* The user file matches some revision in the repository Diff against the repository (for remote CVS, we might not have a copy of the user file around). */ user_file_rev = vers->vn_user; } #endif } } if (empty_file == DIFF_NEITHER && diff_file_nodiff (file, repository, entries, srcfiles, vers)) { freevers_ts (&vers); return (0); } #ifdef DEATH_SUPPORT /* FIXME: Check whether use_rev1 and use_rev2 are dead and deal accordingly. */ #endif /* Output an "Index:" line for patch to use */ (void) fflush (stdout); if (update_dir[0]) (void) printf ("Index: %s/%s\n", update_dir, file); else (void) printf ("Index: %s\n", file); (void) fflush (stdout); tocvsPath = wrap_tocvs_process_file(file); if (tocvsPath) { /* Backup the current version of the file to CVS/,,filename */ sprintf(fname,"%s/%s%s",CVSADM, CVSPREFIX, file); if (unlink_file_dir (fname) < 0) if (errno != ENOENT) error (1, errno, "cannot remove %s", file); rename_file (file, fname); /* Copy the wrapped file to the current directory then go to work */ copy_file (tocvsPath, file); } if (empty_file == DIFF_ADDED || empty_file == DIFF_REMOVED) { (void) printf ("===================================================================\nRCS file: %s\n", file); (void) printf ("diff -N %s\n", file); if (empty_file == DIFF_ADDED) { run_setup ("%s %s %s %s", DIFF, opts, DEVNULL, file); } else { /* * FIXME: Should be setting use_rev1 using the logic in * diff_file_nodiff, and using that revision. This code * is broken for "cvs diff -N -r foo". */ run_setup ("%s%s -p -q %s -r%s", Rcsbin, RCS_CO, *options ? options : vers->options, vers->vn_rcs); run_arg (vers->srcfile->path); if (run_exec (RUN_TTY, tmpnam (tmp), RUN_TTY, RUN_REALLY) == -1) { (void) unlink (tmp); error (1, errno, "fork failed during checkout of %s", vers->srcfile->path); } run_setup ("%s %s %s %s", DIFF, opts, tmp, DEVNULL); } } else { if (use_rev2) { run_setup ("%s%s %s %s -r%s -r%s", Rcsbin, RCS_DIFF, opts, *options ? options : vers->options, use_rev1, use_rev2); } else { run_setup ("%s%s %s %s -r%s", Rcsbin, RCS_DIFF, opts, *options ? options : vers->options, use_rev1); } run_arg (vers->srcfile->path); } switch ((status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_REALLY|RUN_COMBINED))) { case -1: /* fork failed */ error (1, errno, "fork failed during rcsdiff of %s", vers->srcfile->path); case 0: /* everything ok */ err = 0; break; default: /* other error */ err = status; break; } if (tocvsPath) { if (unlink_file_dir (file) < 0) if (errno != ENOENT) error (1, errno, "cannot remove %s", file); rename_file (fname,file); if (unlink_file (tocvsPath) < 0) error (1, errno, "cannot remove %s", file); } if (empty_file == DIFF_REMOVED) (void) unlink (tmp); (void) fflush (stdout); freevers_ts (&vers); diff_mark_errors (err); return (err); } /* * Remember the exit status for each file. */ static void diff_mark_errors (err) int err; { if (err > diff_errors) diff_errors = err; } /* * Print a warm fuzzy message when we enter a dir */ /* ARGSUSED */ static Dtype diff_dirproc (dir, pos_repos, update_dir) char *dir; char *pos_repos; char *update_dir; { /* XXX - check for dirs we don't want to process??? */ if (!quiet) error (0, 0, "Diffing %s", update_dir); return (R_PROCESS); } /* * Concoct the proper exit status - done with files */ /* ARGSUSED */ static int diff_filesdoneproc (err, repos, update_dir) int err; char *repos; char *update_dir; { return (diff_errors); } /* * Concoct the proper exit status - leaving directories */ /* ARGSUSED */ static int diff_dirleaveproc (dir, err, update_dir) char *dir; int err; char *update_dir; { return (diff_errors); } /* * verify that a file is different 0=same 1=different */ static int diff_file_nodiff (file, repository, entries, srcfiles, vers) char *file; char *repository; List *entries; List *srcfiles; Vers_TS *vers; { Vers_TS *xvers; char tmp[L_tmpnam+1]; /* free up any old use_rev* variables and reset 'em */ if (use_rev1) free (use_rev1); if (use_rev2) free (use_rev2); use_rev1 = use_rev2 = (char *) NULL; if (diff_rev1 || diff_date1) { /* special handling for TAG_HEAD */ if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0) use_rev1 = xstrdup (vers->vn_rcs); else { xvers = Version_TS (repository, (char *) NULL, diff_rev1, diff_date1, file, 1, 0, entries, srcfiles); if (xvers->vn_rcs == NULL) { if (diff_rev1) error (0, 0, "tag %s is not in file %s", diff_rev1, file); else error (0, 0, "no revision for date %s in file %s", diff_date1, file); return (1); } use_rev1 = xstrdup (xvers->vn_rcs); freevers_ts (&xvers); } } if (diff_rev2 || diff_date2) { /* special handling for TAG_HEAD */ if (diff_rev2 && strcmp (diff_rev2, TAG_HEAD) == 0) use_rev2 = xstrdup (vers->vn_rcs); else { xvers = Version_TS (repository, (char *) NULL, diff_rev2, diff_date2, file, 1, 0, entries, srcfiles); if (xvers->vn_rcs == NULL) { if (diff_rev1) error (0, 0, "tag %s is not in file %s", diff_rev2, file); else error (0, 0, "no revision for date %s in file %s", diff_date2, file); return (1); } use_rev2 = xstrdup (xvers->vn_rcs); freevers_ts (&xvers); } /* now, see if we really need to do the diff */ if (use_rev1 && use_rev2) { return (strcmp (use_rev1, use_rev2) == 0); } else { error(0, 0, "No HEAD revision for file %s", file); return (1); } } #ifdef SERVER_SUPPORT if (user_file_rev) { /* drop user_file_rev into first unused use_rev */ if (!use_rev1) use_rev1 = xstrdup (user_file_rev); else if (!use_rev2) use_rev2 = xstrdup (user_file_rev); /* and if not, it wasn't needed anyhow */ user_file_rev = 0; } /* now, see if we really need to do the diff */ if (use_rev1 && use_rev2) { return (strcmp (use_rev1, use_rev2) == 0); } #endif /* SERVER_SUPPORT */ if (use_rev1 == NULL || strcmp (use_rev1, vers->vn_user) == 0) { if (strcmp (vers->ts_rcs, vers->ts_user) == 0 && (!(*options) || strcmp (options, vers->options) == 0)) { return (1); } if (use_rev1 == NULL) use_rev1 = xstrdup (vers->vn_user); } /* * with 0 or 1 -r option specified, run a quick diff to see if we * should bother with it at all. */ run_setup ("%s%s -p -q %s -r%s", Rcsbin, RCS_CO, *options ? options : vers->options, use_rev1); run_arg (vers->srcfile->path); switch (run_exec (RUN_TTY, tmpnam (tmp), RUN_TTY, RUN_REALLY)) { case 0: /* everything ok */ if (xcmp (file, tmp) == 0) { (void) unlink (tmp); return (1); } break; case -1: /* fork failed */ (void) unlink (tmp); error (1, errno, "fork failed during checkout of %s", vers->srcfile->path); default: break; } (void) unlink (tmp); return (0); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.