Paste: reverse a file

Author: crest
Mode: c
Date: Wed, 16 Dec 2009 15:18:07
Plain Text |
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h> // Only BSD derivates define these (including Solaris as tested on pierre)
#include <sys/mman.h>
#include <sys/stat.h>

#define OUTPUT_BUFFER_SIZE 8192

const char usage[]                  = "Usage: kopfstand [<source>] [<target>]\n"
									  "       <source> or <target> may be \"-\n for stdin or stdout\n";
const char failed_to_open_input[]   = "Failed to open input";
const char failed_to_stat_input[]   = "Failed to stat input";
const char failed_to_mmap_input[]   = "Failed to mmap input";
const char failed_to_unmap_input[]  = "Failed to unmap input";
const char failed_to_open_output[]  = "Failed to open output";
const char failed_to_write_output[] = "Failed to write output";

#ifndef EX_USAGE

#define EX_USAGE   EXIT_FAILURE
#define EX_NOINPUT EXIT_FAILURE
#define EX_IOERR   EXIT_FAILURE

#endif

#define EVER 23

int main
(
	int			  argc,
	const char ** argv
) {
	int         input_fd;
	int         output_fd;
	ssize_t     output_index;
	uint8_t     output_buffer[OUTPUT_BUFFER_SIZE];
	uintptr_t   input_index;
	struct stat input_stat;
	off_t       input_size;
	
	
	if ( argc > 3 ) // to many command line parameters
	{
		fputs( usage, stderr );
		return EX_USAGE;
	}
	
	// if there is a source other than stdin open it
	if ( argc > 1 && strcmp( "-", argv[1] ) )
	{
		close( 0 );
		input_fd = open( argv[1], O_RDONLY );
		if ( input_fd == -1 )
		{
			perror( failed_to_open_input );
			return EX_NOINPUT;
		}
		assert( input_fd == 0 );
	}
	else
	{
		input_fd = 0;
	}
	
	// mmap need's to know the input size
	if ( fstat( input_fd, &input_stat ) )
	{
		perror( failed_to_stat_input );
		return EX_IOERR;
	}
	input_size = input_stat.st_size;
	assert( input_size >= 0 ); // off_t is signed 
	
	// try to map the complete input this can fail if
	// - the input_fd refences to a filetype which couldn't be mmapped
	// - the file is to big for your crappy operating system
	//  - get a 64 bit cpu & os, try again
	uint8_t * input_map = mmap( NULL, input_size, PROT_READ, MAP_PRIVATE, input_fd, 0 );
	if ( (intptr_t)input_map == -1 )
	{
		perror( failed_to_mmap_input );
		return EX_IOERR;
	}
	
	// if there is a destination other than stdout open it
	if ( argc > 2 && strcmp( "-", argv[2] ) )
	{
		close( 1 );
		output_fd = open( argv[2], O_CREAT | O_WRONLY | O_TRUNC );
		if ( input_fd == -1 ) {
			perror( failed_to_open_output );
			if ( munmap( input_map, input_size ) )
			{
				perror( failed_to_unmap_input );
			}
		}
		assert( output_fd == 1 );
	}
	else
	{
		output_fd = 1;
	}
	
	// while there is input left
	for ( input_index = input_size; input_index ; )
	{
		ssize_t delta, written = 0;
		
		// reverse one block of at most OUTPUT_BUFFER_SIZE bytes into output_buffer
		for ( output_index = 0; output_index < sizeof(output_buffer) && input_index; )
			output_buffer[output_index++] = input_map[--input_index];
		
		// c can be either correct or readable
		do {
			// output_fd may be a non blocking socket (worst case).
			delta = write( output_fd, output_buffer, output_index );
			if ( delta < 0 ) // write failed. kill you're self.
			{
				perror( failed_to_write_output );
				if ( munmap( output_buffer, input_size ) )
				{
					perror( failed_to_unmap_input );
				}
				return EX_IOERR;
			}
			written += delta;
		} while ( output_index != written );
	}

    return EXIT_SUCCESS;
}

New Annotation

Summary:
Author:
Mode:
Body: