/******************************************************************************
***
** 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.