fls.c source

Home
/******************************************************************************
***
**	fls 1.0 (10-Aug-2018) -  Copyright (C) 2011-2018 Dario Niedermann
**
** 	From Line Separator:
**	"Fixes" mbox files by adding a blank line before each "From " line
**	(except the 1st) that is not preceded by a blank line.
**
** 	Compile with: 'cc -O2 -o fls fls.c'
**
** 	Released with NO WARRANTY under the GPLv3 license
**	$Id: fls.c 137 2018-08-10 17:27:06Z ndr $
***
*******************************************************************************/

#include <stdio.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <assert.h>
#include <libgen.h>

#define EXIT_SUCCESS 0
#define LINESIZE 2048	// lines longer than LINESIZE are still supported,
			// they'll just be read LINESIZE bytes at a time

unsigned char	verbose = 0;

void prUsage(FILE *where, char *progName)
/* print usage info to stdout or stderr */
{
 fprintf(where, "Usage: %s [-hv] [FILE]\n",
		basename(progName));

 if (where == stderr)
	fprintf(stderr, "Try '%s -h' for more information.\n",
		basename(progName));
}

void prHelp(char *progName)
{
 prUsage(stdout, progName);
 printf("\n\
In mbox FILE, make sure every 'From line' is preceded by a blank line,\n\
adding one if needed (excepting the first line in the file).\n\
Result goes to standard output.\n\n\
Options:\n\
  -v     when done, print (to stderr) the number of blank lines added\n\
  -h     print this help text and exit\n\n");
 
 printf("If FILE is not specified, %s reads from standard input.\n\n\
Report bugs to: <dario@darioniedermann.it>\n\
fls home page : <https://www.darioniedermann.it/sw/fls.html>\n",
	basename(progName));
}

int parseCmdLine(int argc, char * argv[])
/* returns index of 1st non-option argument if all is OK,
   or a negative number that's the opposite of the intended
   exit code for main() */
{
 int opt, rc = 0;

 while (!rc && (opt = getopt(argc, argv, "vh")) != -1) {
	switch (opt) {
		case 'v': verbose++; break;
		case 'h': prHelp(argv[0]);  rc = -EX_TEMPFAIL; break;
		/* '?' */
		default: prUsage(stderr, argv[0]);  rc = -EX_USAGE;
	}
 }
 assert(optind > 0);
 return (rc < 0) ?  rc : optind;
}

int main (int argc, char * argv[])
{
 char	*fName, line[LINESIZE], thisLineEmpty = 0, prevLineEmpty = 0;
 int	 i = 0, blankLinesAdded = 0, fileArgIndex;
 FILE	*filePtr;

 if ((fileArgIndex = parseCmdLine(argc, argv)) < 0)
	// cmdline parsing returned err
	return (fileArgIndex == -EX_TEMPFAIL) ?    // user asked help with -h ?
		EXIT_SUCCESS : -fileArgIndex;
 // else...
 fName = argv[fileArgIndex];

 if (fName == NULL) {
	assert(fileArgIndex == argc);
	filePtr = stdin;
 }
 else if ((filePtr = fopen(fName, "r")) == NULL) {	// can't open file?
	fprintf(stderr, "%s: `%s' not found or unreadable\n", argv[0], fName);
 	return EX_NOINPUT;	// exit
 }

 // else...
 while (!feof(filePtr)) {
	prevLineEmpty = thisLineEmpty;	// what was 'this line' state in prev.
					// cycle, is 'previous line' now
	thisLineEmpty = 0;		// still don't know if the line we're
					// going to fetch will be empty
 	fgets(line, LINESIZE, filePtr);
 	if (strcmp(line,"\n") == 0)	// found an empty line?
 		thisLineEmpty = 1;	// take note.
 	else {				// Not an empty line?
 		thisLineEmpty = 0;
 		if ((strncmp("From ", line, 5) == 0)
			// if it's a 'From ' line
 		&& (!prevLineEmpty)
			// & previous line wasn't empty
 		&& (i > 0)) {
			// & it's not the 1st line...
 			putchar('\n');		// ...emit an empty line
			blankLinesAdded++; }
 	}
	// in any case, emit the line (except if at end of file)
	if (!feof(filePtr)) {
		fputs(line, stdout);
		i++;			// inc line counter
	}
 }
 fflush(stdout);
 if (fName != NULL) fclose(filePtr);
 if (verbose)
	fprintf(stderr,"%s: added %d blank lines\n", argv[0], blankLinesAdded);
 return EX_OK;
}
“Fls” is Copyright © Dario Niedermann — Released with no warranty under the terms of the GPLv3 license. Written and tested on Linux using GNU tools.
HomeNo Javascript iconValid HTML 4.01 StrictValid CSS!