#include #include #include #include #include #include #include #include #include #include #include #include #include "libio.h" #define uchar unsigned char #define PNAME "mwc" #define COUNT_BYTES 001 #define COUNT_CHARS 002 #define COUNT_LINES 010 #define COUNT_WORDS 020 #define DEFAULT_MODE COUNT_BYTES|COUNT_LINES|COUNT_WORDS typedef struct { unsigned long long bytes, chars, words, lines; } count_t; /* I've read enough source code to understand, that it can be not so bad. * But not enough to understand, that it must be avoided. * */ static int mode = 0; static int count( FILE_T*, count_t *); static void count_print( count_t*, const char*); static int is_space( const uchar*, int); static int is_newline( const uchar*, int); static void usage( void ) { eputs("usage: "PNAME" [clw] file...\n"); } int main( int argc, char* argv[] ) { register int c; count_t cur, tot; while(( c = getopt(argc, argv, "cmlw")) != -1 ) { switch( c ) { case 'c': mode |= COUNT_BYTES; break; case 'm': mode |= COUNT_CHARS; break; case 'l': mode |= COUNT_LINES; break; case 'w': mode |= COUNT_WORDS; break; default: usage(); return -1; } } argc -= optind; argv += optind; if( mode == 0 ) mode = DEFAULT_MODE; memset( &cur, 0, sizeof(count_t)); memset( &tot, 0, sizeof(count_t)); if( *argv ) { FILE_T *file; register int dontclose; while( *argv ) { dontclose = 1; if( strcmp( *argv, "-" ) == 0 ) { file = STDINF; dontclose = 0; } else if((file = file_open( *argv, O_RDONLY|O_LARGEFILE )) == NULL ) { eprint( "%d "PNAME": `%s`: %s\n", errno,*argv,strerror(errno)); *argv++; continue; } if( count( file, &cur ) != 0 ) { eprint( "%d "PNAME": `%s`: %s\n", errno,*argv,strerror(errno)); } count_print( &cur, *argv ); file_fresh(file); if( dontclose && file_close( file ) != 0 ) { eprint( "%d "PNAME": `%s`: %s\n", errno,*argv,strerror(errno)); } if( argc > 1 ) { tot.bytes += cur.bytes; tot.chars += cur.chars; tot.words += cur.words; tot.lines += cur.lines; } *argv++; memset( &cur, 0, sizeof(count_t)); } if( argc > 1 ) count_print( &tot, "total"); } else { if( count( STDINF, &cur) != 0 ) { eprint( "%d "PNAME": `%s`: %s\n", errno,*argv,strerror(errno)); return -1; } count_print( &cur, "" ); } return 0; } /* Need atomic? */ static void count_print( count_t *cnt, const char* name ) { if( mode & COUNT_LINES ) { print( " %7llu ", cnt->lines ); } if( mode & COUNT_WORDS ) { print( " %7llu ", cnt->words ); } if( mode & COUNT_BYTES ) { print( " %7llu ", cnt->bytes ); } if( mode & COUNT_CHARS ) { print( " %7llu ", cnt->chars ); } print("%s\n", name); } static int count( FILE_T *file, count_t *cnt ) { register int bflag = mode&COUNT_BYTES, cflag = mode&COUNT_CHARS, wflag = mode&COUNT_WORDS, lflag = mode&COUNT_LINES, /* fd,*/ chr, tmp, prev = 1; char buf[MB_CUR_MAX]; /* fd = file_fd(file); if( bflag ) { struct stat st; if( fstat( fd, &st ) < 0 ) { if( errno == EOVERFLOW ) { struct stat64 st64; if( fstat64( fd, &st64) < 0 ) { return -1; } else { if(S_ISREG(st64.st_mode) ) { cnt->bytes = st64.st_size; bflag = 0; } } } else { return -1; } } else { if(S_ISREG(st.st_mode) ) { cnt->bytes = st.st_size; bflag = 0; } } } */ if( !(bflag | cflag | wflag | lflag)) return 0; while(( chr = wgetchr(file, buf)) > 0 ) { if( bflag ) cnt->bytes += chr; if( cflag ) cnt->chars++; if( wflag ) { tmp = is_space( buf, chr ); if( !tmp && prev ) { cnt->words++; } prev = tmp; } if( lflag ) { if( is_newline( buf, chr ) ) cnt->lines++; } } if( chr < -1 ) { return -1; } return 0; } static int is_space( const uchar* wnum, int size ) { wchar_t wchr; mbtowc( &wchr, (char*)wnum, size ); return iswspace(wchr); } static int is_newline( const uchar* wnum, int size ) { wchar_t wchr; mbtowc( &wchr, (char*)wnum, size ); return wchr == L'\n' ? 1 : 0; }