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

const char *SPIT_history[] =
{
"------------------------------------------------------------------",
"                                                                  ",
"                             Text v1.62                           ",
"                                                                  ",
"                        by FrANK G. Barchard                      ",
"                                                                  ",
"                     Main program - Nov 08, 2007                  ",
"                                                                  ",
"------------------------------------------------------------------",
"                                                                  ",
" Version Date   SE History:                                       ",
" ------- ----   -- --------                                       ",
"    1.00 931114 FB Initial version, by based on tabs,runpack,ps   ",
"    1.01 931117 FB Convert source file name to lower too          ",
"    1.02 931122 FB Remove CTRL Z's                                ",
"    1.03 931130 FB -s stats                                       ",
"    1.04 931208 FB Full pathnames work                            ",
"    1.05 931211 FB . for destination/better error messages        ",
"    1.06 940302 FB -s for smart translation/-i for info (stats)   ",
"    1.07 940408 FB .bat recoqnize                                 ",
"    1.08 940415 FB .rsp and .scr                                  ",
"    1.09 940714 FB .lnk                                           ",
"    1.10 940806 FB .bif,graphics type list                        ",
"    1.11 940816 FB .3sp packed shape file                         ",
"    1.12 941004 FB .log for development log text files            ",
"    1.13 941018 FB .c65 565 cels                                  ",
"    1.14 950209 FB .ps postscript files                           ",
"    1.15 950308 FB translate to/simple types opts (pc,mac,sgi)    ",
"    1.16 950405 FB change destination name to lower case (sgi)    ",
"    1.17 950508 FB smart smarter, .asc                            ",
"    1.18 950510 FB translate only if not recognised (ie not .tga) ",
"    1.19 950512 FB .htm                                           ",
"    1.20 950524 FB -nocr                                          ",
"    1.21 950622 FB .mov                                           ",
"    1.22 950814 FB .a is archive on SGI, so dont translate on SGI ",
"    1.23 951008 FB .fsh, gimex fileio                             ",
"    1.24 951108 FB make web form messages readable                ",
"    1.25 951119 FB .tgv,.avi                                      ",
"    1.26 951129 FB .sea,.icon                                     ",
"    1.27 960416 FB .dof                                           ",
"    1.28 960509 FB MrC version                                    ",
"    1.29 961122 FB -s2 to also consider extensionless as text     ",
"    1.30 970103 YK -x DOS<-->Windows ASCII translation            ",
"    1.31 970122 FB .c++ extension                                 ",
"    1.32 970627 FB total line count                               ",
"    1.33 980319 FB .hrc                                           ",
"    1.34 980708 FB .rc .err .map added                            ",
"    1.35 980708 FB .tog                                           ",
"    1.36 981106 FB .lst                                           ",
"    1.37 981117 FB use stdgimex to do renaming/temp files         ",
"    1.38 990616 FB tolower not on by default                      ",
"    1.39 990619 FB -linux, fixed 4 warnings                       ",
"    1.40 990628 FB .vsm,.cc,v.dsm for sony                        ",
"    1.41 990709 FB crcrlf input from txt ftp translation of crlf  ",
"    1.42 990712 FB -xl translate to lower case                    ",
"    1.43 990913 FB -w1 for output to console (pipe)               ",
"    1.44 991028 FB unix term..more common than sgi now            ",
"    1.45 991130 FB .java                                          ",
"    1.46 000401 FB -w is just 0 or 1 now                          ",
"    1.47 000518 FB -w0 respected                                  ",
"    1.48 000531 FB .xml                                           ",
"    1.49 000817 LC undocumented -xdw -xwd                         ",
"    1.50 001024 FB 10100013 (lf lf nil cr) from ftp errors        ",
"    1.51 001113 FB rename temp file                               ",
"    1.52 001115 FB rename attempt 2                               ",
"    1.53 010118 FB -opera for space cr lf                         ",
"    1.54 020314 FB .sha (shader)                                  ",
"    1.55 020714 FB -w0 better respected                           ",
"    1.56 020717 FB SPIT_tempname                                  ",
"    1.57 020726 FB -gcc add extra \n at end of file if missing    ",
"    1.58 020930 FB .tcl & .py unix script +x                      ",
"    1.59 021017 FB .cs                                            ",
"    1.60 030122 FB -unsmart to replace ‘ and ’ with '             ",
"    1.61 060313 FB -join2 to join lines into one                  ",
"    1.62 071108 FB -i4 to pad info to 4 chars                     ",
"                                                                  ",
"------------------------------------------------------------------",
"\0"
};

/*
from MSDN 2003
new-line:
Carriage return character (U+000D)
Line feed character (U+000A)
Carriage return character (U+000D) followed by line feed character (U+000A)
Line separator character (U+2028)
Paragraph separator character (U+2029)
*/

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

