// (c) 1992-2007 Electronic Arts Inc.

const char *SPIT_history[] =
{
"------------------------------------------------------------------",
"                                                                  ",
"                             Tabs v1.12                           ",
"                                                                  ",
"            Copyright (c) 1992 Electronic Arts Canada Inc         ",
"                                                                  ",
"                by Brian Plank and FrANK G. Barchard              ",
"                                                                  ",
"                     Main program - Oct 01, 2003                  ",
"                                                                  ",
"------------------------------------------------------------------",
"                                                                  ",
" Version Date   History:                                          ",
" ------- ------ --------                                          ",
"    1.00 911125 Initial version, by Brian Plank                   ",
"    1.01 921207 -l tabs left only/quoted strings not tabbed       ",
"    1.02 921208 allow destination to be named                     ",
"    1.03 921209 tabs of 1 ok                                      ",
"    1.04 921211 if not -l and no tabs, don't insert tabs (bug)    ",
"    1.05 930601 /r/n->/r, tabs stops optional, temp names         ",
"    1.06 930612 use space, not tab, if only 1 space               ",
"    1.07 930620 explicit tabs of 1 ok, numerical filenames ok     ",
"    1.08 930626 cr/lf fix                                         ",
"    1.09 931003 internally use unsigned chars                     ",
"    1.10 940525 -s to replace any space more than in with out tab ",
"    1.11 980112 bumped max line to 512                            ",
"    1.12 031001 temporary file behavior rewritten based on txt    ",
"                                                                  ",
"------------------------------------------------------------------",
"\0"
};

const char TextVersion[]="1.12 (Samba) 2007.12.4";
#define TOOL_VERSION 112
#define TOOL_NAME "tabs"

#if 0
/*
;
; ABSTRACT
;
; tabs - tab and space conversion tool
;
; SUMMARY
;
; tabs [-l][-s] intabstops outtabstops infilename1 [infilename2...]
;     -l   for tabs replaced on left of text only
;     -s   for single tab
;     -r   recurse subdirectories
;
; DESCRIPTION
;
; The TABS tool replaces sequences of spaces with tabs and
; removes trailing spaces from lines of text in a file
; converts CR/LR eoln characters to \n.
;
; SEE ALSO
;
; gsr
;
; SAMPLE OUTPUT
;
; Display contents of all text files:
;
; tabs 8 4 *.cpp
; - the input files contain tabs that assume 8 space tabstops;
;   the program will output files that assume 4 space tabstops
;
; tabs 4 0 *.cpp
; - all tabs are removed and replaced with spaces
;
; END ABSTRACT
;
*/
#endif

/****************************************************************/
/*  Spit Setup                                                  */
/****************************************************************/

#include    "spit.h"
#include    <stdio.h>
#include    <fcntl.h>
#include    <stdlib.h>
#include    <string.h>
#include    <ctype.h>

#define MAX_LINE 512

static int OPTleftonly=0;
static int OPTsingle=0;

static char rcsid[]="$Id: tabs.c%v 1.1 1992/06/19 04:21:26 brian Exp $";

void tabs(FILE *in, FILE *out, int intabstops, int outtabstops)
{
    int     i, j, trailspaces;
    int     last=0;
    int     col;
    int     done;
    int     suppressexpand;
    int     c;
    unsigned char   line[MAX_LINE];

    done=0;
    col=0;
    suppressexpand=0;
    c = 0;
    if (OPTsingle)
        outtabstops = 0;

    while(!done)
    {
        if (col>MAX_LINE-16)
        {
            c = 13;
        }
        else
        {
            last = c;
            c = getc(in);
        }

        switch (c)
        {
            case ' ':
                if (OPTsingle && (!suppressexpand) && (last==9 || last==' '))
                {
                    line[col-1] = '\t'; /* stamp tab over space */
                }
                else
                {
                    line[col++]=(char)c;
                }
                break;

            case 9:
                if (OPTsingle && (!suppressexpand) && (last==9 || last==' '))
                {
                    line[col-1] = '\t'; /* stamp tab over space */
                }
                else
                {
                    if (suppressexpand)
                    {
                        line[col++] = '\t';
                    }
                    else
                    {
                        do
                        {
                            line[col]=' ';
                        } while(++col % intabstops);
                    }
                }
                break;

            case EOF:
                done = 1;

            case 10:
            case 13:
                if ((last==10 && c==13) || (last==13 && c==10))
                {
                    c = 0;  /* allow next cr or lf */
                    break;
                }
                suppressexpand=0;
                while(col && line[col-1]==' ')
                    col--;

                if (outtabstops)
                {
                    for (i=outtabstops; i<=col; i+=outtabstops)
                    {
                        j = i;
                        while (j>i-outtabstops && line[j-1]==' ')
                            --j;
                        trailspaces=i-j;

                        if ((trailspaces!=outtabstops) && (OPTleftonly))
                        {
                            for (j=i-outtabstops; j<(col/outtabstops)*outtabstops; ++j)
                                putc(line[j], out);
                            i = col;
                        }
                        else
                        {
                            for (j=i-outtabstops; j<i-trailspaces; j++)
                            {
                                if (line[j]=='\'' || line[j]=='"')
                                    suppressexpand ^= 1;
                                putc(line[j], out);
                            }

                            if (trailspaces)
                            {
                                if (suppressexpand || (trailspaces==1 && outtabstops>1))
                                    while (trailspaces--)
                                        putc(' ', out);
                                else
                                    putc('\t', out);
                            }
                        }
                    }

                    for (i=(col/outtabstops)*outtabstops; i<col; i++)
                        putc(line[i], out);
                }
                else
                {
                    for (i=0; i<col; i++)
                        putc(line[i], out);
                }

                if (c==10 || c==13)
                    putc('\n', out);

                col=0;
                suppressexpand=0;
                break;

            case '\b':
                if (col) col--;
                break;

            case '\'':
            case '"':
                suppressexpand ^= 1;
                line[col++]=(char)c;
                break;

            default:
                if (OPTleftonly)
                    suppressexpand |= 2;
                line[col++]=(char)c;
                break;
        }
    }
}

