
// MakeDoc
// version 0.8
//
// Compresses text files into a format that is ready to export to a Pilot
// and work with Rick Bram's PilotDOC reader.
//
// Freeware
//
// ver 0.6   enforce 31 char limit on database names
// ver 0.7   change header and record0 to structs
// ver 0.7a  minor mispellings and portability issues
/**********************************************************************
 * Rick,
 *
 * I made some more changes to fix another portability problem.  It seems
 * that SOME compilers will pad a structure to a DWORD boundary when you
 * use the sizeof operator.  In particular, for the Solaris compiler, the
 * 78 byte tDocHeader structure is reported as having 80 bytes.  This shifts
 * EVERYTHING by two bytes and wreaks havoc in the generated .prc file.
 * I fixed this (look at the comments in struct tDocHeader and the DOCHEADSZ
 * definition) in the two places it occurred.
 *
 * I also fixed a spelling error in an error message.
 *
 * I also changed the usage message to say this is version 0.7a (rather than
 * 0.6).
 *
 * I also changed the return type of main() to be int and added various
 * calls to exit() as needed.  Needed for portability and correctness.
 *
 * -- Harold Bamford
 **********************************************************************/
//
// Modifications by Kadar Tanuwidjaja (ktan8@dgs.monash.edu.au)
// ver 0.8   add -q for quiet mode
//           add -p for private flag
//           add -c for category
//           add -i for info (title, private flag, category)
//           add -m for changing title, category or private flag
//           default for out-filename is in-filename.pdb (encoding mode)
//           default for title is in-filename (encoding mode)
//           default for out-filename is stdout (decoding mode)
//           modify command syntax, but still maintain backward
//               compatibiliy (see makedoc -h for more details)

#define UNIX 1
#ifdef sparc
#       ifndef UNIX
#       define UNIX 1
#       endif
#endif

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

//template<class A> A max(const A& a, const A& b) {return (a<b) ? b : a;}
#define max(a,b) ((a>b) ? a : b)

typedef unsigned char byte;
typedef unsigned long DWORD;
typedef unsigned short WORD;
#define DISP_BITS 11
#define COUNT_BITS 3

// all numbers in these structs are big-endian, MAC format
struct tDocHeader {
        char sName[32];         // 32 bytes
        DWORD dwUnknown1;       // 36   2b file attribute & 2b version
        DWORD dwTime1;          // 40   Creation Date
        DWORD dwTime2;          // 44   Modification Date
        DWORD dwTime3;          // 48   Last Backup Date
        DWORD dwLastSync;       // 52   Modication Number
        DWORD ofsSort;          // 56   AppInfo Area
        DWORD ofsCatagories;    // 60   SortInfo Area
        DWORD dwCreator;        // 64   Database Type
        DWORD dwType;           // 68   Creator ID
        DWORD dwUnknown2;       // 72   Unique ID seed
        DWORD dwUnknown3;       // 76   Next Record List ID
        WORD  wNumRecs;         // 78   Number of Records
};

// Some compilers pad structures out to DWORD boundaries so using sizeof()
// doesn't give the intended result.
#define DOCHEADSZ 78

struct tDocRecord0 {
        WORD wVersion;          // 1=plain text, 2=compressed
        WORD wSpare;
        DWORD dwStoryLen;       // in bytes, when decompressed
        WORD wNumRecs;          // text records only; equals tDocHeader.wNumRec
s-1
        WORD wRecSize;          // usually 0x1000
        DWORD dwSpare2;
};

////////////// utilities //////////////////////////////////////

WORD SwapWord21(WORD r)
{
        return (r>>8) + (r<<8);
}
WORD SwapWord12(WORD r)
{
        return r;
}
DWORD SwapLong4321(DWORD r)
{
        return  ((r>>24) & 0xFF) + (r<<24) + ((r>>8) & 0xFF00) + ((r<<8) & 0xFF
0000);
}
DWORD SwapLong1234(DWORD r)
{
        return r;
}