#include    "spit.h"

const char TextVersion[]="1.62 (Samba) 2007.12.4";
#define TOOL_VERSION 162
#define TOOL_NAME "txt"

int *OPTxlate;
int OPTxlatereverse;
int OPTnewlinetype;
int OPTnocr;
int OPTjoin;
int OPTinfo;
int OPTsmart;
int OPTtolower;
int OPTunsmart;
int OPTopera;
int OPTgcc;  // add new line to end of file for gcc on unix
int unweb;  /* auto option */

#define BUFSIZE 256


static int bufpos=0;
static char bufc[BUFSIZE];
static char buft[BUFSIZE];

#define ID(a,b,c,d) (((a)<<24)|((b)<<16)|((c)<<8)|(d))

struct filetypes
{
    char *ext;
    int type;
    int creator;
} filetypes[]=
{
    "TXT",  ID('T','E','X','T'), ID('M','P','S',' '),
    "TXT",  ID('T','E','X','T'), ID('M','P','S',' '),
    "C",    ID('T','E','X','T'), ID('M','P','S',' '),
    "H",    ID('T','E','X','T'), ID('M','P','S',' '),
    "INC",  ID('T','E','X','T'), ID('M','P','S',' '),
    "ASM",  ID('T','E','X','T'), ID('M','P','S',' '),
    "VSM",  ID('T','E','X','T'), ID('M','P','S',' '),
    "DSM",  ID('T','E','X','T'), ID('M','P','S',' '),
    "DEF",  ID('T','E','X','T'), ID('M','P','S',' '),
    "I",    ID('T','E','X','T'), ID('M','P','S',' '),
    "S",    ID('T','E','X','T'), ID('M','P','S',' '),
    "RC",   ID('T','E','X','T'), ID('M','P','S',' '),
    "MAP",  ID('T','E','X','T'), ID('M','P','S',' '),
    "ERR",  ID('T','E','X','T'), ID('M','P','S',' '),
    "JAVA", ID('T','E','X','T'), ID('M','P','S',' '),
    "PY",   ID('T','E','X','T'), ID('M','P','S',' '),
    "CS",   ID('T','E','X','T'), ID('M','P','S',' '),
    "CPP",  ID('T','E','X','T'), ID('M','P','S',' '),
    "C++",  ID('T','E','X','T'), ID('M','P','S',' '),
    "CC",   ID('T','E','X','T'), ID('M','P','S',' '),
    "CXX",  ID('T','E','X','T'), ID('M','P','S',' '),
    "MAK",  ID('T','E','X','T'), ID('M','P','S',' '),
    "BAT",  ID('T','E','X','T'), ID('M','P','S',' '),
    "SCR",  ID('T','E','X','T'), ID('M','P','S',' '),
    "RSP",  ID('T','E','X','T'), ID('M','P','S',' '),
    "LNK",  ID('T','E','X','T'), ID('M','P','S',' '),
    "BIF",  ID('T','E','X','T'), ID('M','P','S',' '),
    "LOG",  ID('T','E','X','T'), ID('M','P','S',' '),
    "PS",   ID('T','E','X','T'), ID('M','P','S',' '),
    "ASC",  ID('T','E','X','T'), ID('M','P','S',' '),
    "HTM",  ID('T','E','X','T'), ID('M','P','S',' '),
    "HTML", ID('T','E','X','T'), ID('M','P','S',' '),
    "XML",  ID('T','E','X','T'), ID('M','P','S',' '),
    "DOF",  ID('T','E','X','T'), ID('M','P','S',' '),
    "HRC",  ID('T','E','X','T'), ID('M','P','S',' '),
    "TOG",  ID('T','E','X','T'), ID('M','P','S',' '),
    "ABS",  ID('T','E','X','T'), ID('M','P','S',' '),
    "LST",  ID('T','E','X','T'), ID('M','P','S',' '),
    "TCL",  ID('T','C','L','T'), ID('M','P','S',' '),
    "ION",  ID('T','E','X','T'), ID('M','P','S',' '),
    "SHA",  ID('T','E','X','T'), ID('M','P','S',' '),
#ifndef __unix__
    "A",    ID('T','E','X','T'), ID('M','P','S',' '),
#endif

    "SEA",  ID('A','P','P','L'), ID('M','P','S',' '),
    "ICON", ID('I','C','O','N'), ID('S','P','o','T'),
    "ICO",  ID('I','C','O','N'), ID('S','P','o','T'),
    "ANIM", ID('3','D','O','a'), ID('S','P','o','T'),
    "BMP",  ID('.','B','M','P'), ID('S','P','o','T'),
    "BLT",  ID('.','B','L','T'), ID('S','P','o','T'),
    "BOB",  ID('B','O','B',' '), ID('B','A','B','L'),
    "CEL",  ID('3','D','O','c'), ID('S','P','o','T'),
    "C65",  ID('.','C','6','5'), ID('S','P','o','T'),
    "CMP",  ID('.','C','M','P'), ID('S','P','o','T'),
    "CUT",  ID('.','C','U','T'), ID('B','A','B','L'),
    "EPS",  ID('E','P','S','F'), ID('8','B','I','M'),
    "EPI",  ID('E','P','S','F'), ID('8','B','I','M'),
    "EPSF", ID('E','P','S','F'), ID('8','B','I','M'),
    "EPSI", ID('E','P','S','F'), ID('8','B','I','M'),
    "FLI",  ID('F','L','I','C'), ID('B','A','B','L'),
    "FLC",  ID('F','L','I','C'), ID('B','A','B','L'),
    "GIF",  ID('G','I','F','f'), ID('8','B','I','M'),
    "IMG",  ID('3','D','O','i'), ID('3','D','O','e'),
    "IMAG", ID('3','D','O','i'), ID('3','D','O','e'),
    "JPG",  ID('J','P','E','G'), ID('8','B','I','M'),
    "JFIF", ID('J','P','E','G'), ID('8','B','I','M'),
    "LBM",  ID('I','L','B','M'), ID('S','P','o','T'),
    "IFF",  ID('I','L','B','M'), ID('S','P','o','T'),
    "BBM",  ID('I','L','B','M'), ID('S','P','o','T'),
    "ILBM", ID('I','L','B','M'), ID('S','P','o','T'),
    "MOOV", ID('M','o','o','V'), ID('B','A','B','L'),
    "MOV",  ID('M','o','o','V'), ID('B','A','B','L'),
    "MSP",  ID('.','M','S','P'), ID('B','A','B','L'),
    "MVI",  ID('.','M','V','I'), ID('S','P','o','T'),
    "NEO",  ID('.','N','E','O'), ID('B','A','B','L'),
    "PAL",  ID('8','B','C','T'), ID('8','B','I','M'),
    "PCD",  ID('P','I','C','T'), ID('p','p','x','i'),
    "PCX",  ID('.','P','C','X'), ID('S','P','o','T'),
    "PI1",  ID('D','E','G','A'), ID('B','A','B','L'),
    "PIC",  ID('.','S','I','M'), ID('S','P','o','T'),
    "PICT", ID('P','I','C','T'), ID('S','P','o','T'),
    "PIQT", ID('P','I','C','T'), ID('T','V','O','D'),
    "PNTG", ID('P','N','T','G'), ID('M','P','N','T'),
    "PPM",  ID('P','P','M',' '), ID('8','B','I','M'),
    "PBM",  ID('P','P','M',' '), ID('8','B','I','M'),
    "PGM",  ID('P','P','M',' '), ID('8','B','I','M'),
    "PNM",  ID('P','P','M',' '), ID('8','B','I','M'),
    "PSD",  ID('8','B','I','M'), ID('8','B','I','M'),
    "PXR",  ID('P','X','A','R'), ID('8','B','I','M'),
    "QDV",  ID('.','Q','D','V'), ID('B','A','B','L'),
    "RAST", ID('.','S','U','N'), ID('B','A','B','L'),
    "RGB",  ID('R','R','G','B'), ID('B','A','B','L'),
    "RAW",  ID('.','H','S','I'), ID('S','P','o','T'),
    "RLE",  ID('R','L','E','C'), ID('B','A','B','L'),
    "SANM", ID('3','D','O','s'), ID('3','D','O','e'),
    "SCAN", ID('S','C','A','N'), ID('B','A','B','L'),
    "SCT",  ID('.','.','C','T'), ID('8','B','I','M'),
    "SCR",  ID('.','S','C','R'), ID('S','P','o','T'),
    "SGI",  ID('.','S','G','I'), ID('B','A','B','L'),
    "TGA",  ID('T','P','I','C'), ID('S','P','o','T'),
    "TIF",  ID('T','I','F','F'), ID('8','B','I','M'),
    "TIFF", ID('T','I','F','F'), ID('8','B','I','M'),
    "TIM",  ID('.','T','I','M'), ID('S','P','o','T'),
    "VDAN", ID('V','D','A','N'), ID('V','D','3','2'),
    "VGA",  ID('.','V','G','A'), ID('S','P','o','T'),
    "VSH",  ID('.','V','S','H'), ID('S','P','o','T'),
    "YUV",  ID('A','B','E','K'), ID('B','A','B','L'),
    "3SP",  ID('.','3','S','P'), ID('S','P','o','T'),
    "3SH",  ID('.','3','S','H'), ID('S','P','o','T'),
    "PSH",  ID('.','P','S','H'), ID('S','P','o','T'),
    "FSH",  ID('.','F','S','H'), ID('S','P','o','T'),
    "TGV",  ID('.','t','g','v'), ID('S','P','o','T'),
    "AVI",  ID('.','a','v','i'), ID('S','P','o','T'),
    "PYC",  ID('.','p','y','c'), ID('S','P','o','T'),
 (char *)0, ID( 0 , 0 , 0 , 0 ), ID( 0 , 0 , 0 , 0 ),
};