void SPIT_usage(const char *prgname, const char *envname, const char *urlname, int utype)
{
    if (utype>1)
    {
        printf("\n");
        printf("TABS       Copyright (c) 1992, 1992 Brian Plank\n");
        printf("           Copyright (c) 1993, 1994 Frank Barchard, EAC\n");
        printf("           Revision %s %s %s\n",TextVersion,__DATE__,__TIME__);
        printf("\n");
        printf("Purpose:   replaces sequences of spaces with tabs and\n");
        printf("           removes trailing spaces from lines of text in a file\n");
        printf("           converts cr/lf eoln to \\n");
        printf("\n");
        printf("usage:     tabs [-l][s][-r] intabstops outtabstops infilename1 [infilename2 ...]\n");
        printf("           -l for tabs replaced on left of text only\n");
        printf("           -s for single tab\n");
        printf("           -r recurse subdirectories");
        printf("\n");
        printf("Example:   C:> %s 8 4 *.c\n",prgname);
        printf("           - the input files contain tabs that assume 8 space tabstops,\n");
        printf("             the program will output files that assume 4 space tabstops\n");
        printf("\n");
        printf("           C:> %s 8 0 *.c\n",prgname);
        printf("           - like above, except all tabs are removed and replaced with spaces\n");
        printf("\n");
        printf("Permission to distribute freely is hereby given by the author.\n");
        printf("\n");
        printf("Disclaimer:\n");
        printf("This software has been tested by the author, but,\n");
        printf("BY USING THIS SOFTWARE YOU, THE SOFTWARE USER, AGREE TO ASSUME\n");
        printf("ALL RISKS AS TO THE RESULTS AND PERFORMANCE OF THIS SOFTWARE.\n");
        printf("IF YOU DON'T AGREE TO ASSUME ALL RISKS, THEN DO NOT USE THIS SOFTWARE.\n");
    }
}

char *SPIT_opt(const char *optptr)
{
    switch (*optptr|(char)' ')
    {
        case 'l': return(SPIT_getopt( &OPTleftonly,optptr+1, "left only", 0, 1));
        case 's': OPTminargs = 1;
                  return(SPIT_getopt( &OPTsingle,optptr+1, "single tab out", 0, 1));

        default: ;
    }
    return((char *) 0);
}

void    rangeerror(int lo, int hi, char *s)
{
    printf("%s must be between %d and %d\n", s, lo, hi);
    exit(1);
}

int  main(int argc, char *argv[])
{
    int     i, intabstops, outtabstops;
    FILE    *in, *outfile;

    OPTminargs = 3;
    OPTleftonly = 0;
    OPTsingle = 0;

    SPIT_init(NULL, SPIT_usage, SPIT_opt, SPIT_history);
    SPIT_parse(argc, argv, TOOL_NAME, TOOL_VERSION);

    i = 0;
    intabstops = 4;
    if (i<(SPITargnum-1))
    {
        if (isdigit(SPITsargv[i][0]))
        {
            intabstops=atoi(SPITsargv[i]);
            if (intabstops<1 || intabstops>32)
                rangeerror(1, 32, "intabstops");
            ++i;
        }
    }

    outtabstops = 4;
    if (i<(SPITargnum-1))
    {
        if (isdigit(SPITsargv[i][0]))
        {
            outtabstops=atoi(SPITsargv[i]);
            if (outtabstops<-32 || outtabstops>32)
                rangeerror(-32, 32, "outtabstops");
            ++i;
        }
    }

    if (i<SPITargnum)
    {
        if (OPTsingle)
        {
            printf("Replacing white space sequences with single tabstop.\n");
        }
        else
        {
            if (outtabstops)
            {
                if (OPTleftonly)
                    printf("Replacing leading %2d character tabstops with %2d character tabstops.\n", intabstops, outtabstops);
                else
                    printf("Replacing %2d character tabstops with %2d character tabstops.\n", intabstops, outtabstops);
            }
            else
                printf("Replacing %2d character tabstops with spaces.\n", intabstops);
        }

        while (i<SPITargnum)
        {
            printf("Processing %s -- ", SPITsargv[i]);
            in=fopen(SPITsargv[i], "rb");
            if (!in) { perror(""); exit(1); };

            char outname[SPITMAXLINE];

            if (OPTwrite)
            {
//              if (OPTtolower)
//                  strlwr(SPITdargv[i]);

                SPIT_tempname(outname,SPITdargv[i]);
                remove(outname);


                outfile=fopen(outname, "w");
                if (!outfile)
                {
                    if (OPTverbose>=2)
                        printf("txt - COULD NOT OPEN FILE FOR WRITE '%s'\n",outname);
                    perror(outname);
                    fclose(in);
                    in = NULL;
                }
            }
            else
                outfile = 0;

            if (in)
            {
                tabs(in, outfile, intabstops, outtabstops);

                fclose(in);
            }

            if (OPTwrite && outfile)
            {
                fclose(outfile);
                remove(SPITdargv[i]);
                rename(outname,SPITdargv[i]);
            }

            printf("done.\n");
            ++i;
        }
    }


    return 0;
}

