PageRenderTime 52ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/usr.bin/nl/nl.c

https://gitlab.com/storedmirrors/minix
C | 406 lines | 284 code | 42 blank | 80 comment | 87 complexity | 67dcf91241f5e096ec4da936bdd33293 MD5 | raw file
  1. /* $NetBSD: nl.c,v 1.12 2013/09/17 20:00:50 wiz Exp $ */
  2. /*-
  3. * Copyright (c) 1999 The NetBSD Foundation, Inc.
  4. * All rights reserved.
  5. *
  6. * This code is derived from software contributed to The NetBSD Foundation
  7. * by Klaus Klein.
  8. *
  9. * Redistribution and use in source and binary forms, with or without
  10. * modification, are permitted provided that the following conditions
  11. * are met:
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in the
  16. * documentation and/or other materials provided with the distribution.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
  19. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  20. * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  21. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
  22. * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  23. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  24. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  25. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  26. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  27. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28. * POSSIBILITY OF SUCH DAMAGE.
  29. */
  30. #include <sys/cdefs.h>
  31. #ifndef lint
  32. __COPYRIGHT("@(#) Copyright (c) 1999\
  33. The NetBSD Foundation, Inc. All rights reserved.");
  34. __RCSID("$NetBSD: nl.c,v 1.12 2013/09/17 20:00:50 wiz Exp $");
  35. #endif
  36. #include <errno.h>
  37. #include <limits.h>
  38. #include <locale.h>
  39. #include <regex.h>
  40. #include <stdio.h>
  41. #include <stdlib.h>
  42. #include <string.h>
  43. #include <unistd.h>
  44. #include <err.h>
  45. typedef enum {
  46. number_all, /* number all lines */
  47. number_nonempty, /* number non-empty lines */
  48. number_none, /* no line numbering */
  49. number_regex /* number lines matching regular expression */
  50. } numbering_type;
  51. struct numbering_property {
  52. const char * const name; /* for diagnostics */
  53. numbering_type type; /* numbering type */
  54. regex_t expr; /* for type == number_regex */
  55. };
  56. /* line numbering formats */
  57. #define FORMAT_LN "%-*d" /* left justified, leading zeros suppressed */
  58. #define FORMAT_RN "%*d" /* right justified, leading zeros suppressed */
  59. #define FORMAT_RZ "%0*d" /* right justified, leading zeros kept */
  60. #define FOOTER 0
  61. #define BODY 1
  62. #define HEADER 2
  63. #define NP_LAST HEADER
  64. static struct numbering_property numbering_properties[NP_LAST + 1] = {
  65. { "footer", number_none, { 0, 0, 0, 0 } },
  66. { "body", number_nonempty, { 0, 0, 0, 0 } },
  67. { "header", number_none, { 0, 0, 0, 0 } },
  68. };
  69. #define max(a, b) ((a) > (b) ? (a) : (b))
  70. /*
  71. * Maximum number of characters required for a decimal representation of a
  72. * (signed) int; courtesy of tzcode.
  73. */
  74. #define INT_STRLEN_MAXIMUM \
  75. ((sizeof (int) * CHAR_BIT - 1) * 302 / 1000 + 2)
  76. static void filter(void);
  77. static void parse_numbering(const char *, int);
  78. static void usage(void) __attribute__((__noreturn__));
  79. /*
  80. * Pointer to dynamically allocated input line buffer, and its size.
  81. */
  82. static char *buffer;
  83. static size_t buffersize;
  84. /*
  85. * Dynamically allocated buffer suitable for string representation of ints.
  86. */
  87. static char *intbuffer;
  88. static size_t intbuffersize;
  89. /*
  90. * Configurable parameters.
  91. */
  92. /* delimiter characters that indicate the start of a logical page section */
  93. static char delim[2] = { '\\', ':' };
  94. /* line numbering format */
  95. static const char *format = FORMAT_RN;
  96. /* increment value used to number logical page lines */
  97. static int incr = 1;
  98. /* number of adjacent blank lines to be considered (and numbered) as one */
  99. static unsigned int nblank = 1;
  100. /* whether to restart numbering at logical page delimiters */
  101. static int restart = 1;
  102. /* characters used in separating the line number and the corrsp. text line */
  103. static const char *sep = "\t";
  104. /* initial value used to number logical page lines */
  105. static int startnum = 1;
  106. /* number of characters to be used for the line number */
  107. /* should be unsigned but required signed by `*' precision conversion */
  108. static int width = 6;
  109. int
  110. main(int argc, char *argv[])
  111. {
  112. int c;
  113. long val;
  114. unsigned long uval;
  115. char *ep;
  116. (void)setlocale(LC_ALL, "");
  117. /*
  118. * Note: this implementation strictly conforms to the XBD Utility
  119. * Syntax Guidelines and does not permit the optional `file' operand
  120. * to be intermingled with the options, which is defined in the
  121. * XCU specification (Issue 5) but declared an obsolescent feature that
  122. * will be removed from a future issue. It shouldn't matter, though.
  123. */
  124. while ((c = getopt(argc, argv, "pb:d:f:h:i:l:n:s:v:w:")) != -1) {
  125. switch (c) {
  126. case 'p':
  127. restart = 0;
  128. break;
  129. case 'b':
  130. parse_numbering(optarg, BODY);
  131. break;
  132. case 'd':
  133. if (optarg[0] != '\0')
  134. delim[0] = optarg[0];
  135. if (optarg[1] != '\0')
  136. delim[1] = optarg[1];
  137. /* at most two delimiter characters */
  138. if (optarg[2] != '\0') {
  139. errx(EXIT_FAILURE,
  140. "invalid delim argument -- %s",
  141. optarg);
  142. /* NOTREACHED */
  143. }
  144. break;
  145. case 'f':
  146. parse_numbering(optarg, FOOTER);
  147. break;
  148. case 'h':
  149. parse_numbering(optarg, HEADER);
  150. break;
  151. case 'i':
  152. errno = 0;
  153. val = strtol(optarg, &ep, 10);
  154. if ((ep != NULL && *ep != '\0') ||
  155. ((val == LONG_MIN || val == LONG_MAX) && errno != 0))
  156. errx(EXIT_FAILURE,
  157. "invalid incr argument -- %s", optarg);
  158. incr = (int)val;
  159. break;
  160. case 'l':
  161. errno = 0;
  162. uval = strtoul(optarg, &ep, 10);
  163. if ((ep != NULL && *ep != '\0') ||
  164. (uval == ULONG_MAX && errno != 0))
  165. errx(EXIT_FAILURE,
  166. "invalid num argument -- %s", optarg);
  167. nblank = (unsigned int)uval;
  168. break;
  169. case 'n':
  170. if (strcmp(optarg, "ln") == 0) {
  171. format = FORMAT_LN;
  172. } else if (strcmp(optarg, "rn") == 0) {
  173. format = FORMAT_RN;
  174. } else if (strcmp(optarg, "rz") == 0) {
  175. format = FORMAT_RZ;
  176. } else
  177. errx(EXIT_FAILURE,
  178. "illegal format -- %s", optarg);
  179. break;
  180. case 's':
  181. sep = optarg;
  182. break;
  183. case 'v':
  184. errno = 0;
  185. val = strtol(optarg, &ep, 10);
  186. if ((ep != NULL && *ep != '\0') ||
  187. ((val == LONG_MIN || val == LONG_MAX) && errno != 0))
  188. errx(EXIT_FAILURE,
  189. "invalid startnum value -- %s", optarg);
  190. startnum = (int)val;
  191. break;
  192. case 'w':
  193. errno = 0;
  194. val = strtol(optarg, &ep, 10);
  195. if ((ep != NULL && *ep != '\0') ||
  196. ((val == LONG_MIN || val == LONG_MAX) && errno != 0))
  197. errx(EXIT_FAILURE,
  198. "invalid width value -- %s", optarg);
  199. width = (int)val;
  200. if (!(width > 0))
  201. errx(EXIT_FAILURE,
  202. "width argument must be > 0 -- %d",
  203. width);
  204. break;
  205. case '?':
  206. default:
  207. usage();
  208. /* NOTREACHED */
  209. }
  210. }
  211. argc -= optind;
  212. argv += optind;
  213. switch (argc) {
  214. case 0:
  215. break;
  216. case 1:
  217. if (strcmp(argv[0], "-") != 0 &&
  218. freopen(argv[0], "r", stdin) == NULL)
  219. err(EXIT_FAILURE, "Cannot open `%s'", argv[0]);
  220. break;
  221. default:
  222. usage();
  223. /* NOTREACHED */
  224. }
  225. /* Determine the maximum input line length to operate on. */
  226. if ((val = sysconf(_SC_LINE_MAX)) == -1) /* ignore errno */
  227. val = LINE_MAX;
  228. /* Allocate sufficient buffer space (including the terminating NUL). */
  229. buffersize = (size_t)val + 1;
  230. if ((buffer = malloc(buffersize)) == NULL)
  231. err(EXIT_FAILURE, "Cannot allocate input line buffer");
  232. /* Allocate a buffer suitable for preformatting line number. */
  233. intbuffersize = max((int)INT_STRLEN_MAXIMUM, width) + 1; /* NUL */
  234. if ((intbuffer = malloc(intbuffersize)) == NULL)
  235. err(EXIT_FAILURE, "cannot allocate preformatting buffer");
  236. /* Do the work. */
  237. filter();
  238. return EXIT_SUCCESS;
  239. /* NOTREACHED */
  240. }
  241. static void
  242. filter(void)
  243. {
  244. int line; /* logical line number */
  245. int section; /* logical page section */
  246. unsigned int adjblank; /* adjacent blank lines */
  247. int consumed; /* intbuffer measurement */
  248. int donumber, idx;
  249. adjblank = 0;
  250. line = startnum;
  251. section = BODY;
  252. #ifdef __GNUC__
  253. donumber = 0; /* avoid bogus `uninitialized' warning */
  254. #endif
  255. while (fgets(buffer, (int)buffersize, stdin) != NULL) {
  256. for (idx = FOOTER; idx <= NP_LAST; idx++) {
  257. /* Does it look like a delimiter? */
  258. if (buffer[2 * idx + 0] == delim[0] &&
  259. buffer[2 * idx + 1] == delim[1]) {
  260. /* Was this the whole line? */
  261. if (buffer[2 * idx + 2] == '\n') {
  262. section = idx;
  263. adjblank = 0;
  264. if (restart)
  265. line = startnum;
  266. goto nextline;
  267. }
  268. } else {
  269. break;
  270. }
  271. }
  272. switch (numbering_properties[section].type) {
  273. case number_all:
  274. /*
  275. * Doing this for number_all only is disputable, but
  276. * the standard expresses an explicit dependency on
  277. * `-b a' etc.
  278. */
  279. if (buffer[0] == '\n' && ++adjblank < nblank)
  280. donumber = 0;
  281. else
  282. donumber = 1, adjblank = 0;
  283. break;
  284. case number_nonempty:
  285. donumber = (buffer[0] != '\n');
  286. break;
  287. case number_none:
  288. donumber = 0;
  289. break;
  290. case number_regex:
  291. donumber =
  292. (regexec(&numbering_properties[section].expr,
  293. buffer, 0, NULL, 0) == 0);
  294. break;
  295. }
  296. if (donumber) {
  297. consumed = snprintf(intbuffer, intbuffersize, format,
  298. width, line);
  299. (void)printf("%s",
  300. intbuffer + max(0, consumed - width));
  301. line += incr;
  302. } else {
  303. (void)printf("%*s", width, "");
  304. }
  305. (void)printf("%s%s", sep, buffer);
  306. if (ferror(stdout))
  307. err(EXIT_FAILURE, "output error");
  308. nextline:
  309. ;
  310. }
  311. if (ferror(stdin))
  312. err(EXIT_FAILURE, "input error");
  313. }
  314. /*
  315. * Various support functions.
  316. */
  317. static void
  318. parse_numbering(const char *argstr, int section)
  319. {
  320. int error;
  321. char errorbuf[NL_TEXTMAX];
  322. switch (argstr[0]) {
  323. case 'a':
  324. numbering_properties[section].type = number_all;
  325. break;
  326. case 'n':
  327. numbering_properties[section].type = number_none;
  328. break;
  329. case 't':
  330. numbering_properties[section].type = number_nonempty;
  331. break;
  332. case 'p':
  333. /* If there was a previous expression, throw it away. */
  334. if (numbering_properties[section].type == number_regex)
  335. regfree(&numbering_properties[section].expr);
  336. else
  337. numbering_properties[section].type = number_regex;
  338. /* Compile/validate the supplied regular expression. */
  339. if ((error = regcomp(&numbering_properties[section].expr,
  340. &argstr[1], REG_NEWLINE|REG_NOSUB)) != 0) {
  341. (void)regerror(error,
  342. &numbering_properties[section].expr,
  343. errorbuf, sizeof (errorbuf));
  344. errx(EXIT_FAILURE,
  345. "%s expr: %s -- %s",
  346. numbering_properties[section].name, errorbuf,
  347. &argstr[1]);
  348. }
  349. break;
  350. default:
  351. errx(EXIT_FAILURE,
  352. "illegal %s line numbering type -- %s",
  353. numbering_properties[section].name, argstr);
  354. exit(EXIT_FAILURE);
  355. }
  356. }
  357. static void
  358. usage(void)
  359. {
  360. (void)fprintf(stderr, "Usage: %s [-p] [-b type] [-d delim] [-f type] "
  361. "[-h type] [-i incr] [-l num]\n\t[-n format] [-s sep] "
  362. "[-v startnum] [-w width] [file]\n", getprogname());
  363. exit(EXIT_FAILURE);
  364. }