fork download
  1. /** C99 **/
  2.  
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <ctype.h>
  7. #include <inttypes.h>
  8. #include <stdbool.h>
  9.  
  10. #define APP_NAME "mywc"
  11. #define APP_VERSION "0.01"
  12.  
  13. #define MAX_FNAMES 100+1
  14.  
  15. #define MAXLEN_OPTSSTR (2+1) /* max len for an option's short string*/
  16. #define MAXLEN_OPTLSTR (20+1) /* max len for an option's long string */
  17. #define MAXLEN_OPTDESC (128+1) /* max len for an option's description */
  18.  
  19. #define INFO_VERSION \
  20. "%s (just for fun) %s\n" \
  21. "Copyright (C) 2012 migf1\n" \
  22. "This is free software. You may redistribute copies of it under the terms of\n"\
  23. "the GNU General Public License <http://w...content-available-to-author-only...u.org/licenses/gpl.html>.\n" \
  24. "There is NO WARRANTY, to the extent permitted by law.\n" \
  25. "\n" \
  26. "Written by migf1 <mig_f1@hotmail.com>"
  27.  
  28. #define INFO_USAGE \
  29. "Usage: %s [OPTION]... [FILE]...\n" \
  30. "Print newline, word, and byte counts for each FILE, and a total line if\n" \
  31. "more than one FILE is specified. With no FILE, or when FILE is -,\n" \
  32. "read standard input."
  33.  
  34. #define INFO_REPORT_BUGS "Report bugs to <mig_f1@hotmail.com>."
  35.  
  36. typedef uint16_t Bitmap16;
  37.  
  38. enum ErrId {
  39. ERR_NOERROR = 0,
  40. ERR_INTERNAL,
  41. ERR_NOFILE,
  42. ERR_RDFILE,
  43. ERR_OPTINVALID,
  44. ERR_OPTUNRECOG,
  45. /* not an id, just their total count*/
  46. MAXERRORS
  47. };
  48.  
  49. enum OptionBit {
  50. OBIT_NONE = 0x00,
  51. OBIT_BCOUNT = (1 << 1),
  52. OBIT_CCOUNT = (1 << 2),
  53. OBIT_LCOUNT = (1 << 3),
  54. OBIT_MAXLL = (1 << 4),
  55. OBIT_WCOUNT = (1 << 5),
  56. OBIT_HELP = (1 << 6),
  57. OBIT_VERS = (1 << 7)
  58. };
  59.  
  60. enum OptionId {
  61. OID_INVALID = -1,
  62. OID_BCOUNT = 0,
  63. OID_CCOUNT,
  64. OID_LCOUNT,
  65. OID_MAXLL,
  66. OID_WCOUNT,
  67. OID_HELP,
  68. OID_VERS,
  69. /* not an id, just their total count*/
  70. MAXOPTIONS
  71. };
  72.  
  73. typedef struct Option {
  74. Bitmap16 obit;
  75. char sstr[ MAXLEN_OPTSSTR ]; /* short string */
  76. char lstr[ MAXLEN_OPTLSTR ]; /* long string */
  77. char desc[ MAXLEN_OPTDESC ]; /* description */
  78. }Option;
  79.  
  80. typedef struct Count {
  81. uintmax_t nl; /* newlines count */
  82. uintmax_t w; /* words count */
  83. uintmax_t c, b; /* chars & bytes count */
  84. uintmax_t maxlnlen; /* length of maximum line */
  85. }Count;
  86.  
  87. /*********************************************************//**
  88.  * @brief Print a different error, according to the value of errid.
  89.  *************************************************************
  90.  */
  91. void print_error( enum ErrId errid, Option options[] )
  92. {
  93. switch ( errid )
  94. {
  95. case ERR_INTERNAL:
  96. printf( "%s: Internal error!\n", APP_NAME );
  97. break;
  98.  
  99. case ERR_NOFILE:
  100. printf( "%s: No such file or directory\n", APP_NAME );
  101. break;
  102.  
  103. case ERR_RDFILE:
  104. printf( "%s: File could not be read!\n", APP_NAME );
  105. break;
  106.  
  107. case ERR_OPTINVALID:
  108. printf( "%s: invalid option\n", APP_NAME );
  109. printf( "Try `%s %s' for more information.\n",
  110. APP_NAME, options[OID_HELP].lstr );
  111. break;
  112.  
  113. case ERR_OPTUNRECOG:
  114. printf( "%s: unrecognized option\n", APP_NAME );
  115. printf( "Try `%s %s' for more information.\n",
  116. APP_NAME, options[OID_HELP].lstr );
  117. break;
  118.  
  119. case ERR_NOERROR:
  120. printf( "%s: no error\n", APP_NAME );
  121. break;
  122.  
  123. case MAXERRORS:
  124. default:
  125. break;
  126. }
  127.  
  128. putchar('\n');
  129.  
  130. return;
  131. }
  132.  
  133.  
  134. /*********************************************************//**
  135.  * @brief Print program-version information.
  136.  *************************************************************
  137.  */
  138. void print_version( void )
  139. {
  140. printf( INFO_VERSION, APP_NAME, APP_VERSION );
  141. puts("\n");
  142.  
  143. return;
  144. }
  145.  
  146. /*********************************************************//**
  147.  * @brief Print short & long aliases, and the description of all options.
  148.  *************************************************************
  149.  */
  150. void print_options( Option options[] )
  151. {
  152. for (int i=0; i < MAXOPTIONS; i++)
  153. {
  154. printf( "\t%s%s%s\t\t%s\n",
  155. options[i].sstr[0] ? options[i].sstr : " ",
  156. options[i].sstr[0] ? ", " : " ",
  157. options[i].lstr,
  158. options[i].desc );
  159. }
  160.  
  161. return;
  162. }
  163.  
  164. /*********************************************************//**
  165.  * @brief Display the help screen.
  166.  *************************************************************
  167.  */
  168. void print_help( Option options[] )
  169. {
  170. printf( INFO_USAGE, APP_NAME );
  171. putchar('\n');
  172. print_options( options );
  173. putchar('\n');
  174. puts( INFO_REPORT_BUGS );
  175. putchar('\n');
  176.  
  177. return;
  178. }
  179.  
  180. /*********************************************************//**
  181.  * @brief Print (one file's) counted elements.
  182.  *************************************************************
  183.  */
  184. bool print_counted(
  185. const Count *count,
  186. Bitmap16 obitmap,
  187. Option options[],
  188. FILE *fp,
  189. const char *fname
  190. )
  191. {
  192. if ( !count || !fp)
  193. return false;
  194.  
  195. if ( obitmap & OBIT_HELP ) {
  196. print_help( options );
  197. goto ret_ok;
  198. }
  199. if ( obitmap & OBIT_VERS ) {
  200. print_version( );
  201. goto ret_ok;
  202. }
  203.  
  204. if ( obitmap & OBIT_LCOUNT )
  205. printf( "%-"PRIuMAX"\t", count->nl );
  206. if ( obitmap & OBIT_WCOUNT )
  207. printf( "%-"PRIuMAX"\t", count->w );
  208. if ( obitmap & OBIT_CCOUNT )
  209. printf( "%-"PRIuMAX"\t", count->c );
  210.  
  211. if ( obitmap & OBIT_BCOUNT )
  212. printf( "%-"PRIuMAX"\t", count->b );
  213. if ( obitmap & OBIT_MAXLL )
  214. printf( "%-"PRIuMAX"\t", count->maxlnlen );
  215.  
  216. if ( stdin != fp && fname )
  217. puts( fname );
  218. else
  219. putchar('\n');
  220.  
  221. ret_ok:
  222. return true;
  223. }
  224.  
  225. /*********************************************************//**
  226.  * @brief Print totals (of all files).
  227.  *************************************************************
  228.  */
  229. bool print_totals( const Count *totals, Bitmap16 obitmap )
  230. {
  231. if ( !totals )
  232. goto ret_fail;
  233.  
  234. if ( obitmap & OBIT_HELP || obitmap & OBIT_VERS )
  235. goto ret_ok;
  236.  
  237. if ( obitmap & OBIT_LCOUNT )
  238. printf( "%-"PRIuMAX"\t", totals->nl );
  239. if ( obitmap & OBIT_WCOUNT )
  240. printf( "%-"PRIuMAX"\t", totals->w );
  241. if ( obitmap & OBIT_CCOUNT )
  242. printf( "%-"PRIuMAX"\t", totals->c );
  243.  
  244. if ( obitmap & OBIT_BCOUNT )
  245. printf( "%-"PRIuMAX"\t", totals->b );
  246. if ( obitmap & OBIT_MAXLL )
  247. printf( "%-"PRIuMAX"\t", totals->maxlnlen );
  248.  
  249. puts("total");
  250.  
  251. putchar('\n');
  252.  
  253. ret_ok:
  254. return true;
  255. ret_fail:
  256. return false;
  257. }
  258.  
  259. /*********************************************************//**
  260.  * @brief True if filename exists, false otherwise.
  261.  *************************************************************
  262.  */
  263. bool f_exists(const char *fname)
  264. {
  265. FILE *fp = fopen(fname, "rb");
  266. if ( !fp )
  267. return false;
  268.  
  269. fclose(fp);
  270. return true;
  271. }
  272.  
  273. /*********************************************************//**
  274.  * @brief Convert an option alias (short or long string) into an option id.
  275.  *************************************************************
  276.  */
  277. enum OptionId ostr2oid( const char *ostr, Option options[] )
  278. {
  279. enum OptionId i;
  280.  
  281. if ( !ostr || !*ostr )
  282. return OID_INVALID;
  283.  
  284. for (i=0; i < MAXOPTIONS; i++)
  285. if ( 0 == strncmp(ostr, options[i].sstr, MAXLEN_OPTSSTR)
  286. || 0 == strncmp(ostr, options[i].lstr, MAXLEN_OPTLSTR) )
  287. return i;
  288.  
  289. return OID_INVALID;
  290. }
  291.  
  292. /*********************************************************//**
  293.  * @brief Update totals (after a file's elements have been counted).
  294.  *************************************************************
  295.  */
  296. bool refresh_totals( Count *totals, const Count *count )
  297. {
  298. if ( !totals || !count )
  299. return false;
  300.  
  301. totals->nl += count->nl;
  302. totals->w += count->w;
  303. totals->b += count->b;
  304. totals->maxlnlen += count->maxlnlen;
  305.  
  306. return true;
  307. }
  308.  
  309. /*********************************************************//**
  310.  * @brief Count and store a file's elements.
  311.  *************************************************************
  312.  */
  313. bool fcount( FILE *fp, Count *count )
  314. {
  315. int c;
  316. bool onword = false;
  317. uintmax_t lnchars = 0;
  318.  
  319. if ( !fp || !count )
  320. return false;
  321.  
  322. /* ensure everyting starts zeroed */
  323. memset( count, 0, sizeof(Count) );
  324.  
  325. onword = (c=isspace( getc(fp) )) ? false : true;
  326. ungetc(c, fp);
  327. while ( (c=getc(fp)) != EOF )
  328. {
  329. if ( isspace(c) )
  330. {
  331. if ( '\n' == c ) {
  332. count->nl++;
  333. if (count->maxlnlen < --lnchars)
  334. count->maxlnlen = lnchars;
  335. lnchars = 0;
  336. if ( onword ) {
  337. count->w++;
  338. onword = false;
  339. }
  340. }
  341. else if ( onword ) {
  342. count->w++;
  343. onword = false;
  344. }
  345. }
  346. else
  347. onword = true;
  348.  
  349. if ( c != '\n')
  350. lnchars++;
  351. count->c++;
  352. }
  353.  
  354. count->b = count->c / sizeof(char);
  355.  
  356. return true;
  357. }
  358.  
  359. /*********************************************************//**
  360.  * @brief Parse the command line arguments, and update accordingly
  361.  * a) the specified options (obitmap)
  362.  * b) the total numeber of specified filenames (nfiles)
  363.  * c) the names of the files (fnames: just pointers to proper argv[i]'s)
  364.  *************************************************************
  365.  */
  366. enum ErrId parse_cmdln_args(
  367. char *argv[],
  368. Option options[],
  369. char *fnames[MAX_FNAMES],
  370. int *nfiles,
  371. Bitmap16 *obitmap
  372. )
  373. {
  374. int iarg = 1, ifname = 0;
  375. enum OptionId oid = OID_INVALID;
  376.  
  377. if ( !obitmap || !fnames || !nfiles )
  378. return ERR_INTERNAL;
  379.  
  380. /* ensure everything starts with default values */
  381. memset( fnames, 0, MAX_FNAMES * sizeof(char *) );
  382. *obitmap = OBIT_NONE;
  383.  
  384. for (iarg = 1; argv[iarg]; iarg++ )
  385. {
  386. oid = ostr2oid( argv[iarg], options );
  387. if ( OID_INVALID == oid )
  388. {
  389. if ( 0 == strcmp( "-", argv[iarg])
  390. || 0 == strcmp( "--", argv[iarg]) )
  391. continue;
  392.  
  393. if ( '-' == argv[iarg][0] ) {
  394. if ( '-' == argv[iarg][1] )
  395. return ERR_OPTUNRECOG;
  396. return ERR_OPTINVALID;
  397. }
  398. if ( !f_exists(argv[iarg]) )
  399. return ERR_NOFILE;
  400.  
  401. if ( ifname < MAX_FNAMES-1 )
  402. fnames[ ifname++ ] = argv[iarg];
  403. }
  404. else /* if ( OID_INVALID != oid ) */
  405. *obitmap |= options[oid].obit;
  406. }
  407.  
  408. *nfiles = ifname;
  409. if ( *obitmap & OBIT_HELP || *obitmap & OBIT_VERS )
  410. return ERR_NOERROR;
  411.  
  412. if ( OBIT_NONE == *obitmap || 1 == iarg || 0 == *nfiles )
  413. *obitmap = OBIT_LCOUNT + OBIT_WCOUNT + OBIT_BCOUNT;
  414.  
  415. return ERR_NOERROR;
  416. }
  417.  
  418. /*********************************************************//**
  419.  *
  420.  *************************************************************
  421.  */
  422. int main( int argc, char *argv[] )
  423. {
  424. Option options[ MAXOPTIONS ] = {
  425. /* bitflag sstr lstr desc */
  426. {OBIT_BCOUNT, "-c", "--bytes", "print the byte counts"},
  427. {OBIT_CCOUNT, "-m", "--chars", "print the character counts"},
  428. {OBIT_LCOUNT, "-l", "--lines", "print the newline counts"},
  429. {OBIT_MAXLL, "-L", "--max-line-length","print the length of the longest line"},
  430. {OBIT_WCOUNT, "-w", "--words", "print the word counts"},
  431. {OBIT_HELP, "\0", "--help", "display this help and exit"},
  432. {OBIT_VERS, "\0", "--version", "output version information and exit"}
  433. };
  434.  
  435. FILE *fp = NULL;
  436. int nfiles = 0;
  437. char *fnames[MAX_FNAMES] = {NULL};
  438. Bitmap16 obitmap = OBIT_LCOUNT + OBIT_WCOUNT + OBIT_BCOUNT;
  439. Count count, totals;
  440. enum ErrId errid = ERR_NOERROR;
  441.  
  442. errid = parse_cmdln_args( argv, options, fnames, &nfiles, &obitmap );
  443. if ( ERR_NOERROR != errid ) {
  444. print_error( errid, options );
  445. goto ret_failure;
  446. }
  447.  
  448. memset( &totals, 0, sizeof(Count) );
  449. for (int i=0; ; i++ )
  450. {
  451. if ( NULL == fnames[i] )
  452. {
  453. if (i == 0)
  454. fp = stdin;
  455. else
  456. break;
  457. }
  458. else if ( NULL == (fp = fopen(fnames[i], "rb")) ) {
  459. print_error( ERR_RDFILE, options );
  460. goto ret_failure;
  461. }
  462.  
  463. if ( !(obitmap & OBIT_HELP) && !(obitmap & OBIT_VERS) )
  464. fcount( fp, &count);
  465. print_counted( &count, obitmap, options, fp, fnames[i] );
  466.  
  467. refresh_totals( &totals, &count);
  468.  
  469. if ( fp && fp != stdin )
  470. fclose(fp);
  471. }
  472.  
  473. if ( nfiles > 1 )
  474. print_totals( &totals, obitmap );
  475.  
  476. exit( EXIT_SUCCESS );
  477.  
  478. ret_failure:
  479. if ( fp && fp != stdin )
  480. fclose(fp);
  481. exit( EXIT_FAILURE );
  482. }
  483.  
Success #stdin #stdout 0.01s 1860KB
stdin
- Lorem ipsum dolor sit amet, elit melius impedit ad eam, vim reque numquam eloquentiam cu.
Quo ne labore propriae. Quis lorem deserunt vis ei, est ei ignota convenire constituam,
sed no cibo nonumes. Ex democritum definitionem quo, vel ei munere luptatum verterem.
stdout
2	42	265