home *** CD-ROM | disk | FTP | other *** search
/ PC-Online 1996 May / PCOnline_05_1996.bin / linux / source / a / txtutils / textutil.9 / textutil / textutils-1.9 / src / head.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-10-24  |  9.2 KB  |  428 lines

  1. /* head -- output first part of file(s)
  2.    Copyright (C) 1989, 1990, 1991 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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Options:
  19.    -b            Print first N 512-byte blocks.
  20.    -c, --bytes=N[bkm]    Print first N bytes
  21.             [or 512-byte blocks, kilobytes, or megabytes].
  22.    -k            Print first N kilobytes.
  23.    -N, -l, -n, --lines=N    Print first N lines.
  24.    -m            Print first N megabytes.
  25.    -q, --quiet, --silent    Never print filename headers.
  26.    -v, --verbose        Always print filename headers.
  27.  
  28.    Reads from standard input if no files are given or when a filename of
  29.    ``-'' is encountered.
  30.    By default, filename headers are printed only if more than one file
  31.    is given.
  32.    By default, prints the first 10 lines (head -n 10).
  33.  
  34.    David MacKenzie <djm@gnu.ai.mit.edu> */
  35.  
  36. #ifdef HAVE_CONFIG_H
  37. #if defined (CONFIG_BROKETS)
  38. /* We use <config.h> instead of "config.h" so that a compilation
  39.    using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
  40.    (which it would do because it found this file in $srcdir).  */
  41. #include <config.h>
  42. #else
  43. #include "config.h"
  44. #endif
  45. #endif
  46.  
  47. #include <stdio.h>
  48. #include <getopt.h>
  49. #include <sys/types.h>
  50. #include "system.h"
  51. #include "version.h"
  52.  
  53. /* Number of lines/chars/blocks to head. */
  54. #define DEFAULT_NUMBER 10
  55.  
  56. /* Size of atomic reads. */
  57. #define BUFSIZE (512 * 8)
  58.  
  59. /* Number of bytes per item we are printing.
  60.    If 0, head in lines. */
  61. static int unit_size;
  62.  
  63. /* If nonzero, print filename headers. */
  64. static int print_headers;
  65.  
  66. /* When to print the filename banners. */
  67. enum header_mode
  68. {
  69.   multiple_files, always, never
  70. };
  71.  
  72. void error ();
  73. void xwrite ();
  74.  
  75. static int head ();
  76. static int head_bytes ();
  77. static int head_file ();
  78. static int head_lines ();
  79. static long atou ();
  80. static void parse_unit ();
  81. static void usage ();
  82. static void write_header ();
  83.  
  84. /* The name this program was run with. */
  85. char *program_name;
  86.  
  87. /* Have we ever read standard input?  */
  88. static int have_read_stdin;
  89.  
  90. /* If non-zero, display usage information and exit.  */
  91. static int show_help;
  92.  
  93. /* If non-zero, print the version on standard output then exit.  */
  94. static int show_version;
  95.  
  96. static struct option const long_options[] =
  97. {
  98.   {"bytes", required_argument, NULL, 'c'},
  99.   {"lines", required_argument, NULL, 'n'},
  100.   {"quiet", no_argument, NULL, 'q'},
  101.   {"silent", no_argument, NULL, 'q'},
  102.   {"verbose", no_argument, NULL, 'v'},
  103.   {"help", no_argument, &show_help, 1},
  104.   {"version", no_argument, &show_version, 1},
  105.   {NULL, 0, NULL, 0}
  106. };
  107.  
  108. void
  109. main (argc, argv)
  110.      int argc;
  111.      char **argv;
  112. {
  113.   enum header_mode header_mode = multiple_files;
  114.   int exit_status = 0;
  115.   long number = -1;        /* Number of items to print (-1 if undef.). */
  116.   int c;            /* Option character. */
  117.  
  118.   program_name = argv[0];
  119.   have_read_stdin = 0;
  120.   unit_size = 0;
  121.   print_headers = 0;
  122.  
  123.   if (argc > 1 && argv[1][0] == '-' && ISDIGIT (argv[1][1]))
  124.     {
  125.       /* Old option syntax; a dash, one or more digits, and one or
  126.      more option letters.  Move past the number. */
  127.       for (number = 0, ++argv[1]; ISDIGIT (*argv[1]); ++argv[1])
  128.     number = number * 10 + *argv[1] - '0';
  129.       /* Parse any appended option letters. */
  130.       while (*argv[1])
  131.     {
  132.       switch (*argv[1])
  133.         {
  134.         case 'b':
  135.           unit_size = 512;
  136.           break;
  137.  
  138.         case 'c':
  139.           unit_size = 1;
  140.           break;
  141.  
  142.         case 'k':
  143.           unit_size = 1024;
  144.           break;
  145.  
  146.         case 'l':
  147.           unit_size = 0;
  148.           break;
  149.  
  150.         case 'm':
  151.           unit_size = 1048576;
  152.           break;
  153.  
  154.         case 'q':
  155.           header_mode = never;
  156.           break;
  157.  
  158.         case 'v':
  159.           header_mode = always;
  160.           break;
  161.  
  162.         default:
  163.           error (0, 0, "unrecognized option `-%c'", *argv[1]);
  164.           usage (1);
  165.         }
  166.       ++argv[1];
  167.     }
  168.       /* Make the options we just parsed invisible to getopt. */
  169.       argv[1] = argv[0];
  170.       argv++;
  171.       argc--;
  172.     }
  173.  
  174.   while ((c = getopt_long (argc, argv, "c:n:qv", long_options, (int *) 0))
  175.      != EOF)
  176.     {
  177.       switch (c)
  178.     {
  179.     case 0:
  180.       break;
  181.  
  182.     case 'c':
  183.       unit_size = 1;
  184.       parse_unit (optarg);
  185.       goto getnum;
  186.     case 'n':
  187.       unit_size = 0;
  188.     getnum:
  189.       number = atou (optarg);
  190.       if (number == -1)
  191.         error (1, 0, "invalid number `%s'", optarg);
  192.       break;
  193.  
  194.     case 'q':
  195.       header_mode = never;
  196.       break;
  197.  
  198.     case 'v':
  199.       header_mode = always;
  200.       break;
  201.  
  202.     default:
  203.       usage (1);
  204.     }
  205.     }
  206.  
  207.   if (show_version)
  208.     {
  209.       printf ("%s\n", version_string);
  210.       exit (0);
  211.     }
  212.  
  213.   if (show_help)
  214.     usage (0);
  215.  
  216.   if (number == -1)
  217.     number = DEFAULT_NUMBER;
  218.  
  219.   if (unit_size > 1)
  220.     number *= unit_size;
  221.  
  222.   if (header_mode == always
  223.       || (header_mode == multiple_files && optind < argc - 1))
  224.     print_headers = 1;
  225.  
  226.   if (optind == argc)
  227.     exit_status |= head_file ("-", number);
  228.  
  229.   for (; optind < argc; ++optind)
  230.     exit_status |= head_file (argv[optind], number);
  231.  
  232.   if (have_read_stdin && close (0) < 0)
  233.     error (1, errno, "-");
  234.   if (close (1) < 0)
  235.     error (1, errno, "write error");
  236.  
  237.   exit (exit_status);
  238. }
  239.  
  240. static int
  241. head_file (filename, number)
  242.      char *filename;
  243.      long number;
  244. {
  245.   int fd;
  246.  
  247.   if (!strcmp (filename, "-"))
  248.     {
  249.       have_read_stdin = 1;
  250.       filename = "standard input";
  251.       if (print_headers)
  252.     write_header (filename);
  253.       return head (filename, 0, number);
  254.     }
  255.   else
  256.     {
  257.       fd = open (filename, O_RDONLY);
  258.       if (fd >= 0)
  259.     {
  260.       int errors;
  261.  
  262.       if (print_headers)
  263.         write_header (filename);
  264.       errors = head (filename, fd, number);
  265.       if (close (fd) == 0)
  266.         return errors;
  267.     }
  268.       error (0, errno, "%s", filename);
  269.       return 1;
  270.     }
  271. }
  272.  
  273. static void
  274. write_header (filename)
  275.      char *filename;
  276. {
  277.   static int first_file = 1;
  278.  
  279.   if (first_file)
  280.     {
  281.       xwrite (1, "==> ", 4);
  282.       first_file = 0;
  283.     }
  284.   else
  285.     xwrite (1, "\n==> ", 5);
  286.   xwrite (1, filename, strlen (filename));
  287.   xwrite (1, " <==\n", 5);
  288. }
  289.  
  290. static int
  291. head (filename, fd, number)
  292.      char *filename;
  293.      int fd;
  294.      long number;
  295. {
  296.   if (unit_size)
  297.     return head_bytes (filename, fd, number);
  298.   else
  299.     return head_lines (filename, fd, number);
  300. }
  301.  
  302. static int
  303. head_bytes (filename, fd, bytes_to_write)
  304.      char *filename;
  305.      int fd;
  306.      long bytes_to_write;
  307. {
  308.   char buffer[BUFSIZE];
  309.   int bytes_read;
  310.  
  311.   while (bytes_to_write)
  312.     {
  313.       bytes_read = read (fd, buffer, BUFSIZE);
  314.       if (bytes_read == -1)
  315.     {
  316.       error (0, errno, "%s", filename);
  317.       return 1;
  318.     }
  319.       if (bytes_read == 0)
  320.     break;
  321.       if (bytes_read > bytes_to_write)
  322.     bytes_read = bytes_to_write;
  323.       xwrite (1, buffer, bytes_read);
  324.       bytes_to_write -= bytes_read;
  325.     }
  326.   return 0;
  327. }
  328.  
  329. static int
  330. head_lines (filename, fd, lines_to_write)
  331.      char *filename;
  332.      int fd;
  333.      long lines_to_write;
  334. {
  335.   char buffer[BUFSIZE];
  336.   int bytes_read;
  337.   int bytes_to_write;
  338.  
  339.   while (lines_to_write)
  340.     {
  341.       bytes_read = read (fd, buffer, BUFSIZE);
  342.       if (bytes_read == -1)
  343.     {
  344.       error (0, errno, "%s", filename);
  345.       return 1;
  346.     }
  347.       if (bytes_read == 0)
  348.     break;
  349.       bytes_to_write = 0;
  350.       while (bytes_to_write < bytes_read)
  351.     if (buffer[bytes_to_write++] == '\n' && --lines_to_write == 0)
  352.       break;
  353.       xwrite (1, buffer, bytes_to_write);
  354.     }
  355.   return 0;
  356. }
  357.  
  358. static void
  359. parse_unit (str)
  360.      char *str;
  361. {
  362.   int arglen = strlen (str);
  363.  
  364.   if (arglen == 0)
  365.     return;
  366.  
  367.   switch (str[arglen - 1])
  368.     {
  369.     case 'b':
  370.       unit_size = 512;
  371.       str[arglen - 1] = '\0';
  372.       break;
  373.     case 'k':
  374.       unit_size = 1024;
  375.       str[arglen - 1] = '\0';
  376.       break;
  377.     case 'm':
  378.       unit_size = 1048576;
  379.       str[arglen - 1] = '\0';
  380.       break;
  381.     }
  382. }
  383.  
  384. /* Convert STR, a string of ASCII digits, into an unsigned integer.
  385.    Return -1 if STR does not represent a valid unsigned integer. */
  386.  
  387. static long
  388. atou (str)
  389.      char *str;
  390. {
  391.   int value;
  392.  
  393.   for (value = 0; ISDIGIT (*str); ++str)
  394.     value = value * 10 + *str - '0';
  395.   return *str ? -1 : value;
  396. }
  397.  
  398. static void
  399. usage (status)
  400.      int status;
  401. {
  402.   if (status != 0)
  403.     fprintf (stderr, "Try `%s --help' for more information.\n",
  404.          program_name);
  405.   else
  406.     {
  407.       printf ("\
  408. Usage: %s [OPTION]... [FILE]...\n\
  409. ",
  410.           program_name);
  411.       printf ("\
  412. \n\
  413.   -c, --bytes=SIZE         print first SIZE bytes\n\
  414.   -l, -n, --lines=NUMBER   print first NUMBER lines instead of first 10\n\
  415.   -q, --quiet, --silent    never print headers giving file names\n\
  416.   -v, --verbose            always print headers giving file names\n\
  417.       --help               display this help and exit\n\
  418.       --version            output version information and exit\n\
  419. \n\
  420. SIZE may have a multiplier suffix: b for 512, k for 1K, m for 1 Meg.\n\
  421. If -VALUE is used as first OPTION, read -c VALUE when one of\n\
  422. multipliers bkm follows concatenated, else read -n VALUE.  With no\n\
  423. FILE, or when FILE is -, read standard input.\n\
  424. ");
  425.     }
  426.   exit (status);
  427. }
  428.