/*
 * The merged diff format added by Chip Rosenthal <chip@chinacat.unicom.com>
 */
 
#include "diff.h"

#define TABSTOP	8	/* must be a power of two (e.g. 4, 8, etc.) */

#define Fput3(FP,S1,S2,S3) \
	( fputs((S1),(FP)), fputs((S2),(FP)), fputs((S3),(FP)), putc('\n',(FP)) )

#define Strncpy(OUT,IN,LEN) \
	( (OUT)[(LEN)] = '\0', strncpy((OUT),(IN),(LEN)) )

static int field_width;
static char *field0_buf, *field1_buf, *tmp_buf;

static void print_merged_hunk();
static void expand_field();

extern struct change *find_change();


void print_merged_script(script)
struct change *script;
{

	/*
	 * Figure out how big a field to print the file differences in.
	 */
	field_width = ( line_width - (sizeof("-+-")-1) ) / 2;
	if ( field_width < 1 ) {
		fputs("diff: bad field width specified\n",stderr);
		exit(1);
	}

	/*
	 * Create the space for formatting.
	 */
	field0_buf = (char*) xmalloc((unsigned)field_width+1);
	field1_buf = (char*) xmalloc((unsigned)field_width+1);
	tmp_buf = (char*) xmalloc((unsigned)field_width+1);

	/*
	 * Print the header.
	 */
	expand_field(tmp_buf, (char*)NULL, field_width, '-'),
	Fput3(outfile, tmp_buf, "-+-", tmp_buf);

	(void) Strncpy(tmp_buf, files[0].name, field_width);
	expand_field(field0_buf, tmp_buf, field_width, ' '),
	(void) Strncpy(tmp_buf, files[1].name, field_width);
	expand_field(field1_buf, tmp_buf, field_width, ' '),
	Fput3(outfile, field0_buf, " | ", field1_buf);

	/*
	 * Print the hunks.
	 */
	print_script(script, find_change, print_merged_hunk);

	/*
	 * Print the trailer.
	 */
	expand_field(tmp_buf, (char*)NULL, field_width, '-'),
	Fput3(outfile, tmp_buf, "-+-", tmp_buf);

	free(field0_buf);
	free(field1_buf);
	free(tmp_buf);
}


static void print_merged_hunk(hunk)
struct change *hunk;
{
	int first0, last0, first1, last1, deletes, inserts, tr_first, tr_last, len;
	char *action;
	register int i0, i1;

	/*
	 * Determine range of line numbers involved in each file.
	 */
	analyze_hunk(hunk, &first0, &last0, &first1, &last1, &deletes, &inserts);
	if ( deletes > 0 && inserts > 0 )
		action = "change";
	else if ( deletes > 0 )
		action = "delete";
	else if ( inserts > 0 )
		action = "insert";
	else
		return;

	/*
	 * Print the header for this hunk.
	 */
	translate_range(&files[0], first0, last0, &tr_first, &tr_last);
	if ( tr_last > tr_first )
		(void) sprintf(tmp_buf, "--- %s at %d-%d ", action, tr_first, tr_last);
	else
		(void) sprintf(tmp_buf, "--- %s at %d ", action, tr_last);
	expand_field(field0_buf, tmp_buf, field_width, '-');

	translate_range(&files[1], first1, last1, &tr_first, &tr_last);
	if ( tr_last > tr_first )
		(void) sprintf(tmp_buf, " %d-%d ", tr_first, tr_last);
	else
		(void) sprintf(tmp_buf, " %d ", tr_last);
	expand_field(field1_buf, tmp_buf, field_width, '-');

	Fput3(outfile, field0_buf, "-+-", field1_buf);

	/*
	 * Format and print each line in this hunk.
	 */
	for ( i0 = first0, i1 = first1 ; i0 <= last0 || i1 <= last1 ; ++i0, ++i1 ) {

		if ( i0 <= last0 ) {
			len = ( files[0].linbuf[i0].length < field_width ?
				files[0].linbuf[i0].length : field_width );
			Strncpy(tmp_buf, files[0].linbuf[i0].text, len);
			expand_field(field0_buf, tmp_buf, field_width, ' ');
		} else {
			expand_field(field0_buf, (char*)NULL, field_width, ' ');
		}

		if ( i1 <= last1 ) {
			len = ( files[1].linbuf[i1].length < field_width ?
				files[1].linbuf[i1].length : field_width );
			Strncpy(tmp_buf, files[1].linbuf[i1].text, len);
			expand_field(field1_buf, tmp_buf, field_width, ' ');
		} else {
			expand_field(field1_buf, (char*)NULL, field_width, ' ');
		}

		Fput3(outfile, field0_buf, " | ", field1_buf);

	}

}


/*
 * The expand_field() routine performs two functions.  First, it goes
 * through "inbuf" and reformats the line doing stuff like expanding
 * tabs and stripping out newlines.  Second, it expands the text to
 * "fieldlen" characters, using the "padchar" to fill to this length.
 * If the "inbuf" is NULL, we just generate a string of the "padchar".
 */
static void expand_field(outbuf,inbuf,fieldlen,padchar)
char *outbuf;
char *inbuf;
int fieldlen;
int padchar;
{
	int currcol;

	/*
	 * If no string was passed to us, make one of the full field length.
	 */
	if ( inbuf == NULL ) {
		while ( --fieldlen >= 0 )
			*outbuf++ = padchar;
		*outbuf = '\0';
		return;
	}

	/*
	 * Go through the string doing translations (tab expansion, etc.).
	 */
	currcol = 1;
	while ( *inbuf != '\0' && fieldlen > 0 ) {
		if ( *inbuf == '\t' ) {
			do {
				*outbuf++ = ' ';
			} while ( --fieldlen >= 0 && !( currcol++ & TABSTOP ) ) ;
			++inbuf;
		} else if ( isascii(*inbuf) && isprint(*inbuf) ) {
			*outbuf++ = *inbuf++;
			++currcol;
			--fieldlen;
		} else {
			++inbuf; /* elide this character */
		}
	}

	/*
	 * Finally, pad it out to the required length.
	 */
	while ( --fieldlen >= 0 )
		*outbuf++ = padchar;
	*outbuf = '\0';
}
