home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / textutils-1.19-src.tgz / tar.out / fsf / textutils / src / wc.c < prev   
C/C++ Source or Header  |  1996-09-28  |  7KB  |  326 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.   -l, --lines            print the newline counts\n\
  83.   -w, --words            print the word counts\n\
  84.   -c, --bytes, --chars   print the byte counts\n\
  85.       --help             display this help and exit\n\
  86.       --version          output version information and exit\n\
  87. "));
  88.     }
  89.   exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
  90. }
  91.  
  92. static void
  93. write_counts (long unsigned int lines, long unsigned int words,
  94.           long unsigned int chars, const char *file)
  95. {
  96.   if (print_lines)
  97.     printf ("%7lu", lines);
  98.   if (print_words)
  99.     {
  100.       if (print_lines)
  101.     putchar (' ');
  102.       printf ("%7lu", words);
  103.     }
  104.   if (print_chars)
  105.     {
  106.       if (print_lines || print_words)
  107.     putchar (' ');
  108.       printf ("%7lu", chars);
  109.     }
  110.   if (*file)
  111.     printf (" %s", file);
  112.   putchar ('\n');
  113. }
  114.  
  115. static void
  116. wc (int fd, const char *file)
  117. {
  118.   char buf[BUFFER_SIZE + 1];
  119.   register int bytes_read;
  120.   register int in_word = 0;
  121.   register unsigned long lines, words, chars;
  122.  
  123.   lines = words = chars = 0;
  124.  
  125.   /* When counting only bytes, save some line- and word-counting
  126.      overhead.  If FD is a `regular' Unix file, using lseek is enough
  127.      to get its `size' in bytes.  Otherwise, read blocks of BUFFER_SIZE
  128.      bytes at a time until EOF.  Note that the `size' (number of bytes)
  129.      that wc reports is smaller than stats.st_size when the file is not
  130.      positioned at its beginning.  That's why the lseek calls below are
  131.      necessary.  For example the command
  132.      `(dd ibs=99k skip=1 count=0; ./wc -c) < /etc/group'
  133.      should make wc report `0' bytes.  */
  134.  
  135.   if (print_chars && !print_words && !print_lines)
  136.     {
  137.       off_t current_pos, end_pos;
  138.       struct stat stats;
  139.  
  140.       if (fstat (fd, &stats) == 0 && S_ISREG (stats.st_mode)
  141.       && (current_pos = lseek (fd, (off_t) 0, SEEK_CUR)) != -1
  142.       && (end_pos = lseek (fd, (off_t) 0, SEEK_END)) != -1)
  143.     {
  144.       off_t diff;
  145.       /* Be careful here.  The current position may actually be
  146.          beyond the end of the file.  As in the example above.  */
  147.       chars = (diff = end_pos - current_pos) < 0 ? 0 : diff;
  148.     }
  149.       else
  150.     {
  151.       while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
  152.         {
  153.           chars += bytes_read;
  154.         }
  155.       if (bytes_read < 0)
  156.         {
  157.           error (0, errno, "%s", file);
  158.           exit_status = 1;
  159.         }
  160.     }
  161.     }
  162.   else if (!print_words)
  163.     {
  164.       /* Use a separate loop when counting only lines or lines and bytes --
  165.      but not words.  */
  166.       while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
  167.     {
  168.       register char *p = buf;
  169.  
  170.       while ((p = memchr (p, '\n', (buf + bytes_read) - p)))
  171.         {
  172.           ++p;
  173.           ++lines;
  174.         }
  175.       chars += bytes_read;
  176.     }
  177.       if (bytes_read < 0)
  178.     {
  179.       error (0, errno, "%s", file);
  180.       exit_status = 1;
  181.     }
  182.     }
  183.   else
  184.     {
  185.       while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
  186.     {
  187.       register char *p = buf;
  188.  
  189.       chars += bytes_read;
  190.       do
  191.         {
  192.           switch (*p++)
  193.         {
  194.         case '\n':
  195.           lines++;
  196.           /* Fall through. */
  197.         case '\r':
  198.         case '\f':
  199.         case '\t':
  200.         case '\v':
  201.         case ' ':
  202.           if (in_word)
  203.             {
  204.               in_word = 0;
  205.               words++;
  206.             }
  207.           break;
  208.         default:
  209.           in_word = 1;
  210.           break;
  211.         }
  212.         }
  213.       while (--bytes_read);
  214.     }
  215.       if (bytes_read < 0)
  216.     {
  217.       error (0, errno, "%s", file);
  218.       exit_status = 1;
  219.     }
  220.       if (in_word)
  221.     words++;
  222.     }
  223.  
  224.   write_counts (lines, words, chars, file);
  225.   total_lines += lines;
  226.   total_words += words;
  227.   total_chars += chars;
  228. }
  229.  
  230. static void
  231. wc_file (const char *file)
  232. {
  233.   if (!strcmp (file, "-"))
  234.     {
  235.       have_read_stdin = 1;
  236.       wc (0, file);
  237.     }
  238.   else
  239.     {
  240.       int fd = open (file, O_RDONLY);
  241.       if (fd == -1)
  242.     {
  243.       error (0, errno, "%s", file);
  244.       exit_status = 1;
  245.       return;
  246.     }
  247.       wc (fd, file);
  248.       if (close (fd))
  249.     {
  250.       error (0, errno, "%s", file);
  251.       exit_status = 1;
  252.     }
  253.     }
  254. }
  255.  
  256. int
  257. main (int argc, char **argv)
  258. {
  259.   int optc;
  260.   int nfiles;
  261.  
  262.   program_name = argv[0];
  263.   setlocale (LC_ALL, "");
  264.   bindtextdomain (PACKAGE, LOCALEDIR);
  265.   textdomain (PACKAGE);
  266.  
  267.   exit_status = 0;
  268.   print_lines = print_words = print_chars = 0;
  269.   total_lines = total_words = total_chars = 0;
  270.  
  271.   while ((optc = getopt_long (argc, argv, "clw", longopts, (int *) 0)) != EOF)
  272.     switch (optc)
  273.       {
  274.       case 0:
  275.     break;
  276.  
  277.       case 'c':
  278.     print_chars = 1;
  279.     break;
  280.  
  281.       case 'l':
  282.     print_lines = 1;
  283.     break;
  284.  
  285.       case 'w':
  286.     print_words = 1;
  287.     break;
  288.  
  289.       default:
  290.     usage (1);
  291.       }
  292.  
  293.   if (show_version)
  294.     {
  295.       printf ("wc - %s\n", PACKAGE_VERSION);
  296.       exit (EXIT_SUCCESS);
  297.     }
  298.  
  299.   if (show_help)
  300.     usage (0);
  301.  
  302.   if (print_lines + print_words + print_chars == 0)
  303.     print_lines = print_words = print_chars = 1;
  304.  
  305.   nfiles = argc - optind;
  306.  
  307.   if (nfiles == 0)
  308.     {
  309.       have_read_stdin = 1;
  310.       wc (0, "");
  311.     }
  312.   else
  313.     {
  314.       for (; optind < argc; ++optind)
  315.     wc_file (argv[optind]);
  316.  
  317.       if (nfiles > 1)
  318.     write_counts (total_lines, total_words, total_chars, _("total"));
  319.     }
  320.  
  321.   if (have_read_stdin && close (0))
  322.     error (EXIT_FAILURE, errno, "-");
  323.  
  324.   exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
  325. }
  326.