home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / txtut122.zip / textutil / src / wc.c < prev   
C/C++ Source or Header  |  1998-04-06  |  8KB  |  332 lines

  1. /* wc - print the number of bytes, words, and lines in files
  2.    Copyright (C) 85, 91, 95, 1996 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
  17.  
  18. /* Written by Paul Rubin, phr@ocf.berkeley.edu
  19.    and David MacKenzie, djm@gnu.ai.mit.edu. */
  20.  
  21. #include <config.h>
  22.  
  23. #include <stdio.h>
  24. #include <getopt.h>
  25. #include <sys/types.h>
  26. #include "system.h"
  27. #include "error.h"
  28.  
  29. /* Size of atomic reads. */
  30. #define BUFFER_SIZE (16 * 1024)
  31.  
  32. int safe_read ();
  33.  
  34. /* The name this program was run with. */
  35. char *program_name;
  36.  
  37. /* Cumulative number of lines, words, and chars in all files so far. */
  38. static unsigned long total_lines, total_words, total_chars;
  39.  
  40. /* Which counts to print. */
  41. static int print_lines, print_words, print_chars;
  42.  
  43. /* Nonzero if we have ever read the standard input. */
  44. static int have_read_stdin;
  45.  
  46. /* The error code to return to the system. */
  47. static int exit_status;
  48.  
  49. /* If nonzero, display usage information and exit.  */
  50. static int show_help;
  51.  
  52. /* If nonzero, print the version on standard output then exits.  */
  53. static int show_version;
  54.  
  55. static struct option const longopts[] =
  56. {
  57.   {"bytes", no_argument, NULL, 'c'},
  58.   {"chars", no_argument, NULL, 'c'},
  59.   {"lines", no_argument, NULL, 'l'},
  60.   {"words", no_argument, NULL, 'w'},
  61.   {"help", no_argument, &show_help, 1},
  62.   {"version", no_argument, &show_version, 1},
  63.   {NULL, 0, NULL, 0}
  64. };
  65.  
  66. static void
  67. usage (int status)
  68. {
  69.   if (status != 0)
  70.     fprintf (stderr, _("Try `%s --help' for more information.\n"),
  71.          program_name);
  72.   else
  73.     {
  74.       printf (_("\
  75. Usage: %s [OPTION]... [FILE]...\n\
  76. "),
  77.           program_name);
  78.       printf (_("\
  79. Print line, word, and byte counts for each FILE, and a total line if\n\
  80. more than one FILE is specified.  With no FILE, or when FILE is -,\n\
  81. read standard input.\n\
  82.   -c, --bytes, --chars   print the byte counts\n\
  83.   -l, --lines            print the newline counts\n\
  84.   -w, --words            print the word counts\n\
  85.       --help             display this help and exit\n\
  86.       --version          output version information and exit\n\
  87. "));
  88.       puts (_("\nReport bugs to textutils-bugs@gnu.ai.mit.edu"));
  89.     }
  90.   exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
  91. }
  92.  
  93. static void
  94. write_counts (long unsigned int lines, long unsigned int words,
  95.           long unsigned int chars, const char *file)
  96. {
  97.   if (print_lines)
  98.     printf ("%7lu", lines);
  99.   if (print_words)
  100.     {
  101.       if (print_lines)
  102.     putchar (' ');
  103.       printf ("%7lu", words);
  104.     }
  105.   if (print_chars)
  106.     {
  107.       if (print_lines || print_words)
  108.     putchar (' ');
  109.       printf ("%7lu", chars);
  110.     }
  111.   if (*file)
  112.     printf (" %s", file);
  113.   putchar ('\n');
  114. }
  115.  
  116. static void
  117. wc (int fd, const char *file)
  118. {
  119.   char buf[BUFFER_SIZE + 1];
  120.   register int bytes_read;
  121.   register int in_word = 0;
  122.   register unsigned long lines, words, chars;
  123.  
  124.   lines = words = chars = 0;
  125.  
  126.   /* When counting only bytes, save some line- and word-counting
  127.      overhead.  If FD is a `regular' Unix file, using lseek is enough
  128.      to get its `size' in bytes.  Otherwise, read blocks of BUFFER_SIZE
  129.      bytes at a time until EOF.  Note that the `size' (number of bytes)
  130.      that wc reports is smaller than stats.st_size when the file is not
  131.      positioned at its beginning.  That's why the lseek calls below are
  132.      necessary.  For example the command
  133.      `(dd ibs=99k skip=1 count=0; ./wc -c) < /etc/group'
  134.      should make wc report `0' bytes.  */
  135.  
  136.   if (print_chars && !print_words && !print_lines)
  137.     {
  138.       off_t current_pos, end_pos;
  139.       struct stat stats;
  140.  
  141.       if (fstat (fd, &stats) == 0 && S_ISREG (stats.st_mode)
  142.       && (current_pos = lseek (fd, (off_t) 0, SEEK_CUR)) != -1
  143.       && (end_pos = lseek (fd, (off_t) 0, SEEK_END)) != -1)
  144.     {
  145.       off_t diff;
  146.       /* Be careful here.  The current position may actually be
  147.          beyond the end of the file.  As in the example above.  */
  148.       chars = (diff = end_pos - current_pos) < 0 ? 0 : diff;
  149.     }
  150.       else
  151.     {
  152.       while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
  153.         {
  154.           chars += bytes_read;
  155.         }
  156.       if (bytes_read < 0)
  157.         {
  158.           error (0, errno, "%s", file);
  159.           exit_status = 1;
  160.         }
  161.     }
  162.     }
  163.   else if (!print_words)
  164.     {
  165.       /* Use a separate loop when counting only lines or lines and bytes --
  166.      but not words.  */
  167.       while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
  168.     {
  169.       register char *p = buf;
  170.  
  171.       while ((p = memchr (p, '\n', (buf + bytes_read) - p)))
  172.         {
  173.           ++p;
  174.           ++lines;
  175.         }
  176.       chars += bytes_read;
  177.     }
  178.       if (bytes_read < 0)
  179.     {
  180.       error (0, errno, "%s", file);
  181.       exit_status = 1;
  182.     }
  183.     }
  184.   else
  185.     {
  186.       while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
  187.     {
  188.       register char *p = buf;
  189.  
  190.       chars += bytes_read;
  191.       do
  192.         {
  193.           switch (*p++)
  194.         {
  195.         case '\n':
  196.           lines++;
  197.           /* Fall through. */
  198.         case '\r':
  199.         case '\f':
  200.         case '\t':
  201.         case '\v':
  202.         case ' ':
  203.           if (in_word)
  204.             {
  205.               in_word = 0;
  206.               words++;
  207.             }
  208.           break;
  209.         default:
  210.           in_word = 1;
  211.           break;
  212.         }
  213.         }
  214.       while (--bytes_read);
  215.     }
  216.       if (bytes_read < 0)
  217.     {
  218.       error (0, errno, "%s", file);
  219.       exit_status = 1;
  220.     }
  221.       if (in_word)
  222.     words++;
  223.     }
  224.  
  225.   write_counts (lines, words, chars, file);
  226.   total_lines += lines;
  227.   total_words += words;
  228.   total_chars += chars;
  229. }
  230.  
  231. static void
  232. wc_file (const char *file)
  233. {
  234.   if (!strcmp (file, "-"))
  235.     {
  236.       have_read_stdin = 1;
  237.       wc (0, file);
  238.     }
  239.   else
  240.     {
  241.       int fd = open (file, O_RDONLY);
  242.       if (fd == -1)
  243.     {
  244.       error (0, errno, "%s", file);
  245.       exit_status = 1;
  246.       return;
  247.     }
  248.       wc (fd, file);
  249.       if (close (fd))
  250.     {
  251.       error (0, errno, "%s", file);
  252.       exit_status = 1;
  253.     }
  254.     }
  255. }
  256.  
  257. int
  258. main (int argc, char **argv)
  259. {
  260.   int optc;
  261.   int nfiles;
  262.  
  263. #ifdef __EMX__
  264. _wildcard(&argc, &argv);
  265. #endif
  266.  
  267.   program_name = argv[0];
  268. #ifndef __EMX__
  269.   setlocale (LC_ALL, "");
  270.   bindtextdomain (PACKAGE, LOCALEDIR);
  271.   textdomain (PACKAGE);
  272. #endif
  273.   exit_status = 0;
  274.   print_lines = print_words = print_chars = 0;
  275.   total_lines = total_words = total_chars = 0;
  276.  
  277.   while ((optc = getopt_long (argc, argv, "clw", longopts, (int *) 0)) != EOF)
  278.     switch (optc)
  279.       {
  280.       case 0:
  281.     break;
  282.  
  283.       case 'c':
  284.     print_chars = 1;
  285.     break;
  286.  
  287.       case 'l':
  288.     print_lines = 1;
  289.     break;
  290.  
  291.       case 'w':
  292.     print_words = 1;
  293.     break;
  294.  
  295.       default:
  296.     usage (1);
  297.       }
  298.  
  299.   if (show_version)
  300.     {
  301.       printf ("wc (%s) %s\n", GNU_PACKAGE, VERSION);
  302.       exit (EXIT_SUCCESS);
  303.     }
  304.  
  305.   if (show_help)
  306.     usage (0);
  307.  
  308.   if (print_lines + print_words + print_chars == 0)
  309.     print_lines = print_words = print_chars = 1;
  310.  
  311.   nfiles = argc - optind;
  312.  
  313.   if (nfiles == 0)
  314.     {
  315.       have_read_stdin = 1;
  316.       wc (0, "");
  317.     }
  318.   else
  319.     {
  320.       for (; optind < argc; ++optind)
  321.     wc_file (argv[optind]);
  322.  
  323.       if (nfiles > 1)
  324.     write_counts (total_lines, total_words, total_chars, _("total"));
  325.     }
  326.  
  327.   if (have_read_stdin && close (0))
  328.     error (EXIT_FAILURE, errno, "-");
  329.  
  330.   exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
  331. }
  332.