WORD (*SwapWord)(WORD r) = NULL;
DWORD (*SwapLong)(DWORD r) = NULL;

// copy bytes into a word and double word and see how they fall,
// then choose the appropriate swappers to make things come out
// in the right order.
int SwapChoose()
{
  union { char b[2]; WORD w; } w;
  union { char b[4]; DWORD d; } d;

  strncpy(w.b, "\1\2", 2);
  strncpy(d.b, "\1\2\3\4", 4);

  if (w.w == 0x0201)
    SwapWord = SwapWord21;
  else if (w.w == 0x0102)
    SwapWord = SwapWord12;
  else
    return 0;

  if (d.d == 0x04030201)
    SwapLong = SwapLong4321;
  else if (d.d == 0x01020304)
    SwapLong = SwapLong1234;
  else
    return 0;

  return 1;
}

// replacement for strstr() which deals with 0's in the data
byte* memfind(byte* t, int t_len, byte* m, int m_len)
{
        int i;

        for (i = t_len - m_len + 1 ; i>0; i--, t++)
                if (t[0]==m[0] && memcmp(t,m,m_len)==0)
                        return t;
        return 0;
}


/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////                                  //////////////////////
/////////////////////      tBuf class                  //////////////////////
/////////////////////                                  //////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////


struct tBuf {
        byte* buf;
        unsigned len;

        tBuf() {buf = new byte[len = 6000];};
        ~tBuf() {       if (buf) delete[] buf; }
        unsigned Len() const {  return len;     }

        unsigned RemoveBinary();
        unsigned Decompress();
        unsigned Compress();
        unsigned Issue(byte src, int& bSpace);
        unsigned DuplicateCR();
        void Clear() {delete[] buf; buf = new byte[len = 6000]; }
        void Dump() {printf("\nbuffer len=%d",len);}
};



//
// Issue()
//
// action: handle the details of writing a single
//              character to the compressed stream
//
unsigned
tBuf::Issue(byte src, int& bSpace)
{
        int iDest = len;
        byte* dest = buf;

        // if there is an outstanding space char, see if
        // we can squeeze it in with an ASCII char
        if (bSpace)
        {
                if (src>=0x40 && src<=0x7F)
                        dest[iDest++] = src ^ 0x80;
                else
                {
                        // couldn't squeeze it in, so issue the space char by i
tself
                        // most chars go out simple, except the range 1...8,0x8
0...0xFF
                        dest[iDest++] = ' ';
                        if (src<0x80 && (src==0 || src>8) )
                                dest[iDest++] = src;
                        else
                                dest[iDest++] = 1, dest[iDest++] = src;
                }
                // knock down the space flag
                bSpace = 0;
        }
        else
        {
                // check for a space char
                if (src==' ')
                        bSpace = 1;
                else
                {
                        if (src<0x80 && (src==0 || src>8))
                                dest[iDest++] = src;
                        else
                                dest[iDest++] = 1, dest[iDest++] = src;

                }
        }
        len = iDest;
        return iDest;
}

