📄 csplit.c
字号:
/* +++Date last modified: 05-Jul-1997 */
/*
* Donated to public domain
*
* Designation: CSplit
*
* Description: 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 program implements the following specifications:
*
* 1) a. Combine multiple source files.
* b. Split source into 90 line sections.
* c. Add header and trailer marker lines delimiting each
* section and file.
* 2) a. Delete any trailing whitespace.
* b. Replace tabs with spaces honoring tabstop boundaries.
* c. Default to 4 columns per tabstop.
* d. Allow user to specify alternate tabstop column number.
* 3) a. Wrap lines longer than 75 characters long using the C
* "\ at the end of a line" notation (using space break).
* b. Distinguish wrapped lines from user-continued lines by
* inserting a line with a single "\" character between the
* two lines that contain the wrapped text.
* 4) a. Calculate a CRC for each section and include it in the
* section trailer marker lines.
* 5) a. Provide a help display for program usage when the
* program is executed without parameters.
* 6) a. Provide as detailed of explanation as possible when
* an unexpected condition occurs.
* b. Attempt to continue execution for all but the most severe
* errors.
*
*
* Syntax:
*
* Split: CSPLIT [/tn] [/wn] [/ln] [/sc] outfile src.ext [ ... ]
*
|* Extract: CSPLIT /x infile [ ... ]
*
* Where: /t n - For TAB character expansion, the number of columns
* for each TAB stop. (defaults to every 4 columns)
* /w n - For width control, the column number to be used for
* breaking long lines. (the default is column 75)
* /l n - For length control, the number of lines to limit
|* each section or 0 for no split. (default is 90)
* /s c - Use 'c' as an alternate line separator character
* instead of the default separator character, '>'.
|* Ignored for extraction - matches separator found
|* in extract file. However, the extract file must
|* only use one separator character.
* infile - Input file name. An extension indicates that the
* file contains the sections in proper consecutive
|* order ready for extraction. Otherwise, infile.001,
|* infile.002, etc., will be used.
|* 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 only under non-ANSI compiling conditions.
*
|* Notes: Paths are supported for all filenames, however, the paths
|* are not preserved internally during the split operation.
|* The extraction process will therefore only create files in
|* the current directory.
*
* Revision History: ( thanks to all the contributors )
*
* 08/31/93 Fred Cole Original draft
* 09/05/93 Fred Cole Added CRC calculation and extraction ability.
* Fixed a line wrap problem.
* 09/14/93 Fred Cole Added conditional compilation directives to
* allow non-ANSI filespec support. Squashed an
* extract() function 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 Fred Cole Fixed a cleanup() function bug.
* 12/09/93 Keith Campbell / TS
* Fixed bug with options preceded by '-' and fixed
* tempfile opening bug.
* 01/02/94 David Nugent / FC
* Additions for findfirst/findnext support for
* MSC6 (& 7) and OS/2 in initlist() routine and
* portable.h header file.
* 01/02/94 Auke Reitsma / FC
* Increased number of chars 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
* redundant line increment.
*--- v2.0 --------------------------------------------------------------
* 07/23/94 Keith Campbell / FC
* Modified to not abort extraction when a CRC
* mismatch occurs - just issue warning message.
* 07/30/94 Auke Reitsma / FC
* Added multiple file extraction ability.
* 09/17/94 Keith Campbell / FC
* Added version separator lines.
* 10/28/94 Bob Stout / FC
* Added separator character, width and length
* command line options.
* 12/18/94 Fred Cole Revised code to facilitate maintenance.
* 12/27/94 Fred Cole Limited the minimum width for breaking long
* lines to column 55 since this is the length
* of the longest separator line.
* 01/15/95 David Gersic / FC
* Modified the line wrap logic to handle long
* sequences of characters lacking whitespace.
*--- v2.1 --------------------------------------------------------------
* 10/30/95 Phi Nguyen / FC
* Added file extraction messages.
* 10/31/95 Fred Cole Added ability to extract unconcatenated files.
* Added path support except for extracted files
* ( i.e., paths are not preserved internally ).
* 11/06/95 Fred Cole Corrected tabstop calculation on wrapped lines.
* 11/07/95 Fred Cole Increased max section length to SHRT_MAX lines.
* 11/08/95 Bob Stout / FC
* Disable the split logic when a 0 section length
* is specified with the /L command line option.
*--- v2.2 --------------------------------------------------------------
* 11/22/95 Fred Cole Modified logic to ignore leading whitespace added
* to separator lines ( mail reader quoting? ).
* 11/22/95 Doug Nazar (DN2) / FC
* Modified sscanf() format specifiers to correctly
* convert CRC values for 32 bit platforms.
* 11/22/95 Fred Cole Changed TRUE/FALSE enum to macros to accommodate
* systems where these identifiers already exist.
* 11/29/95 Bob Stout / FC
* Added unsigned casts to allow signed 'length' to
* be compared to SHRT_MAX on 16-bit platforms.
* 11/29/95 Doug Nazar (DN2) / FC
* Added setbuf() statement to unbuffer stdout.
* 06/02/96 Fred Cole Renamed TRUE/FALSE macros to avoid possible
* identifier conflicts.
* 06/02/96 Darin McBride / FC
* Modified logic to allow source files located on
* on another drive to be processed. This change
* assumes that ':' is not a valid character for a
* file name.
* 06/02/96 Fred Cole Corrected error in extract logic when file does
* not exist.
*/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "csplit.h"
char tempfile[MAXFSPEC + 1]; /* necessary evils - global variables */
FILE *finp = NULL;
FILE *fout = NULL;
FILE *ftmp = NULL;
SLST *head = NULL;
SLST *cur = NULL;
int main (int argc, char *argv[])
{
char sepchar = SEP_CDEF;
char outfile[MAXFSPEC + 1];
char *sptr = 0;
int argndx = 1;
int chr = 0;
int extract = B_FALSE;
int length = LENDEF;
int retc = 0;
int tabstop = TABDEF;
int width = WIDDEF;
setbuf (stdout, 0); /* DN2: buffered output fix */
printf ("\nCSplit %s (pd) 1993-1996 by Fred Cole\n", VERSION);
printf ("This executable program is public domain.\n\n");
if (1 == argc)
{
disp_help ();
return SYNTAX;
}
while (('/' == argv[argndx][0]) || ('-' == argv[argndx][0]))
{
chr = toupper (argv[argndx][1]);
switch (chr)
{
case '?':
case 'H': /* /H,/? help option */
disp_help ();
return SYNTAX;
case 'X': /* /X extract option */
extract = B_TRUE;
break;
case 'T': /* /T tab option */
tabstop = atoi (&argv[argndx][2]);
if ((tabstop < TABMIN) || (tabstop > TABMAX))
printf ("Invalid tab parameter \"%s\" (%d-%d).\n", argv[argndx], TABMIN, TABMAX);
if (tabstop < TABMIN)
tabstop = TABMIN;
else if (tabstop > TABMAX)
tabstop = TABMAX;
break;
case 'W': /* /W width option */
width = atoi (&argv[argndx][2]);
if ((width < WIDMIN) || (width > WIDMAX))
printf ("Invalid width parameter \"%s\" (%d-%d).\n", argv[argndx], WIDMIN, WIDMAX);
if (width < WIDMIN)
width = WIDMIN;
else if (width > WIDMAX)
width = WIDMAX;
break;
case 'L': /* /L length option */
if (('0' == argv[argndx][2]) && ('\0' == argv[argndx][3]))
length = 0;
else
{
length = atoi (&argv[argndx][2]);
if ((length < LENMIN) || ((unsigned)length > LENMAX))
printf ("Invalid length parameter \"%s\" (0,%d-%d).\n", argv[argndx], LENMIN, LENMAX);
if (length < LENMIN)
length = LENDEF;
else if ((unsigned)length > LENMAX)
length = LENMAX;
}
break;
case 'S': /* /S separator character option */
sscanf (&argv[argndx][2], "%c", &sepchar);
if (0 == isgraph (sepchar))
{
printf ("Invalid input parameter \"%s\".\n", argv[argndx]);
sepchar = SEP_CDEF;
}
break;
default:
printf ("Ignoring unknown input parameter \"%s\".\n", argv[argndx]);
}
++argndx;
}
if (B_TRUE == extract)
{
if (argndx == argc)
{
printf ("No file argument specified for extraction.\n");
return SYNTAX;
}
/* AR: handle multiple files; break on error */
for ( ; argndx < argc; ++argndx)
if (NOERR != (retc = extr_file (argv[argndx], sepchar)))
break;
cleanup ();
return retc;
}
if ((argc - argndx) < 2)
{
printf ("Missing input and/or output file name arguments.\n");
disp_help ();
return SYNTAX;
}
if (NULL != (sptr = strrchr (argv[argndx], '\\'))) /* ignore path */
{
if (NULL != (sptr = strchr (sptr, '.')))
*sptr = '\0'; /* truncate any ext */
}
else if (NULL != (sptr = strchr (argv[argndx], '.')))
{
*sptr = '\0'; /* truncate any ext */
}
if (strlen (argv[argndx]) > MAXFSPEC)
{
printf ("Output file name argument too long.\n");
return SYNTAX;
}
strncpy (outfile, argv[argndx], MAXFSPEC);
outfile[MAXFSPEC] = '\0'; /* ensure termination */
if (NOERR != (retc = init_list (argc, argv, ++argndx)))
{
cleanup ();
return retc;
}
retc = split_src (head, outfile, length, width, tabstop, sepchar);
cleanup ();
return retc;
}
/*
* add_list - Add a file name to linked list of files to be processed.
*/
SLST *add_list (char *fname)
{
SLST *new = NULL;
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;
}
/*
* cleanup - Just a convenient way to provide centralized housekeeping.
*/
void cleanup (void)
{
free_list ();
if (NULL != finp) fclose (finp);
if (NULL != fout) fclose (fout);
if (NULL != ftmp) fclose (ftmp);
/* Now, does it really exist? */
if (NULL != (ftmp = fopen (tempfile, "r")))
{
fclose (ftmp);
remove (tempfile);
}
}
/*
* csp_fgets - A custom fgets() function that expands
* tabs, deletes trailing whitespace and
* performs line wrapping.
*/
char *csp_fgets (char *s, int len, FILE * fp, int tabstop)
{
static char sbuf[LLENMAX * 2]; /* big enough for TAB expansion */
static char *beg = sbuf;
static int tofs = 0;
static int wrap = B_FALSE;
char *e = 0;
char *w = 0;
char *p = s;
char *q = 0;
int ch = 0;
int cnt = 0;
int i = 0;
int spaces = 0;
if (B_TRUE == wrap) /* if line wrap */
{
tofs += (int)(beg - sbuf); /* adj. TAB column offset */
strcpy (s, "\\\n");
memmove (sbuf, beg, strlen (beg) + 1); /* DG: Modification for */
beg = sbuf; /* DG: long lines w/o WS */
wrap = B_FALSE;
return s;
}
while ((cnt < len-1) && ('\n' != ch))
{ /* get next char from buffer */
if (0 == (ch = *beg++)) /* if buffer empty */
{
memset (sbuf, 0, sizeof (sbuf));
beg = fgets (sbuf, LLENMAX, 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 */
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -