#ifdef	ournix
#include "ournix.h"
#endif
char sccsID[] =
 "@(#) rev.c V1.2 Copyright Julian H. Stacey, Munich, 16th June 1988\n" ;
/* V1.3 2014-10-12 to fix compile errors */

/*	
|	Function:	Reverses characters within lines.
|			Works on multiple files, write result back to file,
|			Works as a pipe if no arguments.
|	Later:		Add a temp directory flag. Add mktemp()
|	Return Status:	0 if no errors, else number of files that failed.
|	Status:		Seems to work, not rigorously tested,
|			but by 2011 , 13 years on I've not noticed problems.
|	Bugs:		Says line is too long if last line does not end with \n
|Incompatabilities:	Output of BSD utility of named files is to stdout, 
|			this utility isnt.
*/

#include	<stdio.h>
#if ( MSDOS || __OpenBSD__ )
#include	<fcntl.h>
#endif
#ifdef  i386    /* { */
#include <sys/fcntl.h>
#endif          /* } */
#include	<sys/types.h>
#include	<sys/stat.h>
#ifdef ns32000	/* { */
#define BSD
#endif		/* } */
#ifdef	BSD	/* { */
#include	<sys/file.h>
#endif		/* } */

#ifdef	__FreeBSD__	/* { */
/*
cpp -dM /dev/null | grep FreeBSD
#define __FreeBSD_cc_version 900001
#define __VERSION__ "4.2.1 20070831 patched [FreeBSD]"
#define __FreeBSD__ 9
*/
#include 	<stdlib.h>		/* For exit() on FreeBSD-9.2 */
// extern   void exit(int status);
#include <string.h>
#include <fcntl.h>
#endif		/* } */

#define	BUFSIZE	4096
#define	END_CH	'\n'

char	**ARGV ;
int	error_count = 0 ;
int	src_fd ;
int	new_fd ;

char	buf_in[BUFSIZE + 1] ;
char	buf_out[BUFSIZE + 1] ;

extern int errno ;

#ifdef scs	/* { */
extern char *mktemp() ;
#define TMP_NAME "reXXXXXX"
#else		/* }{ */
#include <unistd.h>
#ifdef MSDOS	/* { */
#define TMP_NAME "reXXXXXX"
#else		/* }{ */
#define TMP_NAME "rev.XXXXXX"
#endif		/* } */
#endif		/* } */
#include <sys/param.h>	/* for MAXPATHLEN */
#ifdef sco	/* this sco may come from imake */
#define MAXPATHLEN PATHSIZE
#endif
char	tmp_name[MAXPATHLEN] ;		/* temporary file name */

void perror() ;

/* Output chars starting from top_p, decrementing down to base_p inclusive 
return 0 == ok , 1 == error*/
rev_line(base_p,top_p)
	char *base_p, *top_p;
	{
	unsigned	len ;
	char	*current_p ;
	for (current_p = buf_out ; (top_p >= base_p); *current_p++ = *top_p--);
	*current_p++ = END_CH ;
	*current_p = '\0' ;
	len = strlen(buf_out) ;
	if (write(new_fd,buf_out,(int)len) < len)
		{
		fprintf(stderr,"%s: Failed to write %s.\n",*ARGV,tmp_name);
		(void)close(src_fd);
		(void)close(new_fd);
		(void)unlink(tmp_name);
		return(1) ;	/* dont exit, as error could have been because
				we couldnt create a large temp. file, & next 
				argument may require a smaller temp file */
		}
	return(0) ;
	}

/* Strategy: Reads input in large blocks, Writes output one line at a time. */
	int
