PageRenderTime 26ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/usr.bin/sed/main.c

https://github.com/freebsd/freebsd
C | 557 lines | 425 code | 41 blank | 91 comment | 143 complexity | 1bb2855e6b566528ef122110528a33c0 MD5 | raw file
  1. /*-
  2. * SPDX-License-Identifier: BSD-3-Clause
  3. *
  4. * Copyright (c) 2013 Johann 'Myrkraverk' Oskarsson.
  5. * Copyright (c) 1992 Diomidis Spinellis.
  6. * Copyright (c) 1992, 1993
  7. * The Regents of the University of California. All rights reserved.
  8. *
  9. * This code is derived from software contributed to Berkeley by
  10. * Diomidis Spinellis of Imperial College, University of London.
  11. *
  12. * Redistribution and use in source and binary forms, with or without
  13. * modification, are permitted provided that the following conditions
  14. * are met:
  15. * 1. Redistributions of source code must retain the above copyright
  16. * notice, this list of conditions and the following disclaimer.
  17. * 2. Redistributions in binary form must reproduce the above copyright
  18. * notice, this list of conditions and the following disclaimer in the
  19. * documentation and/or other materials provided with the distribution.
  20. * 3. Neither the name of the University nor the names of its contributors
  21. * may be used to endorse or promote products derived from this software
  22. * without specific prior written permission.
  23. *
  24. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  25. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  26. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  27. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  28. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  29. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  30. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  31. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  32. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  33. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  34. * SUCH DAMAGE.
  35. */
  36. #include <sys/cdefs.h>
  37. __FBSDID("$FreeBSD$");
  38. #ifndef lint
  39. static const char copyright[] =
  40. "@(#) Copyright (c) 1992, 1993\n\
  41. The Regents of the University of California. All rights reserved.\n";
  42. #endif
  43. #ifndef lint
  44. static const char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/3/94";
  45. #endif
  46. #include <sys/types.h>
  47. #include <sys/mman.h>
  48. #include <sys/param.h>
  49. #include <sys/stat.h>
  50. #include <err.h>
  51. #include <errno.h>
  52. #include <fcntl.h>
  53. #include <libgen.h>
  54. #include <limits.h>
  55. #include <locale.h>
  56. #include <regex.h>
  57. #include <stddef.h>
  58. #include <stdio.h>
  59. #include <stdlib.h>
  60. #include <string.h>
  61. #include <unistd.h>
  62. #include "defs.h"
  63. #include "extern.h"
  64. /*
  65. * Linked list of units (strings and files) to be compiled
  66. */
  67. struct s_compunit {
  68. struct s_compunit *next;
  69. enum e_cut {CU_FILE, CU_STRING} type;
  70. char *s; /* Pointer to string or fname */
  71. };
  72. /*
  73. * Linked list pointer to compilation units and pointer to current
  74. * next pointer.
  75. */
  76. static struct s_compunit *script, **cu_nextp = &script;
  77. /*
  78. * Linked list of files to be processed
  79. */
  80. struct s_flist {
  81. char *fname;
  82. struct s_flist *next;
  83. };
  84. /*
  85. * Linked list pointer to files and pointer to current
  86. * next pointer.
  87. */
  88. static struct s_flist *files, **fl_nextp = &files;
  89. FILE *infile; /* Current input file */
  90. FILE *outfile; /* Current output file */
  91. int aflag, eflag, nflag;
  92. int rflags = 0;
  93. int quit = 0;
  94. static int rval; /* Exit status */
  95. static int ispan; /* Whether inplace editing spans across files */
  96. /*
  97. * Current file and line number; line numbers restart across compilation
  98. * units, but span across input files. The latter is optional if editing
  99. * in place.
  100. */
  101. const char *fname; /* File name. */
  102. const char *outfname; /* Output file name */
  103. static char oldfname[PATH_MAX]; /* Old file name (for in-place editing) */
  104. static char tmpfname[PATH_MAX]; /* Temporary file name (for in-place editing) */
  105. const char *inplace; /* Inplace edit file extension. */
  106. u_long linenum;
  107. static void add_compunit(enum e_cut, char *);
  108. static void add_file(char *);
  109. static void usage(void);
  110. int
  111. main(int argc, char *argv[])
  112. {
  113. int c, fflag, fflagstdin;
  114. char *temp_arg;
  115. (void) setlocale(LC_ALL, "");
  116. fflag = 0;
  117. fflagstdin = 0;
  118. inplace = NULL;
  119. while ((c = getopt(argc, argv, "EI:ae:f:i:lnru")) != -1)
  120. switch (c) {
  121. case 'r': /* Gnu sed compat */
  122. case 'E':
  123. rflags = REG_EXTENDED;
  124. break;
  125. case 'I':
  126. inplace = optarg;
  127. ispan = 1; /* span across input files */
  128. break;
  129. case 'a':
  130. aflag = 1;
  131. break;
  132. case 'e':
  133. eflag = 1;
  134. if ((temp_arg = malloc(strlen(optarg) + 2)) == NULL)
  135. err(1, "malloc");
  136. strcpy(temp_arg, optarg);
  137. strcat(temp_arg, "\n");
  138. add_compunit(CU_STRING, temp_arg);
  139. break;
  140. case 'f':
  141. fflag = 1;
  142. if (strcmp(optarg, "-") == 0)
  143. fflagstdin = 1;
  144. add_compunit(CU_FILE, optarg);
  145. break;
  146. case 'i':
  147. inplace = optarg;
  148. ispan = 0; /* don't span across input files */
  149. break;
  150. case 'l':
  151. if(setvbuf(stdout, NULL, _IOLBF, 0) != 0)
  152. warnx("setting line buffered output failed");
  153. break;
  154. case 'n':
  155. nflag = 1;
  156. break;
  157. case 'u':
  158. if(setvbuf(stdout, NULL, _IONBF, 0) != 0)
  159. warnx("setting unbuffered output failed");
  160. break;
  161. default:
  162. case '?':
  163. usage();
  164. }
  165. argc -= optind;
  166. argv += optind;
  167. /* First usage case; script is the first arg */
  168. if (!eflag && !fflag && *argv) {
  169. add_compunit(CU_STRING, *argv);
  170. argv++;
  171. }
  172. compile();
  173. /* Continue with first and start second usage */
  174. if (*argv)
  175. for (; *argv; argv++)
  176. add_file(*argv);
  177. else if (fflagstdin)
  178. exit(rval);
  179. else
  180. add_file(NULL);
  181. process();
  182. cfclose(prog, NULL);
  183. if (fclose(stdout))
  184. err(1, "stdout");
  185. exit(rval);
  186. }
  187. static void
  188. usage(void)
  189. {
  190. (void)fprintf(stderr,
  191. "usage: %s script [-Ealnru] [-i extension] [file ...]\n"
  192. "\t%s [-Ealnu] [-i extension] [-e script] ... [-f script_file]"
  193. " ... [file ...]\n", getprogname(), getprogname());
  194. exit(1);
  195. }
  196. /*
  197. * Like fgets, but go through the chain of compilation units chaining them
  198. * together. Empty strings and files are ignored.
  199. */
  200. char *
  201. cu_fgets(char *buf, int n, int *more)
  202. {
  203. static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF;
  204. static FILE *f; /* Current open file */
  205. static char *s; /* Current pointer inside string */
  206. static char string_ident[30];
  207. char *p;
  208. again:
  209. switch (state) {
  210. case ST_EOF:
  211. if (script == NULL) {
  212. if (more != NULL)
  213. *more = 0;
  214. return (NULL);
  215. }
  216. linenum = 0;
  217. switch (script->type) {
  218. case CU_FILE:
  219. if (strcmp(script->s, "-") == 0) {
  220. f = stdin;
  221. fname = "stdin";
  222. } else {
  223. if ((f = fopen(script->s, "r")) == NULL)
  224. err(1, "%s", script->s);
  225. fname = script->s;
  226. }
  227. state = ST_FILE;
  228. goto again;
  229. case CU_STRING:
  230. if (((size_t)snprintf(string_ident,
  231. sizeof(string_ident), "\"%s\"", script->s)) >=
  232. sizeof(string_ident) - 1)
  233. (void)strcpy(string_ident +
  234. sizeof(string_ident) - 6, " ...\"");
  235. fname = string_ident;
  236. s = script->s;
  237. state = ST_STRING;
  238. goto again;
  239. default:
  240. __unreachable();
  241. }
  242. case ST_FILE:
  243. if ((p = fgets(buf, n, f)) != NULL) {
  244. linenum++;
  245. if (linenum == 1 && buf[0] == '#' && buf[1] == 'n')
  246. nflag = 1;
  247. if (more != NULL)
  248. *more = !feof(f);
  249. return (p);
  250. }
  251. script = script->next;
  252. (void)fclose(f);
  253. state = ST_EOF;
  254. goto again;
  255. case ST_STRING:
  256. if (linenum == 0 && s[0] == '#' && s[1] == 'n')
  257. nflag = 1;
  258. p = buf;
  259. for (;;) {
  260. if (n-- <= 1) {
  261. *p = '\0';
  262. linenum++;
  263. if (more != NULL)
  264. *more = 1;
  265. return (buf);
  266. }
  267. switch (*s) {
  268. case '\0':
  269. state = ST_EOF;
  270. if (s == script->s) {
  271. script = script->next;
  272. goto again;
  273. } else {
  274. script = script->next;
  275. *p = '\0';
  276. linenum++;
  277. if (more != NULL)
  278. *more = 0;
  279. return (buf);
  280. }
  281. case '\n':
  282. *p++ = '\n';
  283. *p = '\0';
  284. s++;
  285. linenum++;
  286. if (more != NULL)
  287. *more = 0;
  288. return (buf);
  289. default:
  290. *p++ = *s++;
  291. }
  292. }
  293. }
  294. /* NOTREACHED */
  295. return (NULL);
  296. }
  297. /*
  298. * Like fgets, but go through the list of files chaining them together.
  299. * Set len to the length of the line.
  300. */
  301. int
  302. mf_fgets(SPACE *sp, enum e_spflag spflag)
  303. {
  304. struct stat sb;
  305. ssize_t len;
  306. char *dirbuf, *basebuf;
  307. static char *p = NULL;
  308. static size_t plen = 0;
  309. int c;
  310. static int firstfile;
  311. if (infile == NULL) {
  312. /* stdin? */
  313. if (files->fname == NULL) {
  314. if (inplace != NULL)
  315. errx(1, "-I or -i may not be used with stdin");
  316. infile = stdin;
  317. fname = "stdin";
  318. outfile = stdout;
  319. outfname = "stdout";
  320. }
  321. firstfile = 1;
  322. }
  323. for (;;) {
  324. if (infile != NULL && (c = getc(infile)) != EOF && !quit) {
  325. (void)ungetc(c, infile);
  326. break;
  327. }
  328. /* If we are here then either eof or no files are open yet */
  329. if (infile == stdin) {
  330. sp->len = 0;
  331. return (0);
  332. }
  333. if (infile != NULL) {
  334. fclose(infile);
  335. if (*oldfname != '\0') {
  336. /* if there was a backup file, remove it */
  337. unlink(oldfname);
  338. /*
  339. * Backup the original. Note that hard links
  340. * are not supported on all filesystems.
  341. */
  342. if ((link(fname, oldfname) != 0) &&
  343. (rename(fname, oldfname) != 0)) {
  344. warn("rename()");
  345. if (*tmpfname)
  346. unlink(tmpfname);
  347. exit(1);
  348. }
  349. *oldfname = '\0';
  350. }
  351. if (*tmpfname != '\0') {
  352. if (outfile != NULL && outfile != stdout)
  353. if (fclose(outfile) != 0) {
  354. warn("fclose()");
  355. unlink(tmpfname);
  356. exit(1);
  357. }
  358. outfile = NULL;
  359. if (rename(tmpfname, fname) != 0) {
  360. /* this should not happen really! */
  361. warn("rename()");
  362. unlink(tmpfname);
  363. exit(1);
  364. }
  365. *tmpfname = '\0';
  366. }
  367. outfname = NULL;
  368. }
  369. if (firstfile == 0)
  370. files = files->next;
  371. else
  372. firstfile = 0;
  373. if (files == NULL) {
  374. sp->len = 0;
  375. return (0);
  376. }
  377. fname = files->fname;
  378. if (inplace != NULL) {
  379. if (lstat(fname, &sb) != 0)
  380. err(1, "%s", fname);
  381. if (!S_ISREG(sb.st_mode))
  382. errx(1, "%s: %s %s", fname,
  383. "in-place editing only",
  384. "works for regular files");
  385. if (*inplace != '\0') {
  386. strlcpy(oldfname, fname,
  387. sizeof(oldfname));
  388. len = strlcat(oldfname, inplace,
  389. sizeof(oldfname));
  390. if (len > (ssize_t)sizeof(oldfname))
  391. errx(1, "%s: name too long", fname);
  392. }
  393. if ((dirbuf = strdup(fname)) == NULL ||
  394. (basebuf = strdup(fname)) == NULL)
  395. err(1, "strdup");
  396. len = snprintf(tmpfname, sizeof(tmpfname),
  397. "%s/.!%ld!%s", dirname(dirbuf), (long)getpid(),
  398. basename(basebuf));
  399. free(dirbuf);
  400. free(basebuf);
  401. if (len >= (ssize_t)sizeof(tmpfname))
  402. errx(1, "%s: name too long", fname);
  403. unlink(tmpfname);
  404. if (outfile != NULL && outfile != stdout)
  405. fclose(outfile);
  406. if ((outfile = fopen(tmpfname, "w")) == NULL)
  407. err(1, "%s", fname);
  408. fchown(fileno(outfile), sb.st_uid, sb.st_gid);
  409. fchmod(fileno(outfile), sb.st_mode & ALLPERMS);
  410. outfname = tmpfname;
  411. if (!ispan) {
  412. linenum = 0;
  413. resetstate();
  414. }
  415. } else {
  416. outfile = stdout;
  417. outfname = "stdout";
  418. }
  419. if ((infile = fopen(fname, "r")) == NULL) {
  420. warn("%s", fname);
  421. rval = 1;
  422. continue;
  423. }
  424. }
  425. /*
  426. * We are here only when infile is open and we still have something
  427. * to read from it.
  428. *
  429. * Use getline() so that we can handle essentially infinite input
  430. * data. The p and plen are static so each invocation gives
  431. * getline() the same buffer which is expanded as needed.
  432. */
  433. len = getline(&p, &plen, infile);
  434. if (len == -1)
  435. err(1, "%s", fname);
  436. if (len != 0 && p[len - 1] == '\n') {
  437. sp->append_newline = 1;
  438. len--;
  439. } else if (!lastline()) {
  440. sp->append_newline = 1;
  441. } else {
  442. sp->append_newline = 0;
  443. }
  444. cspace(sp, p, len, spflag);
  445. linenum++;
  446. return (1);
  447. }
  448. /*
  449. * Add a compilation unit to the linked list
  450. */
  451. static void
  452. add_compunit(enum e_cut type, char *s)
  453. {
  454. struct s_compunit *cu;
  455. if ((cu = malloc(sizeof(struct s_compunit))) == NULL)
  456. err(1, "malloc");
  457. cu->type = type;
  458. cu->s = s;
  459. cu->next = NULL;
  460. *cu_nextp = cu;
  461. cu_nextp = &cu->next;
  462. }
  463. /*
  464. * Add a file to the linked list
  465. */
  466. static void
  467. add_file(char *s)
  468. {
  469. struct s_flist *fp;
  470. if ((fp = malloc(sizeof(struct s_flist))) == NULL)
  471. err(1, "malloc");
  472. fp->next = NULL;
  473. *fl_nextp = fp;
  474. fp->fname = s;
  475. fl_nextp = &fp->next;
  476. }
  477. static int
  478. next_files_have_lines(void)
  479. {
  480. struct s_flist *file;
  481. FILE *file_fd;
  482. int ch;
  483. file = files;
  484. while ((file = file->next) != NULL) {
  485. if ((file_fd = fopen(file->fname, "r")) == NULL)
  486. continue;
  487. if ((ch = getc(file_fd)) != EOF) {
  488. /*
  489. * This next file has content, therefore current
  490. * file doesn't contains the last line.
  491. */
  492. ungetc(ch, file_fd);
  493. fclose(file_fd);
  494. return (1);
  495. }
  496. fclose(file_fd);
  497. }
  498. return (0);
  499. }
  500. int
  501. lastline(void)
  502. {
  503. int ch;
  504. if (feof(infile)) {
  505. return !(
  506. (inplace == NULL || ispan) &&
  507. next_files_have_lines());
  508. }
  509. if ((ch = getc(infile)) == EOF) {
  510. return !(
  511. (inplace == NULL || ispan) &&
  512. next_files_have_lines());
  513. }
  514. ungetc(ch, infile);
  515. return (0);
  516. }