main.c revision 1.22
1/* $NetBSD: main.c,v 1.22 2014/06/06 00:13:13 christos Exp $ */ 2 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 37#if HAVE_NBTOOL_CONFIG_H 38#include "nbtool_config.h" 39#endif 40 41#include <sys/cdefs.h> 42__RCSID("$NetBSD: main.c,v 1.22 2014/06/06 00:13:13 christos Exp $"); 43#ifdef __FBSDID 44__FBSDID("$FreeBSD: head/usr.bin/sed/main.c 252231 2013-06-26 04:14:19Z pfg $"); 45#endif 46 47#ifndef lint 48__COPYRIGHT("@(#) Copyright (c) 1992, 1993\ 49 The Regents of the University of California. All rights reserved."); 50#endif 51 52#ifndef lint 53static const char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/3/94"; 54#endif 55 56#include <sys/types.h> 57#include <sys/mman.h> 58#include <sys/param.h> 59#include <sys/stat.h> 60 61#include <err.h> 62#include <errno.h> 63#include <fcntl.h> 64#include <libgen.h> 65#include <limits.h> 66#include <locale.h> 67#include <regex.h> 68#include <stddef.h> 69#define _WITH_GETLINE 70#include <stdio.h> 71#include <stdlib.h> 72#include <string.h> 73#include <unistd.h> 74 75#include "defs.h" 76#include "extern.h" 77 78/* 79 * Linked list of units (strings and files) to be compiled 80 */ 81struct s_compunit { 82 struct s_compunit *next; 83 enum e_cut {CU_FILE, CU_STRING} type; 84 char *s; /* Pointer to string or fname */ 85}; 86 87/* 88 * Linked list pointer to compilation units and pointer to current 89 * next pointer. 90 */ 91static struct s_compunit *script, **cu_nextp = &script; 92 93/* 94 * Linked list of files to be processed 95 */ 96struct s_flist { 97 char *fname; 98 struct s_flist *next; 99}; 100 101/* 102 * Linked list pointer to files and pointer to current 103 * next pointer. 104 */ 105static struct s_flist *files, **fl_nextp = &files; 106 107FILE *infile; /* Current input file */ 108FILE *outfile; /* Current output file */ 109 110int aflag, eflag, nflag; 111int rflags = 0; 112static int rval; /* Exit status */ 113 114static int ispan; /* Whether inplace editing spans across files */ 115 116/* 117 * Current file and line number; line numbers restart across compilation 118 * units, but span across input files. The latter is optional if editing 119 * in place. 120 */ 121const char *fname; /* File name. */ 122const char *outfname; /* Output file name */ 123static char oldfname[PATH_MAX]; /* Old file name (for in-place editing) */ 124static char tmpfname[PATH_MAX]; /* Temporary file name (for in-place editing) */ 125static const char *inplace; /* Inplace edit file extension. */ 126u_long linenum; 127 128static void add_compunit(enum e_cut, char *); 129static void add_file(char *); 130static void usage(void); 131 132int 133main(int argc, char *argv[]) 134{ 135 int c, fflag; 136 char *temp_arg; 137 138 setprogname(argv[0]); 139 (void) setlocale(LC_ALL, ""); 140 141 fflag = 0; 142 inplace = NULL; 143 144 while ((c = getopt(argc, argv, "EI::ae:f:i::lnr")) != -1) 145 switch (c) { 146 case 'r': /* Gnu sed compat */ 147 case 'E': 148 rflags = REG_EXTENDED; 149 break; 150 case 'I': 151 inplace = optarg ? optarg : __UNCONST(""); 152 ispan = 1; /* span across input files */ 153 break; 154 case 'a': 155 aflag = 1; 156 break; 157 case 'e': 158 eflag = 1; 159 temp_arg = xmalloc(strlen(optarg) + 2); 160 strcpy(temp_arg, optarg); 161 strcat(temp_arg, "\n"); 162 add_compunit(CU_STRING, temp_arg); 163 break; 164 case 'f': 165 fflag = 1; 166 add_compunit(CU_FILE, optarg); 167 break; 168 case 'i': 169 inplace = optarg ? optarg : __UNCONST(""); 170 ispan = 0; /* don't span across input files */ 171 break; 172 case 'l': 173 if(setlinebuf(stdout) != 0) 174 warnx("setlinebuf() failed"); 175 break; 176 case 'n': 177 nflag = 1; 178 break; 179 default: 180 case '?': 181 usage(); 182 } 183 argc -= optind; 184 argv += optind; 185 186 /* First usage case; script is the first arg */ 187 if (!eflag && !fflag && *argv) { 188 add_compunit(CU_STRING, *argv); 189 argv++; 190 } 191 192 compile(); 193 194 /* Continue with first and start second usage */ 195 if (*argv) 196 for (; *argv; argv++) 197 add_file(*argv); 198 else 199 add_file(NULL); 200 process(); 201 cfclose(prog, NULL); 202 if (fclose(stdout)) 203 err(1, "stdout"); 204 exit(rval); 205} 206 207static void 208usage(void) 209{ 210 (void)fprintf(stderr, "%s\n%s\n", 211 "usage: sed script [-Ealn] [-i extension] [file ...]", 212 " sed [-Ealn] [-i extension] [-e script] ... [-f script_file] ... [file ...]"); 213 exit(1); 214} 215 216/* 217 * Like fgets, but go through the chain of compilation units chaining them 218 * together. Empty strings and files are ignored. 219 */ 220char * 221cu_fgets(char *buf, int n, int *more) 222{ 223 static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF; 224 static FILE *f; /* Current open file */ 225 static char *s; /* Current pointer inside string */ 226 static char string_ident[30]; 227 char *p; 228 229again: 230 switch (state) { 231 case ST_EOF: 232 if (script == NULL) { 233 if (more != NULL) 234 *more = 0; 235 return (NULL); 236 } 237 linenum = 0; 238 switch (script->type) { 239 case CU_FILE: 240 if ((f = fopen(script->s, "r")) == NULL) 241 err(1, "%s", script->s); 242 fname = script->s; 243 state = ST_FILE; 244 goto again; 245 case CU_STRING: 246 if (((size_t)snprintf(string_ident, 247 sizeof(string_ident), "\"%s\"", script->s)) >= 248 sizeof(string_ident) - 1) 249 (void)strcpy(string_ident + 250 sizeof(string_ident) - 6, " ...\""); 251 fname = string_ident; 252 s = script->s; 253 state = ST_STRING; 254 goto again; 255 } 256 case ST_FILE: 257 if ((p = fgets(buf, n, f)) != NULL) { 258 linenum++; 259 if (linenum == 1 && buf[0] == '#' && buf[1] == 'n') 260 nflag = 1; 261 if (more != NULL) 262 *more = !feof(f); 263 return (p); 264 } 265 script = script->next; 266 (void)fclose(f); 267 state = ST_EOF; 268 goto again; 269 case ST_STRING: 270 if (linenum == 0 && s[0] == '#' && s[1] == 'n') 271 nflag = 1; 272 p = buf; 273 for (;;) { 274 if (n-- <= 1) { 275 *p = '\0'; 276 linenum++; 277 if (more != NULL) 278 *more = 1; 279 return (buf); 280 } 281 switch (*s) { 282 case '\0': 283 state = ST_EOF; 284 if (s == script->s) { 285 script = script->next; 286 goto again; 287 } else { 288 script = script->next; 289 *p = '\0'; 290 linenum++; 291 if (more != NULL) 292 *more = 0; 293 return (buf); 294 } 295 case '\n': 296 *p++ = '\n'; 297 *p = '\0'; 298 s++; 299 linenum++; 300 if (more != NULL) 301 *more = 0; 302 return (buf); 303 default: 304 *p++ = *s++; 305 } 306 } 307 } 308 /* NOTREACHED */ 309 return (NULL); 310} 311 312/* 313 * Like fgets, but go through the list of files chaining them together. 314 * Set len to the length of the line. 315 */ 316int 317mf_fgets(SPACE *sp, enum e_spflag spflag) 318{ 319 struct stat sb; 320 size_t len; 321 static char *p = NULL; 322 static size_t plen = 0; 323 int c; 324 static int firstfile; 325 326 if (infile == NULL) { 327 /* stdin? */ 328 if (files->fname == NULL) { 329 if (inplace != NULL) 330 errx(1, "-I or -i may not be used with stdin"); 331 infile = stdin; 332 fname = "stdin"; 333 outfile = stdout; 334 outfname = "stdout"; 335 } 336 firstfile = 1; 337 } 338 339 for (;;) { 340 if (infile != NULL && (c = getc(infile)) != EOF) { 341 (void)ungetc(c, infile); 342 break; 343 } 344 /* If we are here then either eof or no files are open yet */ 345 if (infile == stdin) { 346 sp->len = 0; 347 return (0); 348 } 349 if (infile != NULL) { 350 fclose(infile); 351 if (*oldfname != '\0') { 352 /* if there was a backup file, remove it */ 353 unlink(oldfname); 354 /* 355 * Backup the original. Note that hard links 356 * are not supported on all filesystems. 357 */ 358 if ((link(fname, oldfname) != 0) && 359 (rename(fname, oldfname) != 0)) { 360 warn("rename()"); 361 if (*tmpfname) 362 unlink(tmpfname); 363 exit(1); 364 } 365 *oldfname = '\0'; 366 } 367 if (*tmpfname != '\0') { 368 if (outfile != NULL && outfile != stdout) 369 if (fclose(outfile) != 0) { 370 warn("fclose()"); 371 unlink(tmpfname); 372 exit(1); 373 } 374 outfile = NULL; 375 if (rename(tmpfname, fname) != 0) { 376 /* this should not happen really! */ 377 warn("rename()"); 378 unlink(tmpfname); 379 exit(1); 380 } 381 *tmpfname = '\0'; 382 } 383 outfname = NULL; 384 } 385 if (firstfile == 0) 386 files = files->next; 387 else 388 firstfile = 0; 389 if (files == NULL) { 390 sp->len = 0; 391 return (0); 392 } 393 fname = files->fname; 394 if (inplace != NULL) { 395 if (lstat(fname, &sb) != 0) 396 err(1, "%s", fname); 397 if (!(sb.st_mode & S_IFREG)) 398 errx(1, "%s: %s %s", fname, 399 "in-place editing only", 400 "works for regular files"); 401 if (*inplace != '\0') { 402 strlcpy(oldfname, fname, 403 sizeof(oldfname)); 404 len = strlcat(oldfname, inplace, 405 sizeof(oldfname)); 406 if (len > sizeof(oldfname)) 407 errx(1, "%s: name too long", fname); 408 } 409 char d_name[PATH_MAX], f_name[PATH_MAX]; 410 (void)strlcpy(d_name, fname, sizeof(d_name)); 411 (void)strlcpy(f_name, fname, sizeof(f_name)); 412 len = (size_t)snprintf(tmpfname, sizeof(tmpfname), 413 "%s/.!%ld!%s", dirname(d_name), (long)getpid(), 414 basename(f_name)); 415 if (len >= sizeof(tmpfname)) 416 errx(1, "%s: name too long", fname); 417 unlink(tmpfname); 418 if ((outfile = fopen(tmpfname, "w")) == NULL) 419 err(1, "%s", fname); 420 fchown(fileno(outfile), sb.st_uid, sb.st_gid); 421 fchmod(fileno(outfile), sb.st_mode & ALLPERMS); 422 outfname = tmpfname; 423 if (!ispan) { 424 linenum = 0; 425 resetstate(); 426 } 427 } else { 428 outfile = stdout; 429 outfname = "stdout"; 430 } 431 if ((infile = fopen(fname, "r")) == NULL) { 432 warn("%s", fname); 433 rval = 1; 434 continue; 435 } 436 } 437 /* 438 * We are here only when infile is open and we still have something 439 * to read from it. 440 * 441 * Use getline() so that we can handle essentially infinite input 442 * data. The p and plen are static so each invocation gives 443 * getline() the same buffer which is expanded as needed. 444 */ 445 ssize_t slen = getline(&p, &plen, infile); 446 if (slen == -1) 447 err(1, "%s", fname); 448 if (slen != 0 && p[slen - 1] == '\n') 449 slen--; 450 cspace(sp, p, (size_t)slen, spflag); 451 452 linenum++; 453 454 return (1); 455} 456 457/* 458 * Add a compilation unit to the linked list 459 */ 460static void 461add_compunit(enum e_cut type, char *s) 462{ 463 struct s_compunit *cu; 464 465 cu = xmalloc(sizeof(struct s_compunit)); 466 cu->type = type; 467 cu->s = s; 468 cu->next = NULL; 469 *cu_nextp = cu; 470 cu_nextp = &cu->next; 471} 472 473/* 474 * Add a file to the linked list 475 */ 476static void 477add_file(char *s) 478{ 479 struct s_flist *fp; 480 481 fp = xmalloc(sizeof(struct s_flist)); 482 fp->next = NULL; 483 *fl_nextp = fp; 484 fp->fname = s; 485 fl_nextp = &fp->next; 486} 487 488int 489lastline(void) 490{ 491 int ch; 492 493 if (files->next != NULL && (inplace == NULL || ispan)) 494 return (0); 495 if ((ch = getc(infile)) == EOF) 496 return (1); 497 ungetc(ch, infile); 498 return (0); 499} 500