⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tall.c

📁 《linux应用开发技术详解》的配套代码
💻 C
📖 第 1 页 / 共 4 页
字号:
	      f->dev = stats.st_dev;
	      f->ino = stats.st_ino;
	      f->n_unchanged_stats = 0;
	      f->n_consecutive_size_changes = 0;
	      f->ignore = 0;
	    }
	}
      else
	{
	  if (!is_stdin && close (fd))
	    {
	      error (0, errno, "%s", pretty_name (f));
	      errors = 1;
	    }
	}
    }

  return errors;
}

/* If the command line arguments are of the obsolescent form and the
   option string is well-formed, set *FAIL to zero, set *N_UNITS, the
   globals COUNT_LINES, FOREVER, and FROM_START, and return non-zero.
   Otherwise, if the command line arguments appear to be of the
   obsolescent form but the option string is malformed, set *FAIL to
   non-zero, don't modify any other parameter or global variable, and
   return non-zero. Otherwise, return zero and don't modify any parameter
   or global variable.  */

static int
parse_obsolescent_option (int argc, const char *const *argv,
			  off_t *n_units, int *fail)
{
  const char *p = argv[1];
  const char *n_string = NULL;
  const char *n_string_end;
  bool obsolete_usage;

  int t_from_start;
  int t_count_lines;
  int t_forever;

  /* With the obsolescent form, there is one option string and
     (technically) at most one file argument.  But we allow two or more
     by default.  */
  if (argc < 2)
    return 0;

  obsolete_usage = (posix2_version () < 200112) || !getenv ("POSIXLY_CORRECT");

  /* If P starts with `+' and the POSIX version predates 1003.1-2001,
     or if P starts with `-N' (where N is a digit), or `-l', then it
     is obsolescent.  Return zero otherwise.  */
  if (! ((p[0] == '+' && obsolete_usage)
	 || (p[0] == '-' && (p[1] == 'l' || ISDIGIT (p[1])))))
    return 0;

  if (*p == '+')
    t_from_start = 1;
  else if (*p == '-')
    t_from_start = 0;
  else
    return 0;

  ++p;
  if (ISDIGIT (*p))
    {
      n_string = p;
      do
	{
	  ++p;
	}
      while (ISDIGIT (*p));
    }
  n_string_end = p;

  t_count_lines = 1;
  if (*p == 'c' || *p == 'b')
    {
      t_count_lines = 0;
      ++p;
    }
  else if (*p == 'l')
    {
      ++p;
    }

  t_forever = 0;
  if (*p == 'f')
    {
      t_forever = 1;
      ++p;
    }

  if (*p != '\0')
    {
      /* If (argv[1] begins with a `+' or if it begins with `-' followed
	 by a digit), but has an invalid suffix character, give a diagnostic
	 and indicate to caller that this *is* of the obsolescent form,
	 but that it's an invalid option.  */
      if (t_from_start || n_string)
	{
	  error (0, 0,
		 _("%c: invalid suffix character in obsolescent option" ), *p);
	  *fail = 1;
	  return 1;
	}

      /* Otherwise, it might be a valid non-obsolescent option like -n.  */
      return 0;
    }

  *fail = 0;
  if (n_string == NULL)
    *n_units = DEFAULT_N_LINES;
  else
    {
      strtol_error s_err;
      uintmax_t tmp;
      char *end;

      s_err = xstrtoumax (n_string, &end, 10, &tmp,
			  *n_string_end == 'b' ? "b" : NULL);
      if (s_err == LONGINT_OK && tmp <= OFF_T_MAX)
	*n_units = (off_t) tmp;
      else
	{
	  /* Extract a NUL-terminated string for the error message.  */
	  size_t len = n_string_end - n_string;
	  char *n_string_tmp = xmalloc (len + 1);

	  strncpy (n_string_tmp, n_string, len);
	  n_string_tmp[len] = '\0';

	  error (0, 0,
		 _("%s: %s is so large that it is not representable"),
		 n_string_tmp, (t_count_lines
				? _("number of lines")
				: _("number of bytes")));
	  free (n_string_tmp);
	  *fail = 1;
	}
    }

  if (!*fail)
    {
      if (argc > 3)
	{
	  /* When POSIXLY_CORRECT is set, enforce the `at most one
	     file argument' requirement.  */
	  if (getenv ("POSIXLY_CORRECT"))
	    {
	      error (0, 0, _("\
too many arguments;  When using tail's obsolescent option syntax (%s)\n\
there may be no more than one file argument.  Use the equivalent -n or -c\n\
option instead."), argv[1]);
	      *fail = 1;
	      return 1;
	    }

#if DISABLED  /* FIXME: enable or remove this warning.  */
	  error (0, 0, _("\
Warning: it is not portable to use two or more file arguments with\n\
tail's obsolescent option syntax (%s).  Use the equivalent -n or -c\n\
option instead."), argv[1]);
#endif
	}

      if (! obsolete_usage)
	{
	  error (0, 0, _("`%s' option is obsolete; use `%s-%c %.*s'"),
		 argv[1], t_forever ? " -f" : "", t_count_lines ? 'n' : 'c',
		 (int) (n_string_end - n_string), n_string);
	  usage (EXIT_FAILURE);
	}

      /* Set globals.  */
      from_start = t_from_start;
      count_lines = t_count_lines;
      forever = t_forever;
    }

  return 1;
}