//
// Compress
//
// params:      none
//
// action:      takes the given buffer,
//                                      and compresses
//                                      the original data down into a second bu
ffer
//
// comment:     This version make heavy use of walking pointers.
//
unsigned tBuf::Compress()
{
        int i,j;
        int bSpace = 0;

        // run through the input buffer
        byte* pBuffer;          // points to the input buffer
        byte* pHit;             // points to a walking test hit; works upwards
on successive matches
        byte* pPrevHit;         // previous value of pHit
        byte* pTestHead;        // current test string
        byte* pTestTail;        // current walking pointer; one past the curren
t test buffer
        byte* pEnd;             // 1 past the end of the input buffer

        pHit = pPrevHit = pTestHead = pBuffer = buf;
        pTestTail = pTestHead+1;
        pEnd = buf + len;
//printf("pointers %x %x",pTestTail, pEnd);
//printf("\nstart compression buf len=%d",len);

        // make a dest buffer and reassign the local buffer
        buf = new byte[6000];
        len = 0;                // used to walk through the output buffer

        // loop, absorbing one more char from the input buffer on each pass
        for (; pTestHead != pEnd; pTestTail++)
        {
//printf("\npointers pTestHead %x pTestTail %x pTestHead[]=%x %x",pTestHead, pT
estTail, pTestHead[0], pTestHead[1]);
                // establish where the scan can begin
                if (pTestHead - pPrevHit > ((1<<DISP_BITS)-1))
                        pPrevHit = pTestHead - ((1<<DISP_BITS)-1);

                // scan in the previous data for a match
                pHit = memfind(pPrevHit, pTestTail - pPrevHit, pTestHead, pTest
Tail - pTestHead);

                if (pHit==0)
                        printf("!! bug source %x%x%x, dest %x%x%x, %d bytes",
pPrevHit[0],
                                pPrevHit[1],pPrevHit[2],pTestHead[0],
        pTestHead[1],   pTestHead[2],   pTestTail-pTestHead);

                // on a mismatch or end of buffer, issued codes
                if (pHit==0
                        || pHit==pTestHead
                        || pTestTail-pTestHead>(1<<COUNT_BITS)+2
                        || pTestTail==pEnd)
                {
                        // issued the codes
                        // first, check for short runs
                        if (pTestTail-pTestHead < 4)
                        {
//printf("\nissue a char %x",pTestHead[0]);
                                Issue(pTestHead[0], bSpace);
                                pTestHead++;
                        }
                        // for longer runs, issue a run-code
                        else
                        {
                                // issue space char if required
                                if (bSpace) buf[len++] = ' ', bSpace = 0;

                                unsigned int dist = pTestHead - pPrevHit;
                                unsigned int compound = (dist << COUNT_BITS) +
pTestTail-pTestHead - 4;

if (dist>=(1<<DISP_BITS)) printf("\n!! error dist overflow");
if (pTestTail-pTestHead-4>7) printf("\n!! error dist overflow");

                                buf[len++] = 0x80 + (compound>>8);
                                buf[len++] = compound & 0xFF;
//printf("\nissuing code for sequence len %d <%c%c%c>",pTestTail-pTestHead-1,pT
estHead[0],pTestHead[1],pTestHead[2]);
//printf("\n          <%x%x>",pOut[-2],pOut[-1]);
                                // and start again
                                pTestHead = pTestTail-1;
                        }
                        // start the search again
                        pPrevHit = pBuffer;
                }
                // got a match
                else
                {
                        pPrevHit = pHit;
                }
//printf("pointers %x %x %x",pTestHead, pTestTail, pPrevHit);
                // when we get to the end of the buffer, don't inc past the end
                // this forces the residue chars out one at a time
                if (pTestTail==pEnd) pTestTail--;
        }

        // clean up any dangling spaces
        if (bSpace) buf[len++] = ' ';


        // final scan to merge consecutive high chars together
        int k;
        for (i=k=0; i<len; i++,k++)
        {
                buf[k] = buf[i];
                // skip the run-length codes
                if (buf[k]>=0x80 && buf[k]<0xC0)
                        buf[++k] = buf[++i];
                // if we hit a high char marker, look ahead for another
                else if (buf[k]==1)
                {
                        buf[k+1] = buf[i+1];
                        while (i+2<len && buf[i+2]==1 && buf[k]<8)
                        {
                                buf[k]++;
                                buf[k+buf[k]] = buf[i+3];
                                i+=2;
                        }
                        k += buf[k]; i++;
                }
        }

        // delete original buffer
        delete[] pBuffer;
        len = k;

        return k;
}
/*
        Decompress

        params: none

        action: make a new buffer
                                        run through the source data
                                        check the 4 cases:
                                                0,9...7F represent self
                                                1...8           escape n chars
                                                80...bf reference earlier run
                                                c0...ff space+ASCII

*/
unsigned
tBuf::Decompress()
{
        // we "know" that all decompresses fit within 4096, right?
        byte* pOut = new byte[6000];
        byte* in_buf = buf;
        byte* out_buf = pOut;

        int i,j;
        for (j=i=0; j<len; )
        {
                unsigned int c;

                // take a char from the input buffer
                c = in_buf[j++];

                // separate the char into zones: 0, 1...8, 9...0x7F, 0x80...0xB
F, 0xC0...0xFF

                // codes 1...8 mean copy that many bytes; for accented chars &
binary
                if (c>0 && c<9)
                        while(c--) out_buf[i++] = in_buf[j++];

                // codes 0, 9...0x7F represent themselves
                else if (c<0x80)
                        out_buf[i++] = c;

                // codes 0xC0...0xFF represent "space + ascii char"
                else if (c>=0xC0)
                                out_buf[i++] = ' ', out_buf[i++] = c ^ 0x80;

                // codes 0x80...0xBf represent sequences
                else
                {
                        int m,n;
                        c <<= 8;
                        c += in_buf[j++];
                        m = (c & 0x3FFF) >> COUNT_BITS;
                        n = c & ((1<<COUNT_BITS) - 1);
                        n += 3;
                        while (n--)
                        {
                                out_buf[i] = out_buf[i-m];
                                i++;
                        }
                }
        }
        delete[] buf;
        buf = pOut;
        len = i;

        return i;
}

