/* * Source file: * diffs.c * * Implements the highlighting of differences between old and new versions * of files. */ #include "config.h" #ifndef DIFF_CMD #include "trueprint.h" #include "diffs.h" /* If there is no diff command just provide stubs */ void setup_diffs(void) { return; } void init_diffs(char newfile[]) { return; } void end_diffs(void) { return; } boolean getdelline(long current_line, char *input_line, char_status input_status[]) { return 0; } boolean line_inserted(long current_line) { return 0; } #else #include #include /* Ultrix needs these sys header files this way around */ #include #include #include #include #include #include "trueprint.h" #include "utils.h" #include "main.h" #include "output.h" #include "openpipe.h" #include "options.h" #include "debug.h" #include "diffs.h" static char *diffs_string; /* * Private part */ static unsigned short read_values(FILE *,long *value1_ptr, long *value2_ptr); static boolean read_long(FILE *, long *); static void parse_line(void); static stream_status get_diff_char(char *input_char, char_status *status); static FILE *diffs_stream; static boolean diffs_on; static long diff_start_line; static long diff_end_line; static long lines_deleted; static long lines_added; /* * Function: * setup_diffs */ void setup_diffs(void) { diffs_string = NULL; diffs_stream = NULL; string_option("O", "diff", NULL, &diffs_string, NULL, NULL, OPT_MISC, "if is a file then print diffs between and input file\n" " otherwise use as a prefix and print diffs"); } /* * Function: * init_diffs * * Initialises the diffs system: * checks to see if any old version was provided and returns if not; * calls end_diffs() if necessary; * starts up the diff process; */ void init_diffs(char newfile[]) { char command[COMMAND_LEN]; char *diff_cmd; struct stat dummy; /* Check to see if any old versions given */ if (diffs_string == NULL) { /* turn off diffs and return */ diffs_on = FALSE; return; } diffs_on = TRUE; if ((diff_cmd = getenv("TP_DIFF_CMD")) == NULL) { diff_cmd = DIFF_CMD; } /* * diffs_stream is only set if still reading from * diff process, so need to finish the old diffs. */ if (diffs_stream != NULL) end_diffs(); /* * Check to see if diffs_string is a valid prefix or * a filename... */ sprintf(command,"%s%s",diffs_string,newfile); if (stat(command,&dummy) == 0) { sprintf(command,"%s %s%s %s", diff_cmd,diffs_string,newfile,newfile); } else if (stat(diffs_string,&dummy) == 0) { sprintf(command, "%s %s %s", diff_cmd, diffs_string, newfile); } dm('d',3,"diffs command: %s\n",command); if ((diffs_stream = fopenpipe(command, "r")) == NULL) abort; parse_line(); } /* * Function: * read_values * * scan a line reading one integer, and reading a second if the next * character is a "," */ unsigned short read_values(FILE *stream, long *value1_ptr, long *value2_ptr) { int next_char; unsigned short retval; *value1_ptr = 0; *value2_ptr = 0; /* read first value - if none then return*/ if (!read_long(stream, value1_ptr)) return 0; /* skip spaces and check for a comma */ while ((next_char = getc(stream)) == ' ') ; if (next_char == ',') { /* if next char was a comma read second value */ if (!read_long(stream, value2_ptr)) { fprintf(stderr, gettext(CMD_NAME ": internal error reading diffs\n")); exit(2); } /* and skip spaces again */ while ((next_char = getc(stream)) == ' ') ; retval = 2; } else { retval = 1; *value2_ptr = *value1_ptr; } if (next_char != EOF) { /* replace last char in buffer for calling function */ ungetc((int)next_char,stream); } return(retval); } /* * Function: * read_long * * Reads a long from a stream */ boolean read_long(FILE *stream, long *value) { boolean retval = FALSE; int next_char; *value = 0; while (TRUE) { if ((next_char = getc(stream)) == EOF) break; if (!isdigit(next_char)) break; retval = TRUE; *value = (*value * 10) + (next_char - '0'); } if (next_char != EOF) { ungetc(next_char, stream); } return retval; } /* * Function: * parse_line * * Reads a line from the diff stream, reading start and end line numbers * for both the old and new files and the change type (d, a or c). */ void parse_line(void) { long delete_start; long delete_end; int diff_type; /* * Read the first two values - if there aren't any then we've * reached the end of the file. These values refer to the * first file so we're only interested in them for deleting lines. */ if (read_values(diffs_stream, &delete_start, &delete_end) == 0) { end_diffs(); diffs_on = FALSE; return; } /* * Calculate the number of lines deleted. * If delete start and delete end are the same then 1 line has been * deleted... i.e. one more than the difference. */ lines_deleted = delete_end - delete_start + 1; diff_type = getc(diffs_stream); /* a=add, d=del, c=change */ switch (diff_type) { case 'a': case 'd': case 'c': break; default: fprintf(stderr, gettext(CMD_NAME ": warning, bad diffs stream format char %c!\n"), diff_type); end_diffs(); diffs_on = FALSE; return; } /* Same as before, only for new version of file */ if (read_values(diffs_stream, &diff_start_line, &diff_end_line) == 0) { fprintf(stderr, gettext(CMD_NAME ": warning, bad diffs stream format!\n")); end_diffs(); diffs_on = FALSE; return; } lines_added = diff_end_line - diff_start_line + 1; if (diff_type == 'a') lines_deleted = 0; if (diff_type == 'd') { lines_added = 0; /* * deleted lines are actually *after* the specified line, * so we need to pretend they're one line later... */ diff_start_line += 1; diff_end_line += 1; } /* Skip to the end of the line */ while (getc(diffs_stream) != '\n') ; } /* * Function: * end_diffs * * Finish off the diffs system: * - close the diffs stream (i.e. kill off the diff process); */ void end_diffs(void) { if (!diffs_on) return; dm('d',3,"diffs: closing diffs\n"); fclosepipe(diffs_stream); diffs_stream = NULL; } /* * Function: * get_diff_char * * Simple function to be passed to getnextline() (which expands tabs, etc) * in getdelline(), so we don't need to duplicate the functionality of * getnextline(). */ stream_status get_diff_char(char *input_char, char_status *status) { *status = CHAR_NORMAL; *input_char = (char)getc(diffs_stream); return(STREAM_OK); } /* * Function: * getdelline * * Returns: * TRUE if a(nother) deleted line occurs after current line, returns * line in parameters. * FALSE if no deleted line between current line and next line. */ boolean getdelline(long current_line, char *input_line, char_status input_status[]) { boolean blank_line; /* * Check whether there is a deleted line waiting... */ if ((current_line != diff_start_line) || !diffs_on || (lines_deleted == 0)) return(FALSE); /* * First skip the first two characters of the line - * they should be "< " */ if ((getc(diffs_stream)!='<') || (getc(diffs_stream)!=' ')) { fprintf(stderr, gettext(CMD_NAME ": diffs stream in unexpected format\n")); exit(2); } /* Read in the next line from diff stream */ getnextline(get_diff_char,&blank_line,input_line,input_status); lines_deleted -= 1; if (lines_deleted == 0) { /* * If there are lines added then this is a "change" and * the added lines will be dealt with by line_inserted(). * Otherwise we need to find the next difference with * parse_line(). */ if (lines_added == 0) parse_line(); else /* else skip over line of ---- */ while (getc(diffs_stream) != '\n') ; } return(TRUE); } /* * Function: * line_inserted * * Returns: * INSERT if line_number has been inserted * NORMAL otherwise */ boolean line_inserted(long current_line) { boolean retval = FALSE; if (!diffs_on) return(FALSE); if ((current_line >= diff_start_line) && (current_line <= diff_end_line)) { /* skip over line */ while (getc(diffs_stream) != '\n') ; retval = TRUE; } /* If reached the end of the insertion... */ if (current_line >= diff_end_line) parse_line(); return(retval); } #endif