PageRenderTime 31ms CodeModel.GetById 0ms RepoModel.GetById 1ms app.codeStats 0ms

/c/coreutils-7.4/src/nl.c

http://timoseven.googlecode.com/
C | 615 lines | 458 code | 88 blank | 69 comment | 64 complexity | 201530e7697907ba26b1ec146f86200e MD5 | raw file
Possible License(s): MIT, LGPL-2.1, MPL-2.0-no-copyleft-exception, GPL-3.0, AGPL-1.0
  1. /* nl -- number lines of files
  2. Copyright (C) 89, 92, 1995-2009 Free Software Foundation, Inc.
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 3 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  13. /* Written by Scott Bartram (nancy!scott@uunet.uu.net)
  14. Revised by David MacKenzie (djm@gnu.ai.mit.edu) */
  15. #include <config.h>
  16. #include <stdio.h>
  17. #include <sys/types.h>
  18. #include <getopt.h>
  19. #include "system.h"
  20. #include <regex.h>
  21. #include "error.h"
  22. #include "linebuffer.h"
  23. #include "quote.h"
  24. #include "xstrtol.h"
  25. /* The official name of this program (e.g., no `g' prefix). */
  26. #define PROGRAM_NAME "nl"
  27. #define AUTHORS \
  28. proper_name ("Scott Bartram"), \
  29. proper_name ("David MacKenzie")
  30. /* Line-number formats. They are given an int width, an intmax_t
  31. value, and a string separator. */
  32. /* Right justified, no leading zeroes. */
  33. static char const FORMAT_RIGHT_NOLZ[] = "%*" PRIdMAX "%s";
  34. /* Right justified, leading zeroes. */
  35. static char const FORMAT_RIGHT_LZ[] = "%0*" PRIdMAX "%s";
  36. /* Left justified, no leading zeroes. */
  37. static char const FORMAT_LEFT[] = "%-*" PRIdMAX "%s";
  38. /* Default section delimiter characters. */
  39. static char const DEFAULT_SECTION_DELIMITERS[] = "\\:";
  40. /* Types of input lines: either one of the section delimiters,
  41. or text to output. */
  42. enum section
  43. {
  44. Header, Body, Footer, Text
  45. };
  46. /* Format of body lines (-b). */
  47. static char const *body_type = "t";
  48. /* Format of header lines (-h). */
  49. static char const *header_type = "n";
  50. /* Format of footer lines (-f). */
  51. static char const *footer_type = "n";
  52. /* Format currently being used (body, header, or footer). */
  53. static char const *current_type;
  54. /* Regex for body lines to number (-bp). */
  55. static struct re_pattern_buffer body_regex;
  56. /* Regex for header lines to number (-hp). */
  57. static struct re_pattern_buffer header_regex;
  58. /* Regex for footer lines to number (-fp). */
  59. static struct re_pattern_buffer footer_regex;
  60. /* Fastmaps for the above. */
  61. static char body_fastmap[UCHAR_MAX + 1];
  62. static char header_fastmap[UCHAR_MAX + 1];
  63. static char footer_fastmap[UCHAR_MAX + 1];
  64. /* Pointer to current regex, if any. */
  65. static struct re_pattern_buffer *current_regex = NULL;
  66. /* Separator string to print after line number (-s). */
  67. static char const *separator_str = "\t";
  68. /* Input section delimiter string (-d). */
  69. static char const *section_del = DEFAULT_SECTION_DELIMITERS;
  70. /* Header delimiter string. */
  71. static char *header_del = NULL;
  72. /* Header section delimiter length. */
  73. static size_t header_del_len;
  74. /* Body delimiter string. */
  75. static char *body_del = NULL;
  76. /* Body section delimiter length. */
  77. static size_t body_del_len;
  78. /* Footer delimiter string. */
  79. static char *footer_del = NULL;
  80. /* Footer section delimiter length. */
  81. static size_t footer_del_len;
  82. /* Input buffer. */
  83. static struct linebuffer line_buf;
  84. /* printf format string for unnumbered lines. */
  85. static char *print_no_line_fmt = NULL;
  86. /* Starting line number on each page (-v). */
  87. static intmax_t starting_line_number = 1;
  88. /* Line number increment (-i). */
  89. static intmax_t page_incr = 1;
  90. /* If true, reset line number at start of each page (-p). */
  91. static bool reset_numbers = true;
  92. /* Number of blank lines to consider to be one line for numbering (-l). */
  93. static intmax_t blank_join = 1;
  94. /* Width of line numbers (-w). */
  95. static int lineno_width = 6;
  96. /* Line number format (-n). */
  97. static char const *lineno_format = FORMAT_RIGHT_NOLZ;
  98. /* Current print line number. */
  99. static intmax_t line_no;
  100. /* True if we have ever read standard input. */
  101. static bool have_read_stdin;
  102. static struct option const longopts[] =
  103. {
  104. {"header-numbering", required_argument, NULL, 'h'},
  105. {"body-numbering", required_argument, NULL, 'b'},
  106. {"footer-numbering", required_argument, NULL, 'f'},
  107. {"starting-line-number", required_argument, NULL, 'v'},
  108. {"page-increment", required_argument, NULL, 'i'},
  109. {"no-renumber", no_argument, NULL, 'p'},
  110. {"join-blank-lines", required_argument, NULL, 'l'},
  111. {"number-separator", required_argument, NULL, 's'},
  112. {"number-width", required_argument, NULL, 'w'},
  113. {"number-format", required_argument, NULL, 'n'},
  114. {"section-delimiter", required_argument, NULL, 'd'},
  115. {GETOPT_HELP_OPTION_DECL},
  116. {GETOPT_VERSION_OPTION_DECL},
  117. {NULL, 0, NULL, 0}
  118. };
  119. /* Print a usage message and quit. */
  120. void
  121. usage (int status)
  122. {
  123. if (status != EXIT_SUCCESS)
  124. fprintf (stderr, _("Try `%s --help' for more information.\n"),
  125. program_name);
  126. else
  127. {
  128. printf (_("\
  129. Usage: %s [OPTION]... [FILE]...\n\
  130. "),
  131. program_name);
  132. fputs (_("\
  133. Write each FILE to standard output, with line numbers added.\n\
  134. With no FILE, or when FILE is -, read standard input.\n\
  135. \n\
  136. "), stdout);
  137. fputs (_("\
  138. Mandatory arguments to long options are mandatory for short options too.\n\
  139. "), stdout);
  140. fputs (_("\
  141. -b, --body-numbering=STYLE use STYLE for numbering body lines\n\
  142. -d, --section-delimiter=CC use CC for separating logical pages\n\
  143. -f, --footer-numbering=STYLE use STYLE for numbering footer lines\n\
  144. "), stdout);
  145. fputs (_("\
  146. -h, --header-numbering=STYLE use STYLE for numbering header lines\n\
  147. -i, --page-increment=NUMBER line number increment at each line\n\
  148. -l, --join-blank-lines=NUMBER group of NUMBER empty lines counted as one\n\
  149. -n, --number-format=FORMAT insert line numbers according to FORMAT\n\
  150. -p, --no-renumber do not reset line numbers at logical pages\n\
  151. -s, --number-separator=STRING add STRING after (possible) line number\n\
  152. "), stdout);
  153. fputs (_("\
  154. -v, --starting-line-number=NUMBER first line number on each logical page\n\
  155. -w, --number-width=NUMBER use NUMBER columns for line numbers\n\
  156. "), stdout);
  157. fputs (HELP_OPTION_DESCRIPTION, stdout);
  158. fputs (VERSION_OPTION_DESCRIPTION, stdout);
  159. fputs (_("\
  160. \n\
  161. By default, selects -v1 -i1 -l1 -sTAB -w6 -nrn -hn -bt -fn. CC are\n\
  162. two delimiter characters for separating logical pages, a missing\n\
  163. second character implies :. Type \\\\ for \\. STYLE is one of:\n\
  164. "), stdout);
  165. fputs (_("\
  166. \n\
  167. a number all lines\n\
  168. t number only nonempty lines\n\
  169. n number no lines\n\
  170. pBRE number only lines that contain a match for the basic regular\n\
  171. expression, BRE\n\
  172. \n\
  173. FORMAT is one of:\n\
  174. \n\
  175. ln left justified, no leading zeros\n\
  176. rn right justified, no leading zeros\n\
  177. rz right justified, leading zeros\n\
  178. \n\
  179. "), stdout);
  180. emit_bug_reporting_address ();
  181. }
  182. exit (status);
  183. }
  184. /* Set the command line flag TYPEP and possibly the regex pointer REGEXP,
  185. according to `optarg'. */
  186. static bool
  187. build_type_arg (char const **typep,
  188. struct re_pattern_buffer *regexp, char *fastmap)
  189. {
  190. char const *errmsg;
  191. bool rval = true;
  192. switch (*optarg)
  193. {
  194. case 'a':
  195. case 't':
  196. case 'n':
  197. *typep = optarg;
  198. break;
  199. case 'p':
  200. *typep = optarg++;
  201. regexp->buffer = NULL;
  202. regexp->allocated = 0;
  203. regexp->fastmap = fastmap;
  204. regexp->translate = NULL;
  205. re_syntax_options =
  206. RE_SYNTAX_POSIX_BASIC & ~RE_CONTEXT_INVALID_DUP & ~RE_NO_EMPTY_RANGES;
  207. errmsg = re_compile_pattern (optarg, strlen (optarg), regexp);
  208. if (errmsg)
  209. error (EXIT_FAILURE, 0, "%s", errmsg);
  210. break;
  211. default:
  212. rval = false;
  213. break;
  214. }
  215. return rval;
  216. }
  217. /* Print the line number and separator; increment the line number. */
  218. static void
  219. print_lineno (void)
  220. {
  221. intmax_t next_line_no;
  222. printf (lineno_format, lineno_width, line_no, separator_str);
  223. next_line_no = line_no + page_incr;
  224. if (next_line_no < line_no)
  225. error (EXIT_FAILURE, 0, _("line number overflow"));
  226. line_no = next_line_no;
  227. }
  228. /* Switch to a header section. */
  229. static void
  230. proc_header (void)
  231. {
  232. current_type = header_type;
  233. current_regex = &header_regex;
  234. if (reset_numbers)
  235. line_no = starting_line_number;
  236. putchar ('\n');
  237. }
  238. /* Switch to a body section. */
  239. static void
  240. proc_body (void)
  241. {
  242. current_type = body_type;
  243. current_regex = &body_regex;
  244. putchar ('\n');
  245. }
  246. /* Switch to a footer section. */
  247. static void
  248. proc_footer (void)
  249. {
  250. current_type = footer_type;
  251. current_regex = &footer_regex;
  252. putchar ('\n');
  253. }
  254. /* Process a regular text line in `line_buf'. */
  255. static void
  256. proc_text (void)
  257. {
  258. static intmax_t blank_lines = 0; /* Consecutive blank lines so far. */
  259. switch (*current_type)
  260. {
  261. case 'a':
  262. if (blank_join > 1)
  263. {
  264. if (1 < line_buf.length || ++blank_lines == blank_join)
  265. {
  266. print_lineno ();
  267. blank_lines = 0;
  268. }
  269. else
  270. fputs (print_no_line_fmt, stdout);
  271. }
  272. else
  273. print_lineno ();
  274. break;
  275. case 't':
  276. if (1 < line_buf.length)
  277. print_lineno ();
  278. else
  279. fputs (print_no_line_fmt, stdout);
  280. break;
  281. case 'n':
  282. fputs (print_no_line_fmt, stdout);
  283. break;
  284. case 'p':
  285. switch (re_search (current_regex, line_buf.buffer, line_buf.length - 1,
  286. 0, line_buf.length - 1, NULL))
  287. {
  288. case -2:
  289. error (EXIT_FAILURE, errno, _("error in regular expression search"));
  290. case -1:
  291. fputs (print_no_line_fmt, stdout);
  292. break;
  293. default:
  294. print_lineno ();
  295. break;
  296. }
  297. }
  298. fwrite (line_buf.buffer, sizeof (char), line_buf.length, stdout);
  299. }
  300. /* Return the type of line in `line_buf'. */
  301. static enum section
  302. check_section (void)
  303. {
  304. size_t len = line_buf.length - 1;
  305. if (len < 2 || memcmp (line_buf.buffer, section_del, 2))
  306. return Text;
  307. if (len == header_del_len
  308. && !memcmp (line_buf.buffer, header_del, header_del_len))
  309. return Header;
  310. if (len == body_del_len
  311. && !memcmp (line_buf.buffer, body_del, body_del_len))
  312. return Body;
  313. if (len == footer_del_len
  314. && !memcmp (line_buf.buffer, footer_del, footer_del_len))
  315. return Footer;
  316. return Text;
  317. }
  318. /* Read and process the file pointed to by FP. */
  319. static void
  320. process_file (FILE *fp)
  321. {
  322. while (readlinebuffer (&line_buf, fp))
  323. {
  324. switch (check_section ())
  325. {
  326. case Header:
  327. proc_header ();
  328. break;
  329. case Body:
  330. proc_body ();
  331. break;
  332. case Footer:
  333. proc_footer ();
  334. break;
  335. case Text:
  336. proc_text ();
  337. break;
  338. }
  339. }
  340. }
  341. /* Process file FILE to standard output.
  342. Return true if successful. */
  343. static bool
  344. nl_file (char const *file)
  345. {
  346. FILE *stream;
  347. if (STREQ (file, "-"))
  348. {
  349. have_read_stdin = true;
  350. stream = stdin;
  351. }
  352. else
  353. {
  354. stream = fopen (file, "r");
  355. if (stream == NULL)
  356. {
  357. error (0, errno, "%s", file);
  358. return false;
  359. }
  360. }
  361. process_file (stream);
  362. if (ferror (stream))
  363. {
  364. error (0, errno, "%s", file);
  365. return false;
  366. }
  367. if (STREQ (file, "-"))
  368. clearerr (stream); /* Also clear EOF. */
  369. else if (fclose (stream) == EOF)
  370. {
  371. error (0, errno, "%s", file);
  372. return false;
  373. }
  374. return true;
  375. }
  376. int
  377. main (int argc, char **argv)
  378. {
  379. int c;
  380. size_t len;
  381. bool ok = true;
  382. initialize_main (&argc, &argv);
  383. set_program_name (argv[0]);
  384. setlocale (LC_ALL, "");
  385. bindtextdomain (PACKAGE, LOCALEDIR);
  386. textdomain (PACKAGE);
  387. atexit (close_stdout);
  388. have_read_stdin = false;
  389. while ((c = getopt_long (argc, argv, "h:b:f:v:i:pl:s:w:n:d:", longopts,
  390. NULL)) != -1)
  391. {
  392. switch (c)
  393. {
  394. case 'h':
  395. if (! build_type_arg (&header_type, &header_regex, header_fastmap))
  396. {
  397. error (0, 0, _("invalid header numbering style: %s"),
  398. quote (optarg));
  399. ok = false;
  400. }
  401. break;
  402. case 'b':
  403. if (! build_type_arg (&body_type, &body_regex, body_fastmap))
  404. {
  405. error (0, 0, _("invalid body numbering style: %s"),
  406. quote (optarg));
  407. ok = false;
  408. }
  409. break;
  410. case 'f':
  411. if (! build_type_arg (&footer_type, &footer_regex, footer_fastmap))
  412. {
  413. error (0, 0, _("invalid footer numbering style: %s"),
  414. quote (optarg));
  415. ok = false;
  416. }
  417. break;
  418. case 'v':
  419. if (xstrtoimax (optarg, NULL, 10, &starting_line_number, "")
  420. != LONGINT_OK)
  421. {
  422. error (0, 0, _("invalid starting line number: %s"),
  423. quote (optarg));
  424. ok = false;
  425. }
  426. break;
  427. case 'i':
  428. if (! (xstrtoimax (optarg, NULL, 10, &page_incr, "") == LONGINT_OK
  429. && 0 < page_incr))
  430. {
  431. error (0, 0, _("invalid line number increment: %s"),
  432. quote (optarg));
  433. ok = false;
  434. }
  435. break;
  436. case 'p':
  437. reset_numbers = false;
  438. break;
  439. case 'l':
  440. if (! (xstrtoimax (optarg, NULL, 10, &blank_join, "") == LONGINT_OK
  441. && 0 < blank_join))
  442. {
  443. error (0, 0, _("invalid number of blank lines: %s"),
  444. quote (optarg));
  445. ok = false;
  446. }
  447. break;
  448. case 's':
  449. separator_str = optarg;
  450. break;
  451. case 'w':
  452. {
  453. long int tmp_long;
  454. if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
  455. || tmp_long <= 0 || tmp_long > INT_MAX)
  456. {
  457. error (0, 0, _("invalid line number field width: %s"),
  458. quote (optarg));
  459. ok = false;
  460. }
  461. else
  462. {
  463. lineno_width = tmp_long;
  464. }
  465. }
  466. break;
  467. case 'n':
  468. if (STREQ (optarg, "ln"))
  469. lineno_format = FORMAT_LEFT;
  470. else if (STREQ (optarg, "rn"))
  471. lineno_format = FORMAT_RIGHT_NOLZ;
  472. else if (STREQ (optarg, "rz"))
  473. lineno_format = FORMAT_RIGHT_LZ;
  474. else
  475. {
  476. error (0, 0, _("invalid line numbering format: %s"),
  477. quote (optarg));
  478. ok = false;
  479. }
  480. break;
  481. case 'd':
  482. section_del = optarg;
  483. break;
  484. case_GETOPT_HELP_CHAR;
  485. case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
  486. default:
  487. ok = false;
  488. break;
  489. }
  490. }
  491. if (!ok)
  492. usage (EXIT_FAILURE);
  493. /* Initialize the section delimiters. */
  494. len = strlen (section_del);
  495. header_del_len = len * 3;
  496. header_del = xmalloc (header_del_len + 1);
  497. strcat (strcat (strcpy (header_del, section_del), section_del), section_del);
  498. body_del_len = len * 2;
  499. body_del = xmalloc (body_del_len + 1);
  500. strcat (strcpy (body_del, section_del), section_del);
  501. footer_del_len = len;
  502. footer_del = xmalloc (footer_del_len + 1);
  503. strcpy (footer_del, section_del);
  504. /* Initialize the input buffer. */
  505. initbuffer (&line_buf);
  506. /* Initialize the printf format for unnumbered lines. */
  507. len = strlen (separator_str);
  508. print_no_line_fmt = xmalloc (lineno_width + len + 1);
  509. memset (print_no_line_fmt, ' ', lineno_width + len);
  510. print_no_line_fmt[lineno_width + len] = '\0';
  511. line_no = starting_line_number;
  512. current_type = body_type;
  513. current_regex = &body_regex;
  514. /* Main processing. */
  515. if (optind == argc)
  516. ok = nl_file ("-");
  517. else
  518. for (; optind < argc; optind++)
  519. ok &= nl_file (argv[optind]);
  520. if (have_read_stdin && fclose (stdin) == EOF)
  521. error (EXIT_FAILURE, errno, "-");
  522. exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
  523. }