unsigned tBuf::DuplicateCR()
{
        byte* pBuf = new byte[2*len];

        int k,j;
        for (j=k=0; j<len; j++, k++)
        {
                pBuf[k] = buf[j];
                if (pBuf[k]==0x0A) pBuf[k++] = 0x0D, pBuf[k] = 0x0A;
        }
        delete[] buf;
        buf = pBuf;
        len = k;
        return k;
}

// this nasty little beast removes really low ASCII and 0's
// and handles the CR problem
//
// if a cr appears before a lf, then remove the cr
// if a cr appears in isolation, change to a lf
unsigned tBuf::RemoveBinary()
{
        byte* in_buf = buf;
        byte* out_buf = new byte[len];

        int k,j;
        for (j=k=0; j<len; j++,k++)
        {
                // copy each byte
                out_buf[k] = in_buf[j];

                // throw away really low ASCII
                if ((out_buf[k]>=0 && out_buf[k]<9)) k--;

                // for CR
                if (out_buf[k]==0x0D)
                {
                        // if next is LF, then drop it
                        if (j<len-1 && in_buf[j+1]==0x0A)
                                k--;
                        else // turn it into a LF
                                out_buf[k] = 0x0A;
                }
        }
        delete[] buf;
        buf = out_buf;
        len = k;
        return k;
}

void out_word(short w, FILE* fout)
{
        short m = SwapWord(w);
        fwrite(&m,2,1,fout);
}
void out_long(long d, FILE* fout)
{
        long d1 = SwapLong(d);
        fwrite(&d1,4,1,fout);
}



//////////////////////////////////////////////////
// Info Mode
//////////////////////////////////////////////////

void Info(char* src)
{
        FILE* fin;
        FILE* fout;
        fin = fopen(src,"rb");

        if (fin==0)
        {
                printf("problem opening source file %s", src);
#if UNIX
                printf("\n");
#endif
                exit(2);
        }

        // just holds the first few bytes of the file
        byte buf[0x100];
        tDocHeader head;

        fread(&head, 1, DOCHEADSZ, fin);
        if (strncmp((char *)&head.dwType, "REAd", 4) != 0
            || strncmp((char *)&head.dwCreator, "TEXt", 4) != 0)
        {
          //printf("file contains %.4s, %.4s", (char *)&head.dwCreator, (char *
)&head.dwType);
                printf("%s is not a DOC file", src);
#if UNIX
                printf("\n");
#endif
                exit(3);
        }


        WORD wPos;
        int bPrivate = 0;
        int bCategory = 0;

        // point to start of index
        fseek(fin, 0x4E + 4, SEEK_SET);
        // read the location of the first record
        fread(&wPos, 1, 1, fin);
        bPrivate = (int) (wPos & 0x10) / 16;
        bCategory = (int) wPos & 0x0F;
        printf("%s p%d c%02d",head.sName, bPrivate, bCategory);

        fclose(fin);
}


//////////////////////////////////////////////////
// Modify Mode
//////////////////////////////////////////////////

void Modify(char* src, char* title, int bQuiet, int bPrivate, int bCategory )
{
        FILE* fin;
        fin = fopen(src,"r+b");

        if (fin==0)
        {
                printf("problem opening source file %s", src);
#if UNIX
                printf("\n");
#endif
                exit(2);
        }

        // just holds the first few bytes of the file
        byte buf[0x100];
        tDocHeader head;

        fread(&head, 1, DOCHEADSZ, fin);
        if (strncmp((char *)&head.dwType, "REAd", 4) != 0
            || strncmp((char *)&head.dwCreator, "TEXt", 4) != 0)
        {
          //printf("file contains %.4s, %.4s", (char *)&head.dwCreator, (char *
)&head.dwType);
                printf("%s is not a DOC file", src);
#if UNIX
                printf("\n");
#endif
                exit(3);
        }

        if(!bQuiet) printf("Modifying %s",src);

        if ( title[0] != '\0' )
        {
          if ( strcmp(head.sName, title) == 0)
          {
            if(!bQuiet) printf("\n  current title is similar");
          }
          else
          {
            if(!bQuiet) printf("\n  changing title from %s to %s", head.sName,
title);
            sprintf(head.sName,"%.31s",title);
            head.sName[31] = 0;
          }
        }

        fseek(fin, 0, SEEK_SET);
        fwrite(&head,1,DOCHEADSZ,fin);


        WORD wNumRecs =  SwapWord(head.wNumRecs);  //  the number of records to
 follow


        WORD wPos;
        int bPrivateCurr;
        int bCategoryCurr;
        int bChange = 0;
        // point to start of index
        fseek(fin, 0x4E + 4, SEEK_SET);
        // read the location of the first record
        fread(&wPos, 1, 1, fin);
        bPrivateCurr = (int) (wPos & 0x10) / 16;
        bCategoryCurr = (int) wPos & 0x0F;

        if ( bPrivate < 2 )
        {
          if ( bPrivate == bPrivateCurr)
          {
            if(!bQuiet) printf("\n  current private flag is similar");
          }
          else
          {
            if(!bQuiet) printf("\n  changing private flag from %d to %d", bPriv
ateCurr, bPrivate);
            bChange = 1;
            wPos = wPos & 0xEF; // turn off the 5th bit
            wPos =  wPos | (bPrivate * 16);
          }
        }

        if ( bCategory < 16 )
        {
          if ( bCategory == bCategoryCurr)
          {
            if(!bQuiet) printf("\n  current category is similar");
          }
          else
          {
            if(!bQuiet) printf("\n  changing category from %d to %d", bCategory
Curr, bCategory);
            bChange = 1;
            wPos = wPos & 0xF0; // turn off the 1th to 4th bits
            wPos =  wPos | bCategory;
          }
        }

        if(bChange) for ( int i = 0; i < wNumRecs; i++)
        {
          fseek(fin, 0x4E + 4 + i*8,  SEEK_SET);
          fwrite(&wPos, 1, 1, fin);
        }

        fclose(fin);
}


//////////////////////////////////////////////////
// Decode Mode
//////////////////////////////////////////////////

void Decode(char* src, char* dest, int bQuiet, int bBinary)
{

        if ( dest[0] == '\0' )
        {
          strcpy(dest, "/dev/stdout");
          bQuiet = 1;
        }

        FILE* fin;
        FILE* fout;
        fin = fopen(src,"rb");

        if (fin==0)
        {
                printf("problem opening source file %s", src);
#if UNIX
                printf("\n");
#endif
                exit(2);
        }

        // just holds the first few bytes of the file
        byte buf[0x100];
        tDocHeader head;

        fread(&head, 1, DOCHEADSZ, fin);
        if (strncmp((char *)&head.dwType, "REAd", 4) != 0
            || strncmp((char *)&head.dwCreator, "TEXt", 4) != 0)
        {
          //printf("file contains %.4s, %.4s", (char *)&head.dwCreator, (char *
)&head.dwType);
                printf("%s is not a DOC file", src);