/* Translation tables */

/* Convert DOS ASCII to Windows */
int Xdostowindows[256] =
{
    0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15,  /*   0: */
   16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,  /*  16: */
   32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  /*  32: */
   48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,  /*  48: */
   64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,  /*  64: */
   80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,  /*  80: */
   96,  97,  98,  99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,  /*  96: */
  112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,  /* 112: */
  199, 252, 233, 226, 228, 224, 229, 231, 234, 235, 232, 239, 238, 236, 196, 197,  /* 128: */
  201, 230, 198, 244, 246, 242, 251, 249, 255, 214, 220, 162, 163, 165,  -1, 131,  /* 144: */
  225, 237, 243, 250, 241, 209, 170,  -1, 191,  -1, 172, 189, 188, 161, 171, 187,  /* 160: */
   -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  /* 176: */
   -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  /* 192: */
   -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  /* 208: */
   -1, 223,  -1,  -1,  -1,  -1, 181,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  /* 224: */
   -1, 177, 178,  -1,  -1,  -1, 247,  -1, 176, 130, 183,  -1,  -1,  -1,  -1,  -1   /* 240: */
};

/* Convert to lowercase */

int Xtolower[256] =
{
    0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
    0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
    0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,
    0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
    0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
    0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
    0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,
    0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
    0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,
    0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
    0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,
    0x78,0x79,0x7a,0x5b,0x5c,0x5d,0x5e,0x5f,
    0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,
    0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
    0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,
    0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
    0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,
    0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
    0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,
    0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
    0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,
    0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
    0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,
    0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
    0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,
    0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
    0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,
    0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
    0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,
    0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
    0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,
    0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff,
};

void SPIT_flushc(GSTREAM *out)
{
    if (!strnicmp(bufc,"comments=",9))
        unweb = 1;

    if (unweb)
    {
        int i;
        int j=0;
        int c;
        static int lastc=0;

        i = 0;
        if (bufc[i]==lastc)
            ++i;

        while (i<bufpos)
        {
            switch (bufc[i])
            {
                case '%':
                    sscanf(bufc+i+1, "%02x", &c);
                    buft[j++] = (char) c;
                    i += 3-1;
                    break;

                case '+':
                    buft[j++] = (char) ' ';
                    break;

                case 10:
                case 13:
                    break;

                default:
                    lastc = bufc[i];
                    buft[j++] = (char) lastc;
                    break;
            }
            ++i;
        }
        memcpy(bufc,buft,j);
        bufpos = j;
    }

    if (OPTwrite && out)
        gwrite(out,bufc,bufpos);
    bufpos = 0;
}

void SPIT_putc(int c,GSTREAM *out)
{
    bufc[bufpos++] = (char) c;
    if (bufpos>=BUFSIZE)
        SPIT_flushc(out);
}

void SPIT_putcr(GSTREAM *out,int type)
{
    if (OPTopera)
        SPIT_putc(32,out);
    switch(type)
    {
        case 0: /* PC style cr/lf */
            SPIT_putc(13,out);
            SPIT_putc(10,out);
            break;

        case 1: /* Mac style cr */
            SPIT_putc(13,out);
            break;

        default: /* Unix style lf */
            SPIT_putc(10,out);
            break;
    }
    SPIT_flushc(out);
}

/* Table-based character translation. */
int xlate(int cin, int *table, int reverse)
{
    int cout=0;

    if (reverse)
    {
        int     i;

        /* If translation is reversed then search the table (first */
        /* check for identity translation). */
        if (table[cin] == cin)
        {
            cout = cin;
        }
        else
        {
            for (i = 0; i < 256; ++i)
            {
                if (table[i] == cin)
                {
                    cout = i;
                    break;
                }
            }
        }
    }
    else
    {
        cout = table[cin];
    }

    /* If character wasn't found then warn user and return the */
    /* input character. */
    if (cout < 0)
    {
        if (OPTverbose>=4)
        {
            printf("Untranslatable character \'%c\' (0x%02x, %dd)\n", cin, cin, cin);
        }

        cout = cin;
    }

    return(cout);
}