static void
parse_options (int argc, char **argv,
	       off_t *n_units, enum header_mode *header_mode,
	       double *sleep_interval)
{
  int c;

  count_lines = 1;
  forever = from_start = print_headers = 0;

  while ((c = getopt_long (argc, argv, "c:n:fFqs:v", long_options, NULL))
	 != -1)
    {
      switch (c)
	{
	case 0:
	  break;

	case 'F':
	  forever = 1;
	  follow_mode = Follow_name;
	  reopen_inaccessible_files = 1;
	  break;

	case 'c':
	case 'n':
	  count_lines = (c == 'n');
	  if (*optarg == '+')
	    from_start = 1;
	  else if (*optarg == '-')
	    ++optarg;

	  {
	    strtol_error s_err;
	    uintmax_t n;
	    s_err = xstrtoumax (optarg, NULL, 10, &n, "bkm");
	    if (s_err != LONGINT_OK)
	      {
		error (EXIT_FAILURE, 0, "%s: %s", optarg,
		       (c == 'n'
			? _("invalid number of lines")
			: _("invalid number of bytes")));
	      }

	    if (OFF_T_MAX < n)
	      error (EXIT_FAILURE, 0,
		   _("%s is larger than the maximum file size on this system"),
		     optarg);
	    *n_units = (off_t) n;
	  }
	  break;

	case 'f':
	case LONG_FOLLOW_OPTION:
	  forever = 1;
	  if (optarg == NULL)
	    follow_mode = DEFAULT_FOLLOW_MODE;
	  else
	    follow_mode = XARGMATCH ("--follow", optarg,
				     follow_mode_string, follow_mode_map);
	  break;

	case RETRY_OPTION:
	  reopen_inaccessible_files = 1;
	  break;

	case MAX_UNCHANGED_STATS_OPTION:
	  /* --max-unchanged-stats=N */
	  if (xstrtoul (optarg, NULL, 10,
			&max_n_unchanged_stats_between_opens, "") != LONGINT_OK)
	    {
	      error (EXIT_FAILURE, 0,
	       _("%s: invalid maximum number of unchanged stats between opens"),
		     optarg);
	    }
	  break;

	case MAX_CONSECUTIVE_SIZE_CHANGES_OPTION:
  	  /* --max-consecutive-size-changes=N */
	  if (xstrtoul (optarg, NULL, 10,
			&max_n_consecutive_size_changes_between_opens, "")
	      != LONGINT_OK)
	    {
	      error (EXIT_FAILURE, 0,
		   _("%s: invalid maximum number of consecutive size changes"),
		     optarg);
	    }
	  break;

	case PID_OPTION:
	  {
	    strtol_error s_err;
	    unsigned long int tmp_ulong;
	    s_err = xstrtoul (optarg, NULL, 10, &tmp_ulong, "");
	    if (s_err != LONGINT_OK || tmp_ulong > PID_T_MAX)
	      {
		error (EXIT_FAILURE, 0, _("%s: invalid PID"), optarg);
	      }
	    pid = tmp_ulong;
	  }
	  break;

	case 'q':
	  *header_mode = never;
	  break;

	case 's':
	  {
	    double s;
	    if (xstrtod (optarg, NULL, &s) || ! (0 <= s))
	      error (EXIT_FAILURE, 0,
		     _("%s: invalid number of seconds"), optarg);
	    *sleep_interval = s;
	  }
	  break;

	case 'v':
	  *header_mode = always;
	  break;

	case_GETOPT_HELP_CHAR;

	case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

	default:
	  usage (EXIT_FAILURE);
	}
    }

  if (reopen_inaccessible_files && follow_mode != Follow_name)
    error (0, 0, _("warning: --retry is useful only when following by name"));

  if (pid && !forever)
    error (0, 0,
	   _("warning: PID ignored; --pid=PID is useful only when following"));
  else if (pid && kill (pid, 0) != 0 && errno == ENOSYS)
    {
      error (0, 0, _("warning: --pid=PID is not supported on this system"));
      pid = 0;
    }
}