do_file(src_name)
	char	*src_name ;
	{
	char	*base_p, *top_p ;
	char	*tmp_p ;
	int	byte_count ;
	unsigned remnant = 0 ;	/* number of bytes left over from last
						block read */
	if (src_name == (char *)0)
		{ /* no args ; pipe operation, standard input to output */
		src_fd = 0 ;	/* stdin */
		new_fd = 1 ;	/* stdout */
		}
	else	{
		/* now create a name in the same directory as the source,
			so we can guarantee no linking across devices 
			will be attempted */
		(void)strcpy(tmp_name,src_name) ;
		/* to end of string */
		for ( tmp_p = tmp_name ; (*tmp_p++ != '\0') ; ) ;
		for ( tmp_p -= 1 ; (*--tmp_p != '/')
#ifdef	MSDOS
				&& ( *tmp_p != '\\' )
#endif
				&& ( tmp_p >= tmp_name ) ;
			) ;
		(void)strcpy(tmp_p + 1,TMP_NAME);
		if (NULL == mktemp(tmp_name))
			{
			fprintf(stderr,
			"%s: Program error, mktemp %s failed, exiting.\n",
				*ARGV,tmp_name);
			exit(++error_count);
			}
		(void)strcat(tmp_name,".tmp");
		if ((src_fd = open(src_name,O_RDONLY)) == -1)
			{
			fprintf(stderr,"%s: Can't open %s.\n",*ARGV,src_name) ;
			return(1) ;
			}
		if ((new_fd = open(tmp_name,O_RDWR | O_CREAT /* | O_TRUNC */
			, S_IREAD | S_IWRITE )) == -1)
			/* S_IREAD is read permission for owner */
			{
			fprintf(stderr,
				"%s: Cant open temporary file %s, exiting.\n",
				*ARGV,tmp_name) ;
			(void)close(src_fd) ;
			return(1) ;	/* dont exit, as we just be out of
					unix inodes (or msdos directory entries
					in root directory) on this device,
					next argument may be a different file
					system, so keep trying. */
			}
		}
	/* strategy: increment base_p til a \n, reverse chars up to
		next \n, repeat */
	while ( (byte_count = read(src_fd,buf_in + remnant, (int)
		(BUFSIZE - remnant) )) > 0) /* read blocks*/
		{	
		byte_count += remnant ;
		buf_in[byte_count] = '\0' ;
		for (base_p = top_p = buf_in ; (top_p < buf_in + byte_count) ; )
			{
			if (*top_p == END_CH )
				{	
				if (rev_line(base_p,top_p -1)) return(1) ;
				base_p = ++top_p ;
				}
			else top_p++ ;
			}
		if (base_p == buf_in) 
			{
			buf_in[byte_count] = '\0' ;
			fprintf(stderr,"%s: File %s, Line too long:\n %s\n",
				*ARGV, src_name, buf_in ) ;
			(void)close(src_fd) ; (void)close(new_fd) ; 
			(void)unlink(tmp_name) ;
			return(1);	/* dont exit, because next file 
						might be more reasonable. */
			}
		/* now collect remnant of input buffer excluding last \n, 
			including last character in buffer, & move it down */
		if ( base_p < ( buf_in + byte_count))
			{
			(void)strcpy(buf_in,base_p);
			remnant = strlen(buf_in) ;
			}
		else remnant = 0 ;
		}

	/* Now copy temporary file back to source file */
	if (src_name != (char *)0)
		{
		(void)close(src_fd) ;
		(void)close(new_fd) ;
#ifndef	BSD	/* not necessary for BSD */
		(void)unlink(src_name) ;
#endif
		if ( rename(tmp_name,src_name) )
			/* Microsoft C Compiler Version 3 manual says arguments
			should be the opposite way round to BSD,
			but V4 compiler produces code that works OK with BSD
			parameter order - thus this seems to be a bug/
			incompatability detected with V3 manual, (& possibly
			compiler, though untested) */
			{
			fprintf(stderr,"%s: moving %s to %s failed.\n",
				*ARGV, tmp_name, src_name ) ;
			perror(*ARGV);
			exit(++error_count);	/* rename failure is serious,
				possible program error or other unexpected
				contingency, so dont just return, exit */
			}
		}
	return(0) ;
	}

	int
main(argc, argv)
	int	argc ;
	char	**argv ;
	{
	ARGV = argv ;
#ifdef	VSL	/* { */
#include	"../../include/vsl.h"
#endif		/* } */
#ifdef DEBUG
	fprintf(stderr,"%s",sccsID) ;
#endif
	while ((argc > 1) && (**++argv == '-'))
		{
		switch(*++*argv)
			{
			case 'c':	/* reverse characters within line */
				break ;
			case 'l' :
				fprintf(stderr,
	"%s: Reversing lines within file not yet implemented, exiting.\n", 
					*ARGV);
				break ;
			case '?':
				printf("Syntax: %s [-P] [-c] [-l] [files]\n",
					*ARGV);
				exit(0) ;
			case 'P': 
				printf("%s",sccsID); 
				exit(0) ;
			default : 
				fprintf(stderr,
					"%s: bad flag %c.\n",*ARGV,**argv) ;
				exit(1) ;
			}
		argc-- ; argv++ ;
		}
	if (argc == 1) exit(do_file((char *)0)) ;
	else while (--argc > 0) if (do_file(*argv++)) error_count++ ;
	exit(error_count) ;
	}

