/*- * Copyright (C) 2007 * Terry M. Poulin. All rights reserved. * * permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */#include <err.h>#include <stdio.h>#include <stdlib.h>#include <limits.h>#include <locale.h>#include <unistd.h>struct lnpos { long nl; struct lnpos *next;};static FILE *open_file( char * );static void read_all( FILE *, int );static void read_top( FILE *, int );static void read_bottom( FILE *, int );static void clean_up( struct lnpos *);static void be_verbose( void );static void usage( void );static const char *this_progname;/* * rf - read file to standard out v1.28 */intmain( int argc, char *argv[] ) { char *erptr; int t_flag = 0, b_flag = 0, v_flag = 0; int ch, lncnt; FILE *fp; (void)setlocale( LC_ALL, "" ); this_progname = argv[0]; while ( (ch = getopt(argc, argv, "b:t:v")) != -1 ) { switch (ch) { case 'b': b_flag++; lncnt = strtol(optarg, &erptr, 10); if ( *erptr || lncnt <= 0 ) errx( 1, "Improper line count -- %s", optarg ); break; case 't': t_flag++; lncnt = strtol(optarg, &erptr, 10); if ( *erptr || lncnt <= 0 ) errx( 1, "Improper line count -- %s", optarg ); break; case 'v': v_flag++; break; case '?': default: usage(); /* NOTREACHED */ } } /* This is ugly looking but cleaner then w/o open_file(). */ if ( argv[1] == NULL ) { usage(); } else if ( (t_flag < 1) && (b_flag < 1) && (v_flag < 1) ) { fp = open_file( argv[1] ); read_all( fp, lncnt ); } else if ( (v_flag != 0) && (t_flag < 1) && (b_flag < 1) ) { fp = open_file( argv[2] ); read_all( fp, lncnt ); } else if ( t_flag > 0 ) { fp = open_file( argv[3] ); read_top( fp, lncnt ); } else if ( b_flag > 0 ) { fp = open_file( argv[3] ); read_bottom( fp, lncnt ); } else { usage(); /* NOTREACHED */ } if ( v_flag != 0 ) be_verbose(); fclose( fp ); return 0;}/* Simple fopen wrapper to keep the if...else if...else blockage from * getting even uglier. Since doing other wise would defeat the purpose of it. * open_file() halts the program if fopen failed. */static FILE*open_file( char *arg ) { FILE *fto; fto = fopen( arg, "r" ); if ( fto == NULL ) errx( 1, "File can not be opened or" " does not exist -- %s\n", arg ); return fto;}/* * print out an open file to standard output */static voidread_all( FILE *fp, int lncnt ) { while ( (lncnt = fgetc( fp )) != EOF ) { printf( "%c", lncnt ); }}/* * Read n lines from the top of the file. */static voidread_top( FILE *fp, int lncnt ) { int c = 0; int f = 0; while ( (c < lncnt) && (f != EOF ) ) { f = fgetc( fp ); printf( "%c", f ); if ( f == '\n') { c++; } }}/* * Read n lines from the bottom of the file */static voidread_bottom( FILE *fp, int lncnt ) { int hmany = lncnt; long nlnum = 0; long where; struct lnpos *root = 0; struct lnpos *cur = 0; root = malloc( sizeof(struct lnpos) ); if ( root == NULL ) err( 1, "can't init the list" ); root->next = 0; cur = root; cur->next = malloc( sizeof(struct lnpos) ); if ( cur->next == NULL ) err( 1, "can't add first node" ); cur = cur->next; /* read the file, count every '\n' and store them in a new member of * our linked list. */ while ( (lncnt = fgetc( fp )) != EOF ) { if ( lncnt == '\n' ) { nlnum++; cur->nl = ftell( fp ); cur->next = malloc( sizeof(struct lnpos) ); if ( cur->next == NULL ) err( 1, "can't store line feeds" ); cur = cur->next; } } /* Let's mark the end of the list and move to it */ cur->next = malloc( sizeof(struct lnpos) ); if ( cur->next == NULL ) err( 1, "can't terminate the list" ); cur = cur->next; cur->next = NULL; /* rewind our linked-list and seek to b_flag segments. So readall() * starts from the correct fseek offset to print till EOF. - This * keeps down on unnecessary code here ! */ cur = root; while ( hmany < nlnum ) { cur = cur->next; nlnum--; } where = fseek( fp, cur->nl, SEEK_SET ); if ( where != 0 ) err( 1, "could not seek through the file\n" ); read_all( fp, lncnt ); clean_up( root );}/* Simple destructor - walk the list and free() the memory before the * program exits. */static voidclean_up( struct lnpos *rp ) { struct lnpos *t = rp; struct lnpos *atpos = rp; while ( atpos != NULL ) { free( t ); atpos = atpos->next; t = atpos; }}static voidbe_verbose( void ) { printf("==> %s <==\n", __FILE__);}static voidusage( void ) { (void)fprintf( stderr, "usage: %s [-t count | -b count] " "[-v] [file ...]\n", this_progname ); exit( EXIT_FAILURE );}