home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
High Voltage Shareware
/
high1.zip
/
high1
/
DIR41
/
CSP0194B.ZIP
/
CSPLIT.C
next >
Wrap
C/C++ Source or Header
|
1994-01-17
|
24KB
|
830 lines
/*
* CSplit.C
* This program is used to process source files for the purpose
* of transmission through a FidoNet (tm) echo in such a manner
* as to circumvent over-size message problems.
*
* This process is specified as follows:
*
* 1) a. Combine multiple source files
* b. Split into 50-60 line sections
* c. Add header and trailer markers
* delimiting each section and file
* 2) a. Delete trailing whitespace
* b. Replace tabs with spaces
* c. Default to 4 spaces per tab
* d. Allow user to specify alternate
* number of spaces per tab
* 3) a. Wrap lines longer than 75 characters
* long using the C "\ at the end of a
* line" notation (using space break).
* b. Wrapped lines will be followed by a
* line with a single "\" character then
* followed by the remainder of the line.
* 4) a. Calculate a CRC for each section and
* include in the section trailer line
* 5) a. Provide a help display for program usage when
* the program is executed without parameters
*
* Syntax:
*
* Extract: CSPLIT /x infile
*
* Split: CSPLIT [/tn] outfile src.ext [ ... ]
*
* Where: n - For TAB expansion, the number of spaces to
* replace each TAB character (the default is 4)
* infile - Name of file that contains the parts properly
* placed in consecutive order for extraction
* outfile - Name of the output file(s). The extension will
* be ignored if specified and each output file will
* be named such that the section number will be the
* extension (e.g., outfile.001, outfile.002, etc..)
* src.ext - The first source file..etc Wildcard filespecs are
* supported under non-ANSI compiling conditions.
* Filenames are limited to "8.3" string lengths to
* avoid separator line length problems.
*
* 08/31/93 Fred Cole Original draft
* 09/05/93 FC Added CRC calculation, fixed line wrap problem,
* added extraction ability, fixed a couple of bugs
* 09/14/93 FC Added conditional compilation directives to
* allow non-ANSI, multi-compiler, filespec support
* Squashed extract() bug
* 11/21/93 Thad Smith Test for incomplete input file on extraction.
* Remove spaces added in message transmission.
* Default to 90 lines /section.
* Fix tab expansion.
* 12/03/93 FC Fix file cleanup() bug.
* 12/09/93 Keith Campbell / TS
* Fix bug with options preceded by '-', fix
* tempfile opening bug, eliminate unused variables.
* Make sepcrc same type as crc.
* 01/02/94 david nugent / FC
* Additions for findfirst/findnext support for
* MSC6 (& 7) for OS/2 in initlist() routine
* 01/02/94 Auke Reitsma / FC
* Increased number of characters read in to prevent
* line numbers from becoming out-of-sync with input
* 01/12/94 Chad Wagner / FC
* Correction to initlist() function to eliminate
* the redundant line increment
*
* Donated to public domain
*/
#include "csplit.h"
FILE *Fin = NULL, /* necessary evils - global variables */
*Fout = NULL,
*Ftmp = NULL;
char tempfile[FNAME+1];
SLST *head = NULL,
*cur = NULL;
int main(int argc, char **argv)
{
char *ext, line[81], outfile[FNAME+1], *s;
int argndx = 1,
tab2spac = TABDEF,
lines_per_sect = LINES,
j, key, lines, curpart, maxpart, rc;
unsigned short crc;
printf("\nCSplit - (pd) 1993 Revision %s by Fred Cole\n", VERSION);
puts("This executable program is in public domain.\n");
if (('/' == argv[1][0]) || ('-' == argv[1][0]))
{
if ('x' == tolower(argv[1][1])) /* if extract option */
{
if (argc < 3)
{
disp_help();
return(HELP);
}
rc = extract(argv[2]);
cleanup();
return(rc);
}
else if ('t' == tolower(argv[1][1])) /* if tab option */
{
tab2spac = atoi(&argv[1][2]);
if ((tab2spac < TABMIN) || (tab2spac > TABMAX))
tab2spac = TABDEF;
argndx++;
}
}
if ((argc - argndx) < 2)
{
disp_help();
return(HELP);
}
if (strlen(argv[argndx]) > FNAME)
{
printf("Output filename too long: %s\n", argv[argndx]);
cleanup();
return(OUTFILE);
}
strcpy(outfile, argv[argndx]);
if (NULL == (ext = strstr(outfile, "."))) /* ignore any ext */
ext = &outfile[strlen(outfile)];
*ext = 0; /* make temp file name */
strcpy(tempfile, outfile);
strcat(tempfile, ".$$$");
if (NULL == (Ftmp = fopen(tempfile, "w+t")))
{
printf("Error creating temp file: %s\n", tempfile);
cleanup();
return(PROCESS);
}
if (NOERR != initlist(argc, argv, argndx+1))
{
cleanup();
return(MEMORY);
}
for (cur = head, lines = 0; NULL != cur; cur = cur->next)
{
if (NULL == Fin)
{
if (NULL == (Fin = fopen(cur->srcfile, "rt")))
{
printf("Error opening source file: %s\n", cur->srcfile);
cleanup();
return(INFILE);
}
rc = fprintf(Ftmp, "%s%s%s%s\n",
SEP_ID, SEP_BF, cur->srcfile, SEP_AR);
if (0 == rc)
{
puts("Error writing output\n");
cleanup();
return(WRITE);
}
lines++;
}
while (NULL != Fin)
{
/*
* The function my_fgets() is equivalent to fgets() in that it
* too reads n-1 characters or up to a newline character. This
* function additionally expands TAB characters, deletes trailing
* whitespace and will wrap lines exceeding the specified length.
*/
s = my_fgets(line, LENGTH, Fin, tab2spac);
if (NULL == s)
{
if (feof(Fin))
{
fclose(Fin);
Fin = NULL;
rc = fprintf(Ftmp, "%s%s%s%s\n",
SEP_ID, SEP_EF, cur->srcfile, SEP_AR);
if (0 == rc)
{
puts("Error writing output\n");
cleanup();
return(WRITE);
}
lines++; /* adjust line count */
}
else
{
puts("Error reading input\n");
cleanup();
return(READ);
}
} /* endif NULL string */
else
{
if (EOF == fputs(line, Ftmp))
{
puts("Error writing output\n");
cleanup();
return(WRITE);
}
lines++; /* increment line count */
} /* endif non-NULL string */
} /*endwhile*/
} /*endfor*/
if (lines < lines_per_sect) /* if only one section */
maxpart = 1;
else
{
maxpart = lines / lines_per_sect;
if (lines % lines_per_sect < 5)/* if < 5 lines in last section */
{
lines_per_sect--; /* decrement lines per section */
maxpart = lines / lines_per_sect;
}
if ((maxpart > 0) && /* why can't those go on one line? */
(lines % lines_per_sect > 0))
maxpart++; /* perform ceil function */
}
curpart = 1;
/* warn user if 1st output filename already in use */
sprintf(ext, ".%03.3d", curpart); /* make 1st output file name */
if (NULL != (Fout = fopen(outfile, "rt")))
{
key = 0;
printf("Output file already exists: %s\n", outfile);
do
{
printf("Overwrite? (y/n) ");
key = getchar();
puts("");
j = key;
while (j != '\n')
j = getchar(); /* eat all extra keystrokes */
if (('n' == key) || ('N' == key))
{
cleanup();
return(OUTFILE);
}
} while (('y' != key) && ('Y' != key));
fclose(Fout);
Fout = NULL;
}
if (NULL == freopen(tempfile, "rt", Ftmp))
{
printf("Error reopening temp file: %s\n", tempfile);
cleanup();
return(PROCESS);
}
initcrctab();
while (NULL != Ftmp)
{
if (NULL == Fout)
{
sprintf(ext, ".%03.3d", curpart); /* make output file name */
if (NULL == (Fout = fopen(outfile, "w+t")))
{
printf("Error opening output file: %s\n", outfile);
cleanup();
return(OUTFILE);
}
rc = fprintf(Fout, "%s%s%d/%d%s\n",
SEP_ID, SEP_BP, curpart, maxpart, SEP_AR);
if (0 == rc)
{
puts("Error writing output\n");
cleanup();
return(WRITE);
}
}
crc = 0;
lines = 0;
while ((lines < lines_per_sect) && (NULL != Ftmp))
{
s = fgets(line, 80, Ftmp);
if (NULL == s)
{
if (feof(Ftmp))
{
fclose(Ftmp);
Ftmp = NULL;
}
else
{
puts("Error reading input\n");
cleanup();
return(READ);
}
} /*endif NULL string*/
else
{
crc = updcrc(crc, (unsigned char *)line, strlen(line));
if (EOF == fputs(line, Fout))
{
puts("Error writing output\n");
cleanup();
return(WRITE);
}
lines++; /* increment line count */
} /*endif non-NULL string*/
} /*endwhile*/
if (0 == fprintf(Fout, "%s%s%d/%d crc: %04x %s\n",
SEP_ID, SEP_EP, curpart, maxpart, crc, SEP_AR))
{
puts("Error writing output\n");
cleanup();
return(WRITE);
}
fclose(Fout);
Fout = NULL;
curpart++;
} /*endwhile*/
cleanup();
return(NOERR);
}
/*
* cleanup() - Just a convenient way to provide housekeeping
* for all the places the code returns from main.
*/
void cleanup(void)
{
freelist();
if (NULL != Fin) fclose(Fin);
if (NULL != Fout) fclose(Fout);
if (NULL != Ftmp) fclose(Ftmp);
if (NULL != (Ftmp = fopen(tempfile, "rt")))
{
fclose(Ftmp);
remove(tempfile);
}
}
void disp_help(void)
{
puts("This utility is used to process source files for the purpose");
puts("of transmission through a FidoNet (tm) echo in such a manner");
puts("as to circumvent over-size message problems.\n");
puts("Syntax:\n");
puts("Extract: CSPLIT /x infile\n");
puts("Split: CSPLIT [/tn] outfile src.ext [ ... ]\n");
puts("Where: n - For TAB expansion, the number of spaces to");
puts(" replace each TAB character (defaults to 4)");
puts(" infile - File name that contains the parts properly");
puts(" placed in consecutive order for extraction");
puts(" outfile - Name of the output file(s). The extension");
puts(" will be the sequential section/part number");
puts(" (e.g., outfile.001, outfile.002, etc. ...)");
#if !defined(__STDC__)
puts(" src.ext - Source file(s)..etc. (w/wildcard support)");
#else
puts(" src.ext - Source file(s)..etc. (no wildcard support)");
#endif
}
int extract(char *ifn)
{
static char line[(LENGTH+1)*2], line2[(LENGTH+1)*2];
char outfile[FNAME+1], *s;
int i, in_section, key, lines,
curpart, maxpart, pos_wrap,
sepmax, seppart,
sep_id_len = strlen(SEP_ID);
unsigned short crc, sepcrc;
if (NULL == (Fin = fopen(ifn, "rt")))
{
printf("Error opening input file: %s\n", ifn);
return(INFILE);
}
crc = curpart = maxpart = lines = 0;
in_section = pos_wrap = FALSE;
Fout = NULL;
initcrctab();
*line2 = 0;
while (NULL != Fin)
{
s = fgets(line, LENGTH*2, Fin); /* increase input size */
/* Auke Reitsma */
/* TS: eliminate any added trailing spaces */
for (i=strlen(s)-1; i && line[i-1] == ' '; i--) continue;
line[i] = '\n';
line[i+1] = '\0';
lines++;
if (NULL == s)
{
if (feof(Fin)) /* end of file */
{
fclose(Fin);
Fin = NULL;
}
else
{
if (lines) printf("(line %d) ", lines);
printf("Error reading from input file: %s\n", ifn);
return(READ);
}
} /*endif NULL string*/
else /* process line */
{
if (line == (strstr(line , SEP_ID))) /* if separator line */
{
s = line + sep_id_len;
if (s == strstr(s, SEP_BF)) /* if begin file */
{
if (NULL != Fout)
{
printf("(line %d) ", lines);
puts("Error: encountered 2nd \"Begin file\" separator");
puts("before \"End file\" separator\n");
return(PROCESS);
}
s += strlen(SEP_BF);
if (1 != sscanf(s, "%s", outfile))
{
printf("(line %d) ", lines);
puts("Error reading separator line\n");
return(PROCESS);
}
if (NULL != (Fout = fopen(outfile, "rt")))
{
key = 0;
printf("Output file already exists: %s\n", outfile);
do
{
printf("Overwrite? (y/n) ");
key = getchar();
puts("");
i = key;
while (i != '\n')
i = getchar(); /* eat all extra keystrokes */
if (('n' == key) || ('N' == key))
return(OUTFILE);
} while (('y' != key) && ('Y' != key));
if (NULL == freopen(outfile, "wt", Fout))
{
printf("Error opening file for output: %s\n", outfile);
return(OUTFILE);
}
}
else
{
if (NULL == (Fout = fopen(outfile, "wt")))
{
printf("Error opening file for output: %s\n", outfile);
return(OUTFILE);
}
}
crc = updcrc(crc, (unsigned char *)line, strlen(line));
}
else if (s == strstr(s, SEP_EF)) /* if end file */
{
if (NULL == Fout)
{
printf("(line %d) ", lines);
puts("Error: encountered \"End file\" separator");
puts("before \"Begin file\" separator\n");
return(PROCESS);
}
if (fclose(Fout))
{
printf("Error closing output file: %s\n", outfile);
return(OUTFILE);
}
Fout = NULL;
crc = updcrc(crc, (unsigned char *)line, strlen(line));
}
else if (s == strstr(s, SEP_BP)) /* if begin part */
{
if (in_section)
{
printf("(line %d) ", lines);
puts("Error: encountered 2nd \"Begin part\" separator");
puts("before \"End part\" separator\n");
return(PROCESS);
}
s += strlen(SEP_BP);
if (2 != sscanf(s, "%d/%d", &seppart, &sepmax))
{
printf("(line %d) ", lines);
puts("Error reading separator line\n");
return(PROCESS);
}
if (0 == maxpart) maxpart = sepmax;
if (maxpart != sepmax)
{
printf("(line %d) ", lines);
puts("Error reading separator line\n");
return(PROCESS);
}
if (curpart+1 != seppart)
{
printf("(line %d) ", lines);
puts("Error: section(s) missing or out-of-order\n");
return(PROCESS);
}
in_section = TRUE;
curpart++;
}
else if (s == strstr(s, SEP_EP)) /* if end part */
{
if (!in_section)
{
printf("(line %d) ", lines);
puts("Error: encountered 2nd \"End part\" separator");
puts("before \"Begin part\" separator\n");
return(PROCESS);
}
s += strlen(SEP_EP);
s = strstr(s, ": ");
if (1 != sscanf(s+2, "%x", &sepcrc))
{
printf("(line %d) ", lines);
puts("Error reading separator line\n");
return(PROCESS);
}
if (crc != sepcrc)
{
printf("(line %d) ", lines);
puts("CRC error\n");
return(PROCESS);
}
crc = 0;
in_section = FALSE;
}
else
{
printf("(line %d) ", lines);
puts("Error reading separator line\n");
return(PROCESS);
}
}
else /* else process data line */
{
if (in_section) /* save only file data */
{
crc = updcrc(crc, (unsigned char *)line, (i = strlen(line)));
if (pos_wrap) /* if possible line wrap in progress */
{
if (0 == strcmp(line, "\\\n")) /* if wrapped line */
{
strcpy(line, line2);
line[strlen(line)-2] = 0; /* remove wrap EOL */
}
else
{
strcat(line2, line);
strcpy(line, line2);
}
pos_wrap = FALSE;
}
else if ('\\' == line[i-2]) /* if possible wrapped line */
{
strcpy(line2, line);
pos_wrap = TRUE;
}
if ((FALSE == pos_wrap) &&
((NULL == Fout) || (EOF == fputs(line, Fout))))
{
puts("Error writing output\n");
return( NULL == Fout ? PROCESS : WRITE );
}
}
}
} /*endif non-NULL string*/
} /*endwhile*/
/* TS: Test for incompete processing. */
if (in_section) {
printf ("Error on end of input when processing section %d of %d\n",
seppart, sepmax);
return (PROCESS);
}
if (seppart != sepmax) {
printf ("Error on end of input after processing section %d of %d\n",
seppart, sepmax);
return (PROCESS);
}
return(NOERR);
}
/*
* my_fgets() - A custom fgets() function that additionally
* expands tabs and deletes trailing whitespace.
*/
char *my_fgets(char *s, int len, FILE * fp, int tabsiz)
{
static char sbuf[2048] = "", /* big enough for many TAB chars */
*beg = sbuf;
static int wrap = FALSE;
char *e, *w, *p = s, *q;
int ch = 0, cnt = 0, i, spaces;
if (TRUE == wrap) /* if line wrap */
{
strcpy(s, "\\\n");
wrap = FALSE;
return(s);
}
while ((cnt < len-1) && ('\n' != ch))
{ /* get next char from buffer */
if (0 == (ch = *beg++)) /* if buffer empty */
{
beg = fgets(sbuf, 1024, fp); /* grab another string */
if (NULL == beg) /* if end of file */
{
beg = sbuf;
*beg = 0;
if (0 == cnt) return(NULL); /* and buffer empty */
}
else
{
w = e = &sbuf[i = strlen(sbuf)]; /* find 1st trailing ws char */
while ((w > sbuf) && (isspace(*(w-1))))
w--;
if (('\n' == *(e-1)) || /* if terminated w/newline char */
(i < (len-1))) /* or unterminated short line */
*w++ = '\n'; /* terminate with newline char */
*w = 0;
ch = *beg++;
}
} /*endif buffer empty*/
if (ch == '\t') /* if TAB character */
{ /* space to next tab stop */
/* TS: The following code has been changed to pad to the next
** tab stop, rather than insert a fixed number of spaces. */
spaces = tabsiz - ((int)(beg - sbuf) - 1) % tabsiz;
memmove(beg + spaces -1, beg, strlen(beg)+1);
for (q = beg-1; spaces > 0; spaces--)
*q++ = ' ';
ch = ' '; /* change character to space */
}
*p++ = (char) ch; /* update output string */
cnt++;
if ((cnt == len-1) && ('\n' != ch)) /* if need to wrap line */
{
beg -= 2; /* make room for "\\\n" characters */
e = beg;
p -= 2;
q = p;
/* unget characters to 1st previous space */
while ((e > sbuf) && (' ' != *(e-1)))
{
q--;
e--;
}
if (e != sbuf) /* if wrap on space char */
{ /* ( else wrap asis ) */
p = q;
beg = e;
}
*p++ = '\\'; /* terminate current line */
*p++ = '\n';
wrap = TRUE; /* flag wrap line pending */
} /*endif line wrap*/
} /*endwhile*/
*p = 0;
return(s);
}
/*
* CRC-16f.c, from Snippets. Calculate, intelligently, the CRC
* of a dataset incrementally given a buffer full at a time.
* Initialize crc to 0 for XMODEM, -1 for CCITT.
*/
/* P, the CRC polynomial, is used by XMODEM (almost CCITT).
* If you change P, you must change crctab[]'s initial value
* to what is printed by initcrctab().
*/
#define P 0x1021
#define W 16 /* number of bits in CRC: don't change it */
#define B 8 /* number of bits per char: don't change it */
unsigned short crctab[1<<B];
unsigned short updcrc( unsigned short icrc,
unsigned char *icp,
unsigned int icnt )
{
register unsigned short crc = icrc;
register unsigned char *cp = icp;
register unsigned int cnt = icnt;
while( cnt-- )
crc = (crc<<B) ^ crctab[(crc>>(W-B)) ^ *cp++];
return( crc );
}
void initcrctab()
{
register b, v, i;
for( b = 0; b <= (1<<B)-1; ++b )
{
for( v = b<<(W-B), i = B; --i >= 0; )
v = v&0x8000 ? (v<<1)^P : v<<1;
crctab[b] = v;
}
}
SLST *addlist(char *fname)
{
SLST *new = NULL;
if (strlen(fname) > FNAME)
printf("Input file argument too long: %s\n", fname);
else if (NULL == (new = (SLST *)malloc(sizeof(SLST))))
puts("Error allocating memory.\n");
else
{
strcpy(new->srcfile, fname);
new->next = NULL;
if (NULL == cur) head = new;
else cur->next = new;
}
cur = new;
return(cur);
}
void freelist(void)
{
while (NULL != head)
{
cur = head->next;
free(head);
head = cur;
}
}
/*
* This function creates a linked list of input source files.
* Wildcard specifications are accommodated when ANSI mode is
* not in effect.
*/
int initlist(int argc, char **argv, int argo)
{
int i;
for (i = argo; i < argc; i++) /* process CL arguments */
{
#if !defined(__STDC__)
int done;
DOSFileData fd;
done = FIND_FIRST(argv[i], 0x20, &fd);
if (done)
{
printf("Error with filespec: %s\n", argv[i]);
return(PROCESS);
}
while (!done)
{
if (NULL == addlist(ff_name(&fd))) /* david nugent */
return(MEMORY);
done = FIND_NEXT(&fd);
}
FIND_END(&fd); /* david nugent */
#else
if (NULL == addlist(argv[i]))
return(MEMORY);
#endif
}
return(NOERR);
}