int
main (int argc, char **argv)
{
  enum header_mode header_mode = multiple_files;
  int exit_status = 0;
  /* If from_start, the number of items to skip before printing; otherwise,
     the number of items at the end of the file to print.  Although the type
     is signed, the value is never negative.  */
  off_t n_units = DEFAULT_N_LINES;
  int n_files;
  char **file;
  struct File_spec *F;
  int i;

  /* The number of seconds to sleep between iterations.
     During one iteration, every file name or descriptor is checked to
     see if it has changed.  */
  double sleep_interval = 1.0;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  have_read_stdin = 0;

  {
    int fail;

    if (parse_obsolescent_option (argc,
				  (const char *const *) argv,
				  &n_units, &fail))
      {
	if (fail)
	  exit (EXIT_FAILURE);
	optind = 2;
      }
    else
      {
	parse_options (argc, argv, &n_units, &header_mode, &sleep_interval);
      }
  }

  /* To start printing with item N_UNITS from the start of the file, skip
     N_UNITS - 1 items.  `tail -n +0' is actually meaningless, but for Unix
     compatibility it's treated the same as `tail -n +1'.  */
  if (from_start)
    {
      if (n_units)
	--n_units;
    }

  n_files = argc - optind;
  file = argv + optind;

  if (n_files == 0)
    {
      static char *dummy_stdin = "-";
      n_files = 1;
      file = &dummy_stdin;
    }

  F = (struct File_spec *) xmalloc (n_files * sizeof (F[0]));
  for (i = 0; i < n_files; i++)
    F[i].name = file[i];

  if (header_mode == always
      || (header_mode == multiple_files && n_files > 1))
    print_headers = 1;

  for (i = 0; i < n_files; i++)
    exit_status |= tail_file (&F[i], n_units);

  if (forever)
    {
      /* This fflush appears to be required only on Solaris2.7.  */
      if (fflush (stdout) < 0)
	error (EXIT_FAILURE, errno, _("write error"));

      SETVBUF (stdout, NULL, _IONBF, 0);
      tail_forever (F, n_files, sleep_interval);
    }

  if (have_read_stdin && close (STDIN_FILENO) < 0)
    error (EXIT_FAILURE, errno, "-");
  exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -