/usr.bin/sed/main.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 487 lines · 367 code · 36 blank · 84 comment · 116 complexity · 2064a63a55e2cf7de520800c6e0aca92 MD5 · raw file

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