int text(GSTREAM *in,GSTREAM *out, int type, int nocr, int join)
{
    bool done=false;
    int lastch=0;
    int lastnl=0;
    int ch=0;
    unsigned char buf[4];
    int numberoflines=0;

    while (!done)
    {
        lastch = ch;
        ch = EOF;
        if (gread(in,buf,1))
            ch = buf[0];

        switch(ch)
        {
            case EOF:
                done = true;
                break;

            case 13:
                if (nocr)   /* completely ignore CRs */
                {
                    ch = 0;
                }
                else
                {
                    int p;
                    int numread;

                    p = (int) gtell(in);
                    numread = gread(in,buf+1,3);

                    if (!(numread==3 && buf[1]==(char)0 && buf[2]==(char)13 && buf[3]==(char)10)) // if not cr00crlf
                        gseek(in,p);

                    if (lastch==10)     /* lfCR */
                    {
                        ch = 0;
                    }
                    else if (!(lastch==13 && buf[1]==(char)10))   /* !crCRlf */
                    {
                        ++numberoflines;
                        ++lastnl;
                        if (join && (numberoflines%join!=0))    // join nth line with ,
                            SPIT_putc(',',out);
                        else
                            SPIT_putcr(out,type);
                    }
                }
                break;

            case 10:
                if (lastch==13) /* crLF */
                {
                    ch = 0;
                }
                else
                {
                    ++numberoflines;
                    ++lastnl;
                    if (join && (numberoflines%join==0))    // join nth line with ,
                        SPIT_putc(',',out);
                    else
                        SPIT_putcr(out,type);
                }
                break;

            default:
                lastnl = 0;
                if (OPTxlate)
                    ch = xlate(ch, OPTxlate, OPTxlatereverse);
                if (OPTunsmart)
                {
                    if (ch==0x91 || ch==0x92)
                        ch = '\'';
                }
                SPIT_putc(ch,out);
            case 'z'&31:
                break;
        }
    }
    if (OPTgcc && lastnl<2) // gcc wants one blank line
        SPIT_putcr(out,type);

    SPIT_flushc(out);

    return(numberoflines);
}

/* copy file with no translation */

int txtcopy(GSTREAM *in,GSTREAM *out)
{
    int ch=0;
    int len=0;
    unsigned char buf[4];

    while (ch!=EOF)
    {
        ch = EOF;
        if (gread(in,buf,1))
            ch = buf[0];

        if (ch!=EOF)
        {
            SPIT_putc(ch,out);
            ++len;
        }
    }
    SPIT_flushc(out);
    return(len);
}

void SPIT_openstr(const char *fname)
{
    char statbuf[SPITMAXLINE];

    if (OPTverbose)
    {
        if (OPTverbose>=4)
            printf("%s: ",fname);
        else
        {
            if (OPTverbose>=3)
                printf("%s: ",SPIT_getname( statbuf,fname));
            else
                printf("%s ",SPIT_getname( statbuf,fname));
        }
    }
}

int findtype(const char *fname)
{
    int i;
    int type=0;
    char *ext;
    char buf[512];

    ext = strrchr(SPIT_getname(buf,fname),'.');
    if (ext)
    {
        ++ext;
        for (i=0; filetypes[i].ext; ++i)
        {
            if (!stricmp(ext,filetypes[i].ext))
            {
                type = i;
            }
        }
    }

    return(type);
}

/* Do translation if */
/*   recognised text file extension */
/*   or unrecognised extension, 1 file only, and dest==source */

int checkdotranslation(char *inname,char *outname,int SPITargnum,int smart)
{
    int i;
    int doit=1;

    if (smart)
    {
        doit = 0;
        i = findtype(inname);

        if (i)
        {
            if (filetypes[i].type == ID('T','E','X','T'))
                doit = 1;
        }
        else
        {
            char buf[512];
            if (!strrchr(SPIT_getname(buf,inname),'.'))
                if (smart==2)
                    doit = 1;

            if (!strcmp(inname, outname) && SPITargnum==1)
                doit = 1;
        }
    }

    return(doit);
}

