Heres a straight copy & paste of it. Portability wise it assumes that the file is in the correct format and requires a number of functions that first appeared in BSD 4.x, namely getopts, fgetln, and err/errx. It also presumes that their should be enough room to store the location of every newline in the file within memory. So if one only has 64K of RAM, it might be a problem to read a few files xD.
I can't say if its well written or not... I don't know of any program with my system that does exactly this other then what this simulates. Rather then wrap around cat/head/tail using system() or including the sources I worked through this as a learning experience. Its more or less written in the style I prefer, the type on a line of its own bit in function declarations is the only 'real' part of it stemming from my occasional reads of stuff in /usr/src alone.
/* * rf read file to standard out */ // vim: set noexpandtab ts=8 sw=4 ai : // vi: set ai nu ts=8 sw=4 : #include <err.h> #include <stdio.h> #include <stdlib.h> #include <limits.h> #include <unistd.h> static void readall( FILE *, int ); static void readtop( FILE *, int ); static int readbottom( FILE *, int ); static void usage( void ); struct lnpos { long nl; struct lnpos *next; }; int main( int argc, char *argv[] ) { char *erptr; int t_flag = 0, b_flag = 0; int ch, lncnt; FILE *fp; extern char *optarg; extern int optind; extern int optopt; extern int opterr; extern int optreset; while ( (ch = getopt(argc, argv, "b:t:")) != -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 '?': default: usage(); return 1; } argc -= optind; argv += optind; } /* loop through cli args and find a file to open */ for ( int i = 0; i <= argc; i++ ) { fp = fopen( argv[i], "r" ); if ( fp == NULL ) err( 1, "File does not exist or could not be opened " "for reading -- %s", optarg ); } if ( (t_flag < 1) && (b_flag < 1) ) { readall( fp, lncnt ); } else if ( t_flag > 0 ) { readtop( fp, lncnt ); } else if ( b_flag > 0 ) { readbottom( fp, lncnt ); } else { errx( 1, "flag processing failed" ); } fclose( fp ); return 0; } /* * print out an open file to standard output */ static void readall( FILE *fp, int lncnt ) { while ( (lncnt = fgetc( fp )) != EOF ) { printf( "%c", lncnt ); } } /* Read n lines from the top of the file. * note that it was very inspired by the head(1) implementation of BSD */ static void readtop( FILE *fp, int lncnt ) { char *cp; size_t error, rlen; while ( lncnt && (cp = fgetln( fp, &rlen )) != NULL ) { error = fwrite( cp, sizeof(char), rlen, stdout ); if ( error != rlen ) err( 1, "stdout" ); lncnt--; } } /* Read n lines from the bottom of the file * This function really should be broken up - but I have not learned how yet. */ static int readbottom( FILE *fp, int lncnt ) { char *cp; int hmany = lncnt; long nlnum = 0; long where; size_t error, rlen; struct lnpos *root; struct lnpos *cur; 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 nodes" ); 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 ); if ( cur->next != NULL ) cur = cur->next; cur->next = malloc( sizeof(struct lnpos) ); if ( cur->next == NULL ) err( 1, "can't add nodes" ); cur = cur->next; } } /* rewind our linked-list and seek b_flag + 1 segments short of the * end of the list. */ cur = root->next; hmany++; 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" ); while ( lncnt && (cp = fgetln( fp, &rlen )) != NULL ) { error = fwrite( cp, sizeof(char), rlen, stdout ); if ( error != rlen ) err( 1, "stdout" ); lncnt--; cur = cur->next; if ( cur->next == 0 ) return 0; } return 0; } static void usage( void ) { fprintf( stderr, "usage:\n rf file\n " "rf [-t number lines from the top] file\n " "rf [ -b number of lines from the bottom ] [ file ]\n" ); }
Any comments about the file it self or its design is much welcome by this moron who tries to teach him self.
No comments:
Post a Comment