#if UNIX
                printf("\n");
#endif
                exit(3);
        }

        WORD bCompressed;
        DWORD dwPos;
        tDocRecord0 rec0;
        // point to start of index
        fseek(fin, 0x4E, SEEK_SET);
        // read the location of the first record
        fread(&dwPos, 4, 1, fin);
        dwPos = SwapLong(dwPos);
        fseek(fin, dwPos, SEEK_SET);
        fread(&rec0, sizeof(rec0), 1, fin);
        bCompressed = SwapWord(rec0.wVersion);
        if (bCompressed!=1 && bCompressed!=2)
                printf("WARNING: unknown file compression type: %d", bCompresse
d);
        bCompressed--;

        fout = fopen(dest,"wb");
        if (fout==0)
        {
                printf("problem opening output file %s",dest);
#if UNIX
                printf("\n");
#endif
                exit(2);
        }


        DWORD dwLen;
        fseek(fin,0,SEEK_END);
        dwLen = ftell(fin);

        WORD nRecs;
        nRecs = SwapWord(head.wNumRecs) - 1;

        // this is the main record buffer
        // it knows how to stretch to accomodate the decompress
        tBuf t;

        DWORD dwRecLen;
        for (int i=0; i<nRecs; i++)
        {
                // read the record offset
                fseek(fin, 0x56 + 8*i, SEEK_SET);
                fread(&dwPos, 4, 1, fin);
                dwPos = SwapLong(dwPos);

                // read start of next record
                fseek(fin, 0x5E + 8*i, SEEK_SET);
                fread(&dwRecLen, 4, 1, fin);
                dwRecLen = SwapLong(dwRecLen);

                // for the last, use the file len
                if (i==nRecs-1) dwRecLen = dwLen;

                dwRecLen -= dwPos;

                fseek(fin,dwPos,SEEK_SET);
                int n = fread(t.buf, 1, dwRecLen, fin);
                t.len = n;
                if(bCompressed)
                        t.Decompress();

#ifndef UNIX
                // check for CR insert
                if (!bBinary)
                        t.DuplicateCR();
#endif

                if(!bQuiet) printf("\rreconverting %s: record %d of %d",head.sN
ame,i,nRecs);

                fwrite(t.buf, 1, t.Len(), fout);
        }

        fclose(fin);
        fclose(fout);

}



//////////////////////////////////////////////////
// Encode Mode
//////////////////////////////////////////////////

void Encode(char* src, char* dest, char* title, int bQuiet, int bBinary,
            int bReport, int bCompress, int bPrivate, int bCategory)
{

        FILE* fin;
        FILE* fout;
        tDocHeader head1;

        if ( bPrivate > 1)
        {
          bPrivate = 0;
        }

        if ( bCategory > 15)
        {
          bCategory = 0;
        }

        if ( dest[0] == '\0' )
        {
          if(strrchr(src, '/'))
          {
            strcpy(dest, strrchr(src, '/')+1);
          }
          else
          {
            strcpy(dest, src);
          }
          strcat(dest, ".pdb");
        }

        if ( title[0] == '\0' )
        {
          if(strrchr(src, '/'))
          {
            strcpy(title, strrchr(src, '/')+1);
          }
          else
          {
            strcpy(title, src);
          }
        }

        unsigned long index;
        if(bPrivate)
        {
          index = 0x50000000; // private and dirty
        }
        else
        {
          index = 0x40000000; // dirty
        }
        index = index + (bCategory * 0x1000000); // set category
        index = index + 0x6F8000; // unique_id=0x6f8000

        fin = fopen(src,"rb");
        fout = fopen(dest,"wb");
        if (fin==0 || fout==0)
        {
                printf("problem opening files");
#if UNIX
                printf("\n");
#endif
                exit(2);
        }

        fseek(fin,0,SEEK_END);
        DWORD storySize = ftell(fin);
        fseek(fin,0,SEEK_SET);

        DWORD   x;
        WORD w;
        long    recSize = 4096;
        DWORD           z,numRecs;

        sprintf(head1.sName,"%.31s",title);
        head1.sName[31] = 0;
        if(!bQuiet) printf("Saving to %s as <%s>,%s%s compressed", dest, title,
                        bBinary ? " binary mode," : "",
                        bCompress ? "" : " not");

                /*LocalWrite just writes to the new file the number of bytes st
arting at the passed pointer*/

        head1.dwUnknown1 = 0;
        strncpy((char *)&head1.dwTime1, "\x06\xD1\x44\xAE", 4);
        strncpy((char *)&head1.dwTime2, "\x06\xD1\x44\xAE", 4);
        head1.dwTime3 = 0;
        head1.dwLastSync = 0;
        head1.ofsSort = 0;
        head1.ofsCatagories = 0;
        strncpy((char *)&head1.dwCreator, "TEXt", 4);   // database creator
        strncpy((char *)&head1.dwType, "REAd", 4);      // database type
        head1.dwUnknown2 = 0;
        head1.dwUnknown3 = 0;



        z = (int) (storySize/(long) recSize);
        if (((long) z * recSize) < storySize)
                z ++;

        numRecs = z;
        z ++;

        head1.wNumRecs = SwapWord(z);           //  the number of records to fo
llow
        fwrite(&head1,1,DOCHEADSZ,fout);
        x = 0x50L + (long) z * 8;

        out_long(x,fout);               // start writing the record offsets
        out_long(index,fout);
        x += 0x0010L;

        index++;
        z--;

        while(z--) {
                out_long(x,fout);               //more record offsets
                out_long(index++,fout);         // the attributes + ID's
                x += 0x1000L;
        }
        // one more word.....
        out_word(0,fout);


        tDocRecord0 rec0;
        rec0.wVersion = SwapWord(bCompress ? 2 : 1);
        rec0.wSpare = 0;
        rec0.dwStoryLen = SwapLong(storySize);
        rec0.wNumRecs = SwapWord(SwapWord(head1.wNumRecs) - 1);
        rec0.wRecSize = SwapWord(recSize);
        rec0.dwSpare2 = 0;

        fwrite(&rec0,1,sizeof(rec0),fout);

        int n = recSize;
        // dump the whole story into the new file
        int recNum = 0;
        if(!bQuiet) printf("\n");

        tBuf buf;

        while(recNum < numRecs)
                {
                long pos;
                pos = ftell(fout);
                fseek(fout, 0x56 + 8*recNum, SEEK_SET);
                if (recNum!=numRecs) out_long(pos,fout);
                fseek(fout, pos, SEEK_SET);

                int nOrg;

                buf.Clear();
                nOrg = n = fread(buf.buf,1,4096,fin);
                buf.len = n;
                if (n==0) break;

                if (!bBinary)
                        buf.RemoveBinary();
                if (bCompress)
                        buf.Compress();
                n = fwrite(buf.buf,1,buf.Len(),fout);

                if(!bQuiet) printf("\rconverting record %d of %d",recNum+1,numR
ecs);
                if (bReport && n && bCompress)
                        if(!bQuiet) printf("\noriginal %d bytes, compressed to
%d bytes, ratio: %f5.1\n",
                                nOrg, n, 100. * n / nOrg);
                recNum++;
                }

        fclose(fin);
        fclose(fout);
}



//////////////////////////////////////////////////
// Main Program
//////////////////////////////////////////////////

int
main(int argc, char** argv)
{
        int iArg = 1;
        int bEncode = 1;
        int bDecode = 0;
        int bInfo   = 0;
        int bModify = 0;
        int bBinary = 0;
        int bReport = 0;
        int bCompress = 1;
        int bPrivate = 2;
        int bCategory = 16;
        int bQuiet = 0;
        char filein[250]="";
        char fileout[250]="";
        char title[250]="";

        if ( ! SwapChoose()) {
          printf("\nfailed to select proper byte swapping algorithm");
#if UNIX
          printf("\n");
#endif
          exit(1);
        }

        while (iArg < argc)
        {
          if (argv[iArg][0] == '-')
          {
            switch(argv[iArg][1])
            {
              case 'e' :
                bEncode = 1;
                bDecode = 0;
                bInfo   = 0;
                bModify = 0;
                break;
              case 'd' :
                bEncode = 0;
                bDecode = 1;
                bInfo   = 0;
                bModify = 0;
                break;
              case 'i':
                bEncode = 0;
                bDecode = 0;
                bInfo   = 1;
                bModify = 0;
                break;
              case 'm':
                bEncode = 0;
                bDecode = 0;
                bInfo   = 0;
                bModify = 1;
                break;
              case 'b' :
                bBinary = 1;
                break;
              case 'r' :
                bReport = 1;
                break;
              case 'n' :
                bCompress = 0;
              case 'p' :
                bPrivate = 1;
                if (argv[iArg][2]=='0')
                   bPrivate = 0;
                break;
              case 'c' :
                char nb[3];
                nb[0] = argv[iArg][2];
                nb[1] = argv[iArg][3];
                nb[2] = argv[iArg][4];
                bCategory = atoi(nb);
                break;
              case 'o' :
                iArg++;
                strcpy(fileout, argv[iArg]);
                break;
              case 't' :
                iArg++;
                strcpy(title, argv[iArg]);
                break;
              case 'q' :
                bQuiet = 1;
                break;
            }
          }
          else if (filein[0] == '\0')
          {
            strcpy(filein, argv[iArg]);
          }
          else if (fileout[0] == '\0')
          {
            strcpy(fileout, argv[iArg]);
          }
          else if (title[0] == '\0')
          {
            strcpy(title, argv[iArg]);
          }
          iArg++;
        }

        if (bInfo && filein[0] != '\0')
        {
                Info(filein);
        }
        else if (bModify && filein[0] != '\0')
        {
                Modify(filein, title, bQuiet, bPrivate, bCategory);
        }
        else if (bDecode && filein[0] != '\0')
        {
                Decode(filein, fileout, bQuiet, bBinary);
        }
        else if (bEncode && filein[0] != '\0')
        {
                Encode(filein, fileout, title, bQuiet, bBinary, bReport, bCompr
ess, bPrivate, bCategory);
        }
        else
        {
           printf("MakeDoc ver 0.8\n");
           printf("\nmakedoc [-q] [-n] [-b] [-p#] [-c#] [-o <pdb-file>] [-t <pd
b-title>] <text-file>");
           printf("\nmakedoc [-q] [-n] [-b] [-p#] [-c#] <text-file> <pdb-file>
<pdb-title>");
           printf("\n   encode text file to DOC format");
           printf("\n");
           printf("\nmakedoc -d [-q] [-b] [-o <txt-file>] <pdb-file>");
           printf("\nmakedoc -d [-q] [-b] <pdb-file> <txt-file>");
           printf("\n   decode DOC file back to text file");
           printf("\n");
           printf("\nmakedoc -i <pdb-file>");
           printf("\n   print the DOC info (title, private flag, category)");
           printf("\n");
           printf("\nmakedoc -m [-q] [-p#] [-c#] [-t <pdb-title>] <pdb-file>");
           printf("\n   modify title, private flag, category");
           printf("\n");
           printf("\n   -q quiet mode");
           printf("\n   -n builds the .pdb file without compression");
           printf("\n   -b option compresses/decompresses binary");
           printf("\n   -p private, #=0 private off (default=off)");
           printf("\n   -c category, #=[0-15] category number (default=0)");
           printf("\n   -o output filename (encoding mode default=<text-file>.p
db)");
           printf("\n                      (decoding mode default=stdout)");
           printf("\n   -t title (max=31, default=<text-file>)");
#if UNIX
           printf("\n");
#endif
           exit(1);
        }
#if UNIX
        if(!bQuiet) printf("\n");
#endif
        exit(0);
}