/*
;
; ABSTRACT
;
; txt - convert text file CR styles
;
; SUMMARY
;
; txt -options [files]
;     @file     response file, containing a list of options and files
;     =file     specify a output file ie txt source=dest
;     -pc       PC type crlf (also -ibm,-pc,-dos or -crlf)
;     -mac      Mac type cr (also -mpw,-ppc or -cr)
;     -unix     Unix type lf (also -sgi, -linux or -lf)
;     -opera    Opera likes you to a add a space at end of line
;     -gcc      GCC Unix likes you to a add a NL at end of file
;     -t0       PC type crlf
;     -t1       Mac type cr
;     -t2       SGI type lf
;     -nologo   disable banner
;     -i0       disable info/stats
;     -i1       enable info/stats
;     -join<n>  join N lines into one, seperating by comma
;     -tolower  lower case filenames
;     -r        recurse subdirectories
;     -s0       disable smartness - translate all files
;     -s1       smart translate only known file types based on extension
;     -s2       smart + translate extensionless(default)
;     -unsmart  replace smart quotes with single quotes
;     -v0       no console output
;     -v1       prints source file name and errors
;     -v2       status line for each file (default)
;     -v3       additional info
;     -w0       don't output file
;     -w1       output to file (default)
;     -xl       translate to lower case
;
; DESCRIPTION
;
; TXT converts textfile formats between PC, Unix and Mac.
; The input file type is automatically detected.  The output type
; is specified by the -t option, which defaults to the type native
; to the machine TXT is running on.  Normally, only the filenames
; need to be specified.  Files are converted and written over the
; original files.  Options can be used to write files with different
; names or to different directories.  TXT can also be used to view
; text files on the console, using the -w1 option.
;
; The -x option is used to specify character translations.
;
; The unix version of txt sets scripts to executable, equivalent to chmod +x
;
; RETURNS
;
; no return code
;
; SEE ALSO
;
; xd -t, tabs, type, Spit
;
; SAMPLE OUTPUT
;
; >txt *.h
;
; HUFF.H: Translating, 163 lines, done.
; FILLMAC.H: Translating, 97 lines, done.
; MACPS.H: Translating, 16 lines, done.
; FILL.H: Translating, 102 lines, done.
; FILLCUR.H: Translating, 18 lines, done.
; CEL2SHPI.H: Translating, 2 lines, done.
; FILLSMGR.H: Translating, 14 lines, done.
; SPIT.H: Translating, 169 lines, done.
; XI.H: Translating, 29 lines, done.
; IMG2SHPI.H: Translating, 17 lines, done.
; MACHINE.H: Translating, 470 lines, done.
; VSH2SHPI.H: Translating, 10 lines, done.
; SHP2CELI.H: Translating, 21 lines, done.
; EMM.H: Translating, 14 lines, done.
; AZM.H: Translating, 1043 lines, done.
; LUMPY.H: Translating, 70 lines, done.
;
; END ABSTRACT
;
*/

/****************************************************************/
/*  SPIT Routines                                               */
/****************************************************************/

void SPIT_reset(void)
{
    OPTxlate = NULL;
    OPTxlatereverse = 0;
    OPTinfo = 0;
    OPTsmart = 2;
    OPTnocr = 0;
    OPTjoin = 0;
/*    OPTverbose = -1; */

#if defined(__WATCOMC__) || defined(_MSC_VER)
    OPTnewlinetype = 0;   /* crlf */
#elif defined(applec) || defined(powerc) || defined (__powerc)
    OPTnewlinetype = 1;   /* cr */
#else
    OPTnewlinetype = 2;   /* lf */
#endif
    OPTtolower = 0;
    OPTopera = 0;
    OPTgcc = 0;
    OPTbanner = 0;
    OPTunsmart = 0;
    unweb = 0;

}

void SPIT_usage(const char *prgname,const char *envname,const char *urlname, int utype)
{
    printf("Text v%s by FrANK Barchard, EAC\n",TextVersion);
    if (utype)
        printf("USAGE: %s -options [files]\n", prgname);

    switch (utype)
    {
        case 2:
            puts("   -pc convert to pc text     -unix convert to unix (SGI)");
            puts("  -mac convert to mac text   -opera add a space at end of line");
            puts("  -gcc add nl to end of text -unsmart replace smart quotes with single quotes");
            puts("    -i info/stats          -tolower lower case file names");
            puts("    -t type (0 PC,1 Mac,2 SGI,3 Opera) -s smart translate on extension");
            puts("    -w write file                -v verbose");
            puts("    -x translate characters      -r recurse subdirectories");
            puts("     @ response file              = output file");
          printf("type '%s -h' for more help\n", prgname);
          printf("type '%s -help' for web page (%s)\n", prgname,urlname);
            break;

        case 3:
            puts("");
          printf("VERSION: %s v%s, Spit v%.2f\n",SPIT_MACHINE,TextVersion,(float)SPIT_VERSION/100.0f);
            puts("SUMMARY: Text convert text file cr/lf styles for PC,MAC & SGI");
          printf("         SPIT environment variable is %s\n",envname);
            puts("");
            puts("    @file     response file, containing a list of options and files");
          printf("    =file     specify a output file ie %s source=dest\n", prgname);
            puts("    -pc       PC type crlf (also -ibm,-pc,-dos or -crlf)");
            puts("    -mac      Mac type cr (also -mpw,-ppc or -cr)");
            puts("    -unix     Unix type lf (also -sgi, -linux or -lf)");
            puts("    -opera    Opera likes you to a add a space at end of line");
            puts("    -gcc      GCC Unix likes you to a add a NL at end of file");
            puts("    -t0       PC type crlf");
            puts("    -t1       Mac type cr");
            puts("    -t2       SGI type lf");
            puts("    -nologo   disable banner");
            puts("    -i0       disable info/stats");
            puts("    -i1       enable info/stats");
            puts("    -join<n>  join N lines into one, seperating by comma");
            puts("    -tolower  lower case filenames");
            puts("    -r        recurse subdirectories");
            puts("    -s0       disable smartness - translate all files");
            puts("    -s1       smart translate only known file types based on extension");
            puts("    -s2       smart + translate extensionless(default)");
            puts("    -unsmart  replace smart quotes with single quotes");
            puts("    -v0       no console output");
            puts("    -v1       prints source file name and errors");
            puts("    -v2       status line for each file (default)");
            puts("    -v3       additional info");
            puts("    -w0       don't output file");
            puts("    -w1       output to file (default)");
            puts("    -xl       translate to lower case");
            //puts("    -xdw      translate DOS ASCII to Windows");
            //puts("    -xwd      translate Windows ASCII to DOS");

        default:
            break;
    }
}

