/*
	TAB.C

	Simple program to add or remove tabs from
	a text file while preserving its spacing.

	Usage:-

	    $ tab [-iN] [-oN] [-d] [-s] input_file output_file
		-iN  sets the input tab size (default is 8)
		-oN  sets the output tab size (default is input -
		     a value of 1 or zero replaces all tabs with spaces)
		-d   process spacing inside double quotes
		-s   process spacing inside single quotes

	For example:-

	    $ tab -o tab.c x.c

	will replace tab characters in the source with appropriate
	numbers of space characters.
*/

#include <stdio.h>
#include <stdlib.h>


int tab(char *in_filename,char *out_filename,int in_size,int out_size,
	int double_q,int single_q)
{
    FILE *in_file,*out_file;
    if (in_size < 1) {
	printf("Input tab size too small (%d)\n",in_size);
	exit(EXIT_FAILURE);
    }
    if (out_size < 1) out_size = in_size;
    if ((in_file = fopen(in_filename,"r")) == NULL) {
	printf("Cannot open input file %s\n",in_filename);
	perror("Input error: ");
    } else {
	if ((out_file = fopen(out_filename,"w")) == NULL) {
	    printf("Cannot open output file %s\n",out_filename);
	    perror("Output error: ");
	} else {
	    int ch,quote_ch = 0;
	    int tab_pos = 0,last_pos = 0;
	    int in_count = 0,out_count = 0,rec_count = 0;
	    while ((ch = fgetc(in_file)) != EOF) {
		int err = 0;
		int ch_width;
		in_count++;
		if (ch >= ' ') {
		    ch_width = 1;
		} else {
		    ch_width = 0;
		    if (ch == '\n' || ch == '\r') {
			tab_pos = 0;
			last_pos = 0;
			quote_ch = 0;
			rec_count++;
		    } else {
			if (ch == '\t')
			    ch_width = (tab_pos / in_size + 1) * in_size - tab_pos;;
		    }
		}
		if (quote_ch || (ch != ' ' && ch != '\t')) {
		    while (last_pos < tab_pos) {
			int tab_skip = (last_pos / out_size + 1) * out_size;
			if (tab_skip - last_pos <= 1 || tab_skip > tab_pos) {
			    err = fputc(' ',out_file);
			    out_count++;
			    last_pos++;
			} else {
			    err = fputc('\t',out_file);
			    out_count++;
			    last_pos = (last_pos / out_size + 1) * out_size;
			}
			if (err == EOF) {
			    printf("I/O error writing %s\n",out_filename);
			    perror("Output error: ");
			    exit(EXIT_FAILURE);
			}
		    }
		    err = fputc(ch,out_file);
		    out_count++;
		    last_pos += ch_width;
		    if (err == EOF) {
			printf("I/O error writing %s\n",out_filename);
			perror("Output error: ");
			exit(EXIT_FAILURE);
		    }
		    if (ch == quote_ch) {
			quote_ch = 0;
		    } else {
			if ((ch == '\"' && double_q == 0) ||
			    (ch == '\'' && single_q == 0)) quote_ch = ch;
		    }
		}
		tab_pos += ch_width;
	    }
	    fclose(out_file);
	    fclose(in_file);
	    printf("Produced %d bytes from %d bytes in %d records\n",
		   in_count,out_count,rec_count);
	    return 1;
	}
    }
    return 0;
}

int main(int argc,char *argv[])
{
    int in_size = 8,out_size = 0,double_q = 0,single_q = 0;
    char *in_filename = NULL,*out_filename = NULL;
    int arg;
    for (arg = 1; arg < argc; arg++) {
	if (*argv[arg] == '-') {
	    switch (*(argv[arg] + 1)) {
		case 'i':
		    in_size = atoi(argv[arg] + 2);
		    break;
		case 'o':
		    out_size = atoi(argv[arg] + 2);
		    if (out_size == 0) out_size = 1;
		    break;
		case 'd':
		    double_q = 1;
		    break;
		case 's':
		    single_q = 1;
		    break;
		default:
		    printf("Don't understand \'%s\'\n",argv[arg]);
		    exit(EXIT_FAILURE);
	    }
	} else {
	    if (in_filename == NULL) {
		in_filename = argv[arg];
	    } else {
		if (out_filename == NULL) {
		    out_filename = argv[arg];
		} else {
		    printf("Too many parameters specified (%s)\n",argv[arg]);
		    exit(EXIT_FAILURE);
		}
	    }
	}
    }
    if (out_filename != NULL) {
	tab(in_filename,out_filename,in_size,out_size,double_q,single_q);
    } else {
	printf("TAB v1.0\n");
	printf("Usage: tab [-iN] [-oN] [-d] [-s] input_file output_file\n");
	printf("  -iN  input tab size is N characters\n");
	printf("  -oN  output tab size is N (defaults to input size)\n");
	printf("  -d   process tabs inside double quotes\n");
	printf("  -s   process tabs inside single quotes\n");
	printf("Setting output tab size to 1 (-o or -o1) will replace tabs with spaces\n");
	printf("Example:\n    tab  -o4 tab.c newtab.c\n");
    }
    return EXIT_SUCCESS;
}


syntax highlighted by Code2HTML, v. 0.9.1