/*++++++++++++++
 Copyright:    (C) 2008-2015 UDS/CNRS
 License:       GNU General Public License
.IDENTIFICATION trim.c
.LANGUAGE       C
.AUTHOR         Francois Ochsenbein [CDS]
.ENVIRONMENT    Unix
.KEYWORDS       Translate
.VERSION  1.0   22-Jul-2000
.VERSION  1.1   03-May-2005: Option -b (blanks only)
.VERSION  1.2   06-May-2007: cosmetics
.VERSION  1.3   19-Dec-2009: -s (squeeze) option
.VERSION  1.4   26-Aug-2010: -t (expand/detab)/ option
.VERSION  1.5   26-Sep-2011: bug in -t option
.COMMENTS       Insert newlines, remove \r, adjusrt without cutting words
---------------*/
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

#define VERSION  "1.5"
#define MAX(x,y)        ((x) >= (y) ? (x) : (y))
#define ITEMS(a)        sizeof(a)/sizeof(a[0])

static int verbop   = 0;  /* Verbose option       */
static int optb     = 0;  /* -b option	*/
static int expand   = 0;  /* -t option  */
static FILE *input  = (FILE *)0;
static int errors   = 0;  /* Errors counter       */
static int linelen ;	  /* Length line (Gets()) */
static char blanks[] = "       ";

static char pgm[] = "*** trim";
static char usage[] = "\
Usage: trim [-v] [-t] [-b[s]] [file]\n\
" ;
static char help[] = "\
   -b: only trailing blanks are removed (default include tabs, spaces,...)\n\
  -bs: blanks are squeezed (multiples blanks replaced by a single one)\n\
   -t: Replace the tab(s) by blanks (on the basis on 8bytes/tab)\n\
   -v: Verbose option, gives statistics (number lines / empty lines / reclen)\n\
" ;

/*==================================================================
		Internal Routines
 *==================================================================*/
static void error(text)
/*++++++++++++++++
.PURPOSE  Error exit
.RETURNS  -
-----------------*/
  char *text;     /* IN: Text before error */
{
    if (text)   fprintf(stderr, "%s: %s\n", pgm, text);
    fprintf(stderr, "%s", usage) ;
    exit(1);
}

static int strloc(text, c)
/*++++++++++++++++
.PURPOSE  Locate specified character
.RETURNS  Index of located char
-----------------*/
  char *text;     /* IN: String to interpret */
  int  c;         /* IN: Char to look for */
{
  char *s;
    for (s = text; *s && (*s != c); s++) ;
    return(s-text);
}

static char *Gets()
/*++++++++++++++++
.PURPOSE  Get a line (and forget the end of) on input
.RETURNS  as gets
.REMARKS  The length of line is saved in the static linelen
-----------------*/
{
  static char *abuf; static int lbuf;
  int i;
    
    if (!lbuf) { lbuf = BUFSIZ-1; abuf = malloc(lbuf+1); abuf[lbuf] = 0; }

    if (! fgets(abuf, lbuf-1, input)) {	/* Error or EOF ? */
	if (ferror(input)) perror("****Gets");
	return((char *)0);
    }
    i = strloc(abuf, '\n');
    while (!abuf[i]) {
	lbuf += BUFSIZ; abuf = realloc(abuf, lbuf);
	if (!fgets(abuf+i, BUFSIZ, input)) break;
	i = strloc(abuf, '\n');
    }
    abuf[i] = 0;
    linelen = i ;
    return(abuf);
}

static int exec()
/*++++++++++++++++
.PURPOSE  Execute the transformation on the opened file
.RETURNS  0 = OK 
-----------------*/
{
  int  reclen, nlines, spaces, zerolen ;
  char *line, *p ;
    reclen = nlines = spaces = zerolen = 0 ;
    while((line = Gets())) {
	spaces += linelen ;	/* Input spaces */
	if (optb) for (p=line+linelen-1 ; (p>=line) && (*p == ' '); p--) ;
	else      for (p=line+linelen-1 ; (p>=line) && isspace(*p); p--) ;
	*++p = 0 ;
	/* Squeeze blanks */
	if (optb&2) { int i, ignore; p = line;
	    for (i=0, ignore=1; line[i]; i++) {
		if (line[i] == ' ') {
		    if (!ignore) *(p++) = ' ';
		    ignore = 1;
		}
		else if (isspace(line[i])) {  /* Remove blanks preceding TAB */
		    for (--p; (p>=line) && (*p == ' '); p--) ;
		    *(++p) = line[i];
		    p++;
		    ignore = 1;
		}
		else {
		    *(p++) = line[i]; 
		    ignore = 0;
		}
	    }
	    *p = 0;
	}
	if (p == line) zerolen++ ;
	else if (expand) {	/* Convert the tabs */
	  int i, o=0;
	    linelen = (p-line);
	    for (p=line; *p; p++) {
		if ((i = strloc(p, '\t'))) fwrite(p, 1, i, stdout); 
		o += i; p += i;
		if (*p) { 
		    putchar(' '); o++;	/* Fixed V1.5 */
		    while (o&7) { o++; putchar(' '); linelen++; }
		}
	    }
	}
	else {
	    linelen = (p-line) ;
	    if (linelen > reclen) reclen = linelen ;
	    fwrite(line, 1, linelen, stdout) ;
	}
	spaces -= linelen ;
	putchar('\n') ;
	nlines++ ;
    }
    if (verbop) fprintf(stderr, 
	"%5d lines, empty=%d; reclen=%d; stripped=%d\n",
	nlines, zerolen, reclen, spaces) ;
    return(0);
}

/*==================================================================*/

#define pVal(text)      if (! *++p) {                           \
				if (--argc <= 0) error(text);   \
				else    p = *++argv;            \
			}
/*==================================================================
		Program
 *==================================================================*/
int main (int argc, char **argv)
{
  char *p;

	/* Check the Arguments */
  
  while (-- argc > 0) {
	p = *++argv;
	if (*p == '-') switch (*++p) {
	  case 'b':
		optb = 1;
		if (p[1] == 's') optb |= 2;
		continue;
	  case 't':			/* -t: expand   */
		expand = 1;
		continue;
	  case 'v':                     /* Verbose      */
		if (strncmp(*argv, "-ver", 4) == 0) {
		     printf("trim (CDS) Version %s\n", VERSION);
		     exit(0);
		}
		verbop = 1;
		/* fprintf(stderr, "... %s: Examine argument ", pgm+4); */
		continue;
	  case 'h':                     /* Help         */
		printf("%s%s", usage, help) ;
		exit(0);
	  default:
	  bad_option:
		fprintf(stderr, "\n*** %s: Bad option: -%s\n", pgm+4, p);
		errors ++;
		exit (1);
	} else {
	    if (*p == '<') { 
		pVal("Input file");
	    }
	    input = fopen(p, "r");
	    if (! input) { fprintf(stderr, "***"); perror(p); exit(1); }
	    if (verbop) fprintf(stderr, "==== %-12s :", p) ;
	    if (exec()) exit (1);
	    fclose(input);
	}
    }
    if (! input) {
  	input = stdin;
	if (verbop) fprintf(stderr, "==== (stdin) :") ;
  	if (exec()) exit(1);
    }
    exit(0);
}
