/*
	Program to fix text files.
	It reads text files with lines terminated by linefeed, return,
	linefeed/return, or return/linefeed and output a file that has lines
	terminated according to the system for which this program was compiled.
	Works for Mac (MPW C and THINK C), DOS, and UNIX.

	Usage: fixeol [<file> ...]

	The file is fixed in place unless there is some error, in which
	case the original file is unchanged. If no file is specified on
	the command line, then this filters characters from stdin to stdout.

	Eric Blossom, 1995, 2004
	R. Steven Elliot, December 1991
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef THINK_C
#include <console.h>
#endif

#define CR '\015'  /* carriage return   */
#define LF '\012'  /* line feed         */
#define NL '\n'    /* portable new line */


/**
 * Fix the line endings in one stream.
 * infp must point to a stream opened for reading in binary mode.
 * outfp must pint to a stream opened for writing in normal (text) mode.
 * The input must be open in binary mode so CR/LF translation WON'T be done
 * by stdio, and the output must be open in text mode so translation WILL be done.
 */
void fixone( FILE *infp, FILE *outfp )
{
    register int ch;
    int finished = 0;
    while ( ( ch = fgetc( infp ) ) != EOF ) {
        switch ( ch ) {
        case CR:
            if ( ( ch = fgetc( infp ) ) != LF )
                ungetc( ch, infp );
            break;
        case LF:
            if ( ( ch = fgetc( infp ) ) != CR )
                ungetc( ch, infp );
            break;
        case '\0':
            continue;
        default:
            fputc( ch, outfp );
            finished = 0;
            continue;
        }
        fputc( NL, outfp );
        finished = 1;
    }
    if ( !finished ) {
        fputc( NL, outfp );
    }
}


int main( int argc, char **argv )
{
    FILE *infp, *outfp;
    char *infile, *outfile;

    /* Warnings are issued by some compilers
       when using tmpnam().
       So we try using mkstemp on systems that have it.
       However, since tmpname() is ANSI C,
       we keep it in here.
       This might allow porting to more systems.
       */

#if defined _POSIX_C_SOURCE && 199506L <= _POSIX_C_SOURCE
# define _USE_POSIX 1
# define USE_MKSTEMP
#endif
#if defined _POSIX_VERSION && 199506L <= _POSIX_VERSION
# define _USE_POSIX 1
# define USE_MKSTEMP
#endif
#if defined __MACH__ && defined __APPLE__
# define _USE_POSIX 1
# define USE_MKSTEMP
#endif
#ifdef __FreeBSD__
# define _USE_POSIX 1
# if 3 <= __FreeBSD__
#  define USE_MKSTEMP
# endif
#endif

#ifdef USE_MKSTEMP
    int mkstemp_result = -1;
    char out_template[1024];
#endif

#ifdef THINK_C
    argc = ccommand( &argv );
#endif

#if defined unix || defined __unix__ || defined __unix
# define _USE_POSIX 1
#endif

#ifdef _USE_POSIX
# define MOVE_COMMAND "mv %s %s"
#endif

#ifdef MOVE_COMMAND
    char command[1024];  /* For systems (like UNIX) where the rename might fail. */
#endif

    if ( argc < 2 ) {
        infp = freopen( NULL, "rb", stdin );
        if ( NULL == infp ) {
            perror( "fixeol" );
            fprintf( stderr, "Unable to reopen stdin for binary input.\n" );
            exit( EXIT_FAILURE );
        }
        fixone( infp, stdout );
    }

    while ( --argc > 0 ) {

        infile = *++argv;
        fprintf( stderr, "%s\n", infile );
        if ( ( infp = fopen( infile, "rb" ) ) == NULL ) {
            fprintf( stderr, "Unable to open input file \"%s\".\n",
                     infile );
            continue;
        }

#ifdef USE_MKSTEMP
        if ( strlen( infile ) < sizeof out_template  ) {
            strcpy( out_template, infile );
            strcat( out_template, "-XXXXXX" );
        }
        else {
            strcpy( out_template, "fixeol_XXXXXX" );
        }
        mkstemp_result = mkstemp( out_template ) ;
        if ( -1 == mkstemp_result || NULL == ( outfp = fdopen( mkstemp_result, "w" ) ) ) {
            perror( "fixeol" );
            fprintf( stderr, "Unable to open temporary output file.\n" );
            exit( EXIT_FAILURE );
        }
        outfile = out_template;
#else
        outfile = tmpnam( NULL );
        if ( ( outfp = fopen( outfile, "w" ) ) == NULL ) {
            fprintf( stderr, "Unable to open output file \"%s\".\n",
                     outfile );
            exit( EXIT_FAILURE );
        }
#endif

        fixone( infp, outfp );

        fclose( infp );
        fclose( outfp );
        remove( infile );
        if ( 0 != rename( outfile, infile ) ) {
            perror( "fixeol" );
            fprintf( stderr, "fixeol: Couldn't rename %s to %s.\n",
                     outfile, infile );
#ifdef MOVE_COMMAND
            /* WARNING: The following is not portable.
               It is used on UNIX systems where the rename can fail
               because the temporary directory may be on another volume.
             */
            fprintf( stderr, "fixeol: Let's try moving:  " );
            sprintf( command, MOVE_COMMAND, outfile, infile );
            fprintf( stderr, "%s\n", command );
#endif
        }
    }
    return 0;
}