char *SPIT_opt(const char *optptr)
{
    int i;
    const char *d=NULL;
    char optbuf[SPITMAXLINE];

    for (i=0; (unsigned char)optptr[i]>(unsigned char)' ' && i<SPITMAXLINE; ++i)
        optbuf[i] = (char) tolower(optptr[i]);
    optbuf[i] = 0;

    if (!stricmp(optbuf,"pc") || !stricmp(optbuf,"ibm") || !stricmp(optbuf,"dos") || !stricmp(optbuf,"pc") || !stricmp(optbuf,"crlf"))
    {
        OPTnewlinetype = 0;
        d = (char *)optptr+i;
    }
    else if (!stricmp(optbuf,"mac") || !stricmp(optbuf,"mpw") || !stricmp(optbuf,"ppc") || !stricmp(optbuf,"cr"))
    {
        OPTnewlinetype = 1;
        d = (char *)optptr+i;
    }
    else if (!stricmp(optbuf,"sgi") || !stricmp(optbuf,"linux") || !stricmp(optbuf,"unix") || !stricmp(optbuf,"lf"))
    {
        OPTnewlinetype = 2;
        d = (char *)optptr+i;
    }
    else if (!stricmp(optbuf,"nocr"))
    {
        d = SPIT_getopt( &OPTnocr,optptr+4, "Kill CRs", 0, 1);
    }

    if (!d)
    {
        switch (*optptr|(char)' ')
        {
            case 'i':
                d = SPIT_getopt( &OPTinfo,optptr+1, "info", 0, 16);
                break;

            case 'j':
                if (!strnicmp(optptr,"join",4))
                    d = SPIT_getopt( &OPTjoin,optptr+4, "join lines", 0, 10000000);
                break;

            case 'g':
                if (!strnicmp(optptr,"gcc",3))
                    d = SPIT_getopt( &OPTgcc,optptr+3, "gcc nl at end of text", 0, 1);
                break;


            case 's':
                d = SPIT_getopt( &OPTsmart,optptr+1, "smart", 0, 2);
                break;

            case 't':
                if (!strnicmp(optptr, "tolower", 7))
                {
                    d = SPIT_getopt( &OPTtolower,optptr+7, "name to lower case", 0, 1);
                }
                else
                {
                    d = SPIT_getopt( &OPTnewlinetype,optptr+1, "cr type", 0, 3);
                }
                break;

            case 'l':
            case 'n':
                d = SPIT_getopt( &OPTtolower,optptr+1, "name to lower case", 0, 1);
                break;

            case 'o':
                if (!strnicmp(optptr,"opera",5))
                    d = SPIT_getopt( &OPTopera,optptr+5, "opera add space at eol", 0, 1);
                break;

            case 'u':
                if (!strnicmp(optptr, "unsmart", 7))
                {
                    d = SPIT_getopt( &OPTunsmart,optptr+7, "convert smart quotes to single quotes", 0, 1);
                }
                break;

            /* Translation options */
            case 'x':
                if ((optptr[1]|(char)' ') == 'l')
                {
                    /* Convert DOS ASCII to Windows */
                    OPTxlate = Xtolower;
                    OPTxlatereverse = 0;
                    d = (char *)optptr+2;
                }
                else if (((optptr[1]|(char)' ') == 'd') &&
                    ((optptr[2]|(char)' ') == 'w'))
                {
                    /* Convert DOS ASCII to Windows */
                    OPTxlate = Xdostowindows;
                    OPTxlatereverse = 0;
                    d = (char *)optptr+3;
                }
                else if (((optptr[1]|(char)' ') == 'w') &&
                         ((optptr[2]|(char)' ') == 'd'))
                {
                    /* Convert Windows ASCII to DOS */
                    OPTxlate = Xdostowindows;
                    OPTxlatereverse = 1;
                    d = (char *)optptr+3;
                }
                break;

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

int main(int argc,char **argv)
{
    int more;
    int i;
    int ok;
    int numberoflines;
    int numberofbytes;
    int totalnumberoflines;
    int totalnumberofbytes;
    char *transtype;
    GSTREAM *infile=0;
    GSTREAM *outfile=0;

    SPIT_init(SPIT_reset, SPIT_usage, SPIT_opt, SPIT_history);
    do
    {
        more = SPIT_parse(argc, argv, TOOL_NAME, TOOL_VERSION);

        switch (OPTnewlinetype)
        {
            default:;
            case 0: transtype = "PC";    break;
            case 1: transtype = "Mac";   break;
            case 2: transtype = "Unix";  break;
        }

        totalnumberoflines = 0;
        totalnumberofbytes = 0;
        for (i=0; i<SPITargnum; ++i)
        {
            SPIT_openstr(SPITsargv[i]);
            SPIT_flush();

            infile = gopen(SPITsargv[i]);
            if (!infile)
            {
                if (OPTverbose>=2)
                    printf("txt - COULD NOT OPEN FILE FOR READ '%s'\n",SPITsargv[i]);
            }
            else
            {
                char outname[SPITMAXLINE];

                ok = 1;
                if (OPTwrite)
                {
                    if (OPTtolower)
                        strlwr(SPITdargv[i]);

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

                    outfile = gwopen(outname);
                    if (!outfile)
                    {
                        if (OPTverbose>=2)
                            printf("txt - COULD NOT OPEN FILE FOR WRITE '%s'\n",outname);
                        gclose(infile);
                        ok = 0;
                    }
                }
                else
                    outfile = 0;

                if (ok)
                {
                    numberoflines = 0;
                    numberofbytes = 0;
                    if (checkdotranslation(SPITsargv[i],SPITdargv[i],SPITargnum,OPTsmart))
                    {
                        if (OPTverbose>=3)
                        {   printf("Translating to %s",transtype);
                            if (OPTopera)
                                printf(" Opera");
                            if (OPTgcc)
                                printf(" gcc");
                            SPIT_flush();
                        }

                        numberoflines = text(infile,outfile,OPTnewlinetype,OPTnocr,OPTjoin);
                    }
                    else
                    {
                        if (OPTverbose>=3)
                        {   printf("Copying");
                            SPIT_flush();
                        }
                        numberofbytes = txtcopy(infile,outfile);
                    }

                    totalnumberoflines += numberoflines;
                    totalnumberofbytes += numberofbytes;


                    gclose(infile);
                    if (OPTwrite && outfile)
                    {
                        gclose(outfile);
#if defined(applec) || defined(powerc) || defined (__powerc)
                        {
                            int type;

                            type = findtype(SPITsargv[i]);
                            fsetfileinfo(SPITdargv[i],filetypes[type].creator,filetypes[type].type);
                        }
#endif
                        remove(SPITdargv[i]);
                        rename(outname,SPITdargv[i]);

#if defined(__unix__)
                        if (OPTnewlinetype == 2)  /* unix script? */
                        {
                            int type;
                            char *ext;
                            char buf[512];

                            /* if not extension, assume its a unix script or executable and make it executable */

                            ext = strrchr(SPIT_getname(buf,SPITsargv[i]),'.');
                            if (!ext || !strcmp(ext,".tcl") || !strcmp(ext,".py"))
                            {
                                if (OPTverbose>=3)
                                    printf(" script");
                                sprintf(buf,"chmod +x %s",SPITdargv[i]);
                                system(buf);
                            }
                        }
#endif
                    }

                    if (OPTverbose>=3)
                    {
                        printf(", ");
                        if (numberofbytes)
                           printf("%ld bytes, done.\n",numberofbytes);
                        else
                           printf("%d lines, done.\n",numberoflines);
                        SPIT_flush();
                    }
                    if (OPTverbose>=5)
                        printf("renamed %s to %s\n",outname,SPITdargv[i]);
                }
            }
        }
        if (OPTverbose && OPTinfo)
        {   printf("total %*d files, %*d text lines, %*d binary bytes\n",OPTinfo,SPITargnum,OPTinfo,totalnumberoflines,OPTinfo,totalnumberofbytes);
            SPIT_flush();
        }
    } while (more > 0);
    SPIT_restore();

    return(0);
}

