Index: head/usr.bin/calendar/io.c =================================================================== --- head/usr.bin/calendar/io.c (revision 303525) +++ head/usr.bin/calendar/io.c (revision 303526) @@ -1,503 +1,502 @@ /*- * Copyright (c) 1989, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif #if 0 #ifndef lint static char sccsid[] = "@(#)calendar.c 8.3 (Berkeley) 3/25/94"; #endif #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include -#define _WITH_GETLINE #include #include #include #include #include #include "pathnames.h" #include "calendar.h" enum { T_OK = 0, T_ERR, T_PROCESS, }; const char *calendarFile = "calendar"; /* default calendar file */ static const char *calendarHomes[] = {".calendar", _PATH_INCLUDE}; /* HOME */ static const char *calendarNoMail = "nomail";/* don't sent mail if file exist */ static char path[MAXPATHLEN]; struct fixs neaster, npaskha, ncny, nfullmoon, nnewmoon; struct fixs nmarequinox, nsepequinox, njunsolstice, ndecsolstice; static int cal_parse(FILE *in, FILE *out); static StringList *definitions = NULL; static struct event *events[MAXCOUNT]; static char *extradata[MAXCOUNT]; static void trimlr(char **buf) { char *walk = *buf; char *last; while (isspace(*walk)) walk++; if (*walk != '\0') { last = walk + strlen(walk) - 1; while (last > walk && isspace(*last)) last--; *(last+1) = 0; } *buf = walk; } static FILE * cal_fopen(const char *file) { FILE *fp; char *home = getenv("HOME"); unsigned int i; if (home == NULL || *home == '\0') { warnx("Cannot get home directory"); return (NULL); } if (chdir(home) != 0) { warnx("Cannot enter home directory"); return (NULL); } for (i = 0; i < sizeof(calendarHomes)/sizeof(calendarHomes[0]) ; i++) { if (chdir(calendarHomes[i]) != 0) continue; if ((fp = fopen(file, "r")) != NULL) return (fp); } warnx("can't open calendar file \"%s\"", file); return (NULL); } static int token(char *line, FILE *out, bool *skip) { char *walk, c, a; if (strncmp(line, "endif", 5) == 0) { *skip = false; return (T_OK); } if (*skip) return (T_OK); if (strncmp(line, "include", 7) == 0) { walk = line + 7; trimlr(&walk); if (*walk == '\0') { warnx("Expecting arguments after #include"); return (T_ERR); } if (*walk != '<' && *walk != '\"') { warnx("Excecting '<' or '\"' after #include"); return (T_ERR); } a = *walk; walk++; c = walk[strlen(walk) - 1]; switch(c) { case '>': if (a != '<') { warnx("Unterminated include expecting '\"'"); return (T_ERR); } break; case '\"': if (a != '\"') { warnx("Unterminated include expecting '>'"); return (T_ERR); } break; default: warnx("Unterminated include expecting '%c'", a == '<' ? '>' : '\"' ); return (T_ERR); } walk[strlen(walk) - 1] = '\0'; if (cal_parse(cal_fopen(walk), out)) return (T_ERR); return (T_OK); } if (strncmp(line, "define", 6) == 0) { if (definitions == NULL) definitions = sl_init(); walk = line + 6; trimlr(&walk); if (*walk == '\0') { warnx("Expecting arguments after #define"); return (T_ERR); } sl_add(definitions, strdup(walk)); return (T_OK); } if (strncmp(line, "ifndef", 6) == 0) { walk = line + 6; trimlr(&walk); if (*walk == '\0') { warnx("Expecting arguments after #ifndef"); return (T_ERR); } if (definitions != NULL && sl_find(definitions, walk) != NULL) *skip = true; return (T_OK); } return (T_PROCESS); } #define REPLACE(string, slen, struct_) \ if (strncasecmp(buf, (string), (slen)) == 0 && buf[(slen)]) { \ if (struct_.name != NULL) \ free(struct_.name); \ if ((struct_.name = strdup(buf + (slen))) == NULL) \ errx(1, "cannot allocate memory"); \ struct_.len = strlen(buf + (slen)); \ continue; \ } static int cal_parse(FILE *in, FILE *out) { char *line = NULL; char *buf; size_t linecap = 0; ssize_t linelen; ssize_t l; static int d_first = -1; static int count = 0; int i; int month[MAXCOUNT]; int day[MAXCOUNT]; int year[MAXCOUNT]; bool skip = false; char dbuf[80]; char *pp, p; struct tm tm; int flags; /* Unused */ tm.tm_sec = 0; tm.tm_min = 0; tm.tm_hour = 0; tm.tm_wday = 0; if (in == NULL) return (1); while ((linelen = getline(&line, &linecap, in)) > 0) { if (*line == '#') { switch (token(line+1, out, &skip)) { case T_ERR: free(line); return (1); case T_OK: continue; case T_PROCESS: break; default: break; } } if (skip) continue; buf = line; for (l = linelen; l > 0 && isspace((unsigned char)buf[l - 1]); l--) ; buf[l] = '\0'; if (buf[0] == '\0') continue; /* Parse special definitions: LANG, Easter, Paskha etc */ if (strncmp(buf, "LANG=", 5) == 0) { (void)setlocale(LC_ALL, buf + 5); d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); setnnames(); continue; } REPLACE("Easter=", 7, neaster); REPLACE("Paskha=", 7, npaskha); REPLACE("ChineseNewYear=", 15, ncny); REPLACE("NewMoon=", 8, nnewmoon); REPLACE("FullMoon=", 9, nfullmoon); REPLACE("MarEquinox=", 11, nmarequinox); REPLACE("SepEquinox=", 11, nsepequinox); REPLACE("JunSolstice=", 12, njunsolstice); REPLACE("DecSolstice=", 12, ndecsolstice); if (strncmp(buf, "SEQUENCE=", 9) == 0) { setnsequences(buf + 9); continue; } /* * If the line starts with a tab, the data has to be * added to the previous line */ if (buf[0] == '\t') { for (i = 0; i < count; i++) event_continue(events[i], buf); continue; } /* Get rid of leading spaces (non-standard) */ while (isspace((unsigned char)buf[0])) memcpy(buf, buf + 1, strlen(buf)); /* No tab in the line, then not a valid line */ if ((pp = strchr(buf, '\t')) == NULL) continue; /* Trim spaces in front of the tab */ while (isspace((unsigned char)pp[-1])) pp--; p = *pp; *pp = '\0'; if ((count = parsedaymonth(buf, year, month, day, &flags, extradata)) == 0) continue; *pp = p; if (count < 0) { /* Show error status based on return value */ if (debug) fprintf(stderr, "Ignored: %s\n", buf); if (count == -1) continue; count = -count + 1; } /* Find the last tab */ while (pp[1] == '\t') pp++; if (d_first < 0) d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); for (i = 0; i < count; i++) { tm.tm_mon = month[i] - 1; tm.tm_mday = day[i]; tm.tm_year = year[i] - 1900; (void)strftime(dbuf, sizeof(dbuf), d_first ? "%e %b" : "%b %e", &tm); if (debug) fprintf(stderr, "got %s\n", pp); events[i] = event_add(year[i], month[i], day[i], dbuf, ((flags &= F_VARIABLE) != 0) ? 1 : 0, pp, extradata[i]); } } free(line); fclose(in); return (0); } void cal(void) { FILE *fpin; FILE *fpout; int i; for (i = 0; i < MAXCOUNT; i++) extradata[i] = (char *)calloc(1, 20); if ((fpin = opencalin()) == NULL) return; if ((fpout = opencalout()) == NULL) { fclose(fpin); return; } if (cal_parse(fpin, fpout)) return; event_print_all(fpout); closecal(fpout); } FILE * opencalin(void) { struct stat sbuf; FILE *fpin; /* open up calendar file */ if ((fpin = fopen(calendarFile, "r")) == NULL) { if (doall) { if (chdir(calendarHomes[0]) != 0) return (NULL); if (stat(calendarNoMail, &sbuf) == 0) return (NULL); if ((fpin = fopen(calendarFile, "r")) == NULL) return (NULL); } else { fpin = cal_fopen(calendarFile); } } return (fpin); } FILE * opencalout(void) { int fd; /* not reading all calendar files, just set output to stdout */ if (!doall) return (stdout); /* set output to a temporary file, so if no output don't send mail */ snprintf(path, sizeof(path), "%s/_calXXXXXX", _PATH_TMP); if ((fd = mkstemp(path)) < 0) return (NULL); return (fdopen(fd, "w+")); } void closecal(FILE *fp) { uid_t uid; struct stat sbuf; int nread, pdes[2], status; char buf[1024]; if (!doall) return; rewind(fp); if (fstat(fileno(fp), &sbuf) || !sbuf.st_size) goto done; if (pipe(pdes) < 0) goto done; switch (fork()) { case -1: /* error */ (void)close(pdes[0]); (void)close(pdes[1]); goto done; case 0: /* child -- set stdin to pipe output */ if (pdes[0] != STDIN_FILENO) { (void)dup2(pdes[0], STDIN_FILENO); (void)close(pdes[0]); } (void)close(pdes[1]); uid = geteuid(); if (setuid(getuid()) < 0) { warnx("setuid failed"); _exit(1); } if (setgid(getegid()) < 0) { warnx("setgid failed"); _exit(1); } if (setuid(uid) < 0) { warnx("setuid failed"); _exit(1); } execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F", "\"Reminder Service\"", (char *)NULL); warn(_PATH_SENDMAIL); _exit(1); } /* parent -- write to pipe input */ (void)close(pdes[0]); write(pdes[1], "From: \"Reminder Service\" <", 26); write(pdes[1], pw->pw_name, strlen(pw->pw_name)); write(pdes[1], ">\nTo: <", 7); write(pdes[1], pw->pw_name, strlen(pw->pw_name)); write(pdes[1], ">\nSubject: ", 11); write(pdes[1], dayname, strlen(dayname)); write(pdes[1], "'s Calendar\nPrecedence: bulk\n\n", 30); while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0) (void)write(pdes[1], buf, nread); (void)close(pdes[1]); done: (void)fclose(fp); (void)unlink(path); while (wait(&status) >= 0); } Index: head/usr.bin/checknr/checknr.c =================================================================== --- head/usr.bin/checknr/checknr.c (revision 303525) +++ head/usr.bin/checknr/checknr.c (revision 303526) @@ -1,647 +1,646 @@ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1980, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #if 0 #ifndef lint static char sccsid[] = "@(#)checknr.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); /* * checknr: check an nroff/troff input file for matching macro calls. * we also attempt to match size and font changes, but only the embedded * kind. These must end in \s0 and \fP resp. Maybe more sophistication * later but for now think of these restrictions as contributions to * structured typesetting. */ #include -#define _WITH_GETLINE #include #include #include #include #define MAXSTK 100 /* Stack size */ #define MAXBR 100 /* Max number of bracket pairs known */ #define MAXCMDS 600 /* Max number of commands known */ static void addcmd(char *); static void addmac(const char *); static int binsrch(const char *); static void checkknown(const char *); static void chkcmd(const char *, const char *); static void complain(int); static int eq(const char *, const char *); static void nomatch(const char *); static void pe(int); static void process(FILE *); static void prop(int); static void usage(void); /* * The stack on which we remember what we've seen so far. */ static struct stkstr { int opno; /* number of opening bracket */ int pl; /* '+', '-', ' ' for \s, 1 for \f, 0 for .ft */ int parm; /* parm to size, font, etc */ int lno; /* line number */ } stk[MAXSTK]; static int stktop; /* * The kinds of opening and closing brackets. */ static struct brstr { const char *opbr; const char *clbr; } br[MAXBR] = { /* A few bare bones troff commands */ #define SZ 0 {"sz", "sz"}, /* also \s */ #define FT 1 {"ft", "ft"}, /* also \f */ /* the -mm package */ {"AL", "LE"}, {"AS", "AE"}, {"BL", "LE"}, {"BS", "BE"}, {"DF", "DE"}, {"DL", "LE"}, {"DS", "DE"}, {"FS", "FE"}, {"ML", "LE"}, {"NS", "NE"}, {"RL", "LE"}, {"VL", "LE"}, /* the -ms package */ {"AB", "AE"}, {"BD", "DE"}, {"CD", "DE"}, {"DS", "DE"}, {"FS", "FE"}, {"ID", "DE"}, {"KF", "KE"}, {"KS", "KE"}, {"LD", "DE"}, {"LG", "NL"}, {"QS", "QE"}, {"RS", "RE"}, {"SM", "NL"}, {"XA", "XE"}, {"XS", "XE"}, /* The -me package */ {"(b", ")b"}, {"(c", ")c"}, {"(d", ")d"}, {"(f", ")f"}, {"(l", ")l"}, {"(q", ")q"}, {"(x", ")x"}, {"(z", ")z"}, /* The -mdoc package */ {"Ao", "Ac"}, {"Bd", "Ed"}, {"Bk", "Ek"}, {"Bo", "Bc"}, {"Do", "Dc"}, {"Fo", "Fc"}, {"Oo", "Oc"}, {"Po", "Pc"}, {"Qo", "Qc"}, {"Rs", "Re"}, {"So", "Sc"}, {"Xo", "Xc"}, /* Things needed by preprocessors */ {"EQ", "EN"}, {"TS", "TE"}, /* Refer */ {"[", "]"}, {0, 0} }; /* * All commands known to nroff, plus macro packages. * Used so we can complain about unrecognized commands. */ static const char *knowncmds[MAXCMDS] = { "$c", "$f", "$h", "$p", "$s", "%A", "%B", "%C", "%D", "%I", "%J", "%N", "%O", "%P", "%Q", "%R", "%T", "%V", "(b", "(c", "(d", "(f", "(l", "(q", "(t", "(x", "(z", ")b", ")c", ")d", ")f", ")l", ")q", ")t", ")x", ")z", "++", "+c", "1C", "1c", "2C", "2c", "@(", "@)", "@C", "@D", "@F", "@I", "@M", "@c", "@e", "@f", "@h", "@m", "@n", "@o", "@p", "@r", "@t", "@z", "AB", "AE", "AF", "AI", "AL", "AM", "AS", "AT", "AU", "AX", "Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At", "B", "B" , "B1", "B2", "BD", "BE", "BG", "BL", "BS", "BT", "BX", "Bc", "Bd", "Bf", "Bk", "Bl", "Bo", "Bq", "Bsx", "Bx", "C1", "C2", "CD", "CM", "CT", "Cd", "Cm", "D", "D" , "D1", "DA", "DE", "DF", "DL", "DS", "DT", "Db", "Dc", "Dd", "Dl", "Do", "Dq", "Dt", "Dv", "EC", "EF", "EG", "EH", "EM", "EN", "EQ", "EX", "Ec", "Ed", "Ef", "Ek", "El", "Em", "Eo", "Er", "Ev", "FA", "FD", "FE", "FG", "FJ", "FK", "FL", "FN", "FO", "FQ", "FS", "FV", "FX", "Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Ft", "Fx", "H", "H" , "HC", "HD", "HM", "HO", "HU", "I", "I" , "ID", "IE", "IH", "IM", "IP", "IX", "IZ", "Ic", "In", "It", "KD", "KE", "KF", "KQ", "KS", "LB", "LC", "LD", "LE", "LG", "LI", "LP", "Lb", "Li", "MC", "ME", "MF", "MH", "ML", "MR", "MT", "ND", "NE", "NH", "NL", "NP", "NS", "Nd", "Nm", "No", "Ns", "Nx", "OF", "OH", "OK", "OP", "Oc", "Oo", "Op", "Os", "Ot", "Ox", "P", "P" , "P1", "PF", "PH", "PP", "PT", "PX", "PY", "Pa", "Pc", "Pf", "Po", "Pp", "Pq", "QE", "QP", "QS", "Qc", "Ql", "Qo", "Qq", "R", "R" , "RA", "RC", "RE", "RL", "RP", "RQ", "RS", "RT", "Re", "Rs", "S", "S" , "S0", "S2", "S3", "SA", "SG", "SH", "SK", "SM", "SP", "SY", "Sc", "Sh", "Sm", "So", "Sq", "Ss", "St", "Sx", "Sy", "T&", "TA", "TB", "TC", "TD", "TE", "TH", "TL", "TM", "TP", "TQ", "TR", "TS", "TX", "Tn", "UL", "US", "UX", "Ud", "Ux", "VL", "Va", "Vt", "WC", "WH", "XA", "XD", "XE", "XF", "XK", "XP", "XS", "Xc", "Xo", "Xr", "[", "[" , "[-", "[0", "[1", "[2", "[3", "[4", "[5", "[<", "[>", "[]", "\\{", "\\}", "]", "]" , "]-", "]<", "]>", "][", "ab", "ac", "ad", "af", "am", "ar", "as", "b", "b" , "ba", "bc", "bd", "bi", "bl", "bp", "br", "bx", "c.", "c2", "cc", "ce", "cf", "ch", "chop", "cs", "ct", "cu", "da", "de", "di", "dl", "dn", "do", "ds", "dt", "dw", "dy", "ec", "ef", "eh", "el", "em", "eo", "ep", "ev", "evc", "ex", "fallback", "fc", "feature", "fi", "fl", "flig", "fo", "fp", "ft", "ftr", "fz", "fzoom", "hc", "he", "hidechar", "hl", "hp", "ht", "hw", "hx", "hy", "hylang", "i", "i" , "ie", "if", "ig", "in", "ip", "it", "ix", "kern", "kernafter", "kernbefore", "kernpair", "lc", "lc_ctype", "lg", "lhang", "li", "ll", "ln", "lo", "lp", "ls", "lt", "m1", "m2", "m3", "m4", "mc", "mk", "mo", "n1", "n2", "na", "ne", "nf", "nh", "nl", "nm", "nn", "np", "nr", "ns", "nx", "of", "oh", "os", "pa", "papersize", "pc", "pi", "pl", "pm", "pn", "po", "pp", "ps", "q", "q" , "r", "r" , "rb", "rd", "re", "recursionlimit", "return", "rhang", "rm", "rn", "ro", "rr", "rs", "rt", "sb", "sc", "sh", "shift", "sk", "so", "sp", "ss", "st", "sv", "sz", "ta", "tc", "th", "ti", "tl", "tm", "tp", "tr", "track", "u", "uf", "uh", "ul", "vs", "wh", "xflag", "xp", "yr", 0 }; static int lineno; /* current line number in input file */ static const char *cfilename; /* name of current file */ static int nfiles; /* number of files to process */ static int fflag; /* -f: ignore \f */ static int sflag; /* -s: ignore \s */ static int ncmds; /* size of knowncmds */ static int slot; /* slot in knowncmds found by binsrch */ int main(int argc, char **argv) { FILE *f; int i; char *cp; char b1[4]; /* Figure out how many known commands there are */ while (knowncmds[ncmds]) ncmds++; while (argc > 1 && argv[1][0] == '-') { switch(argv[1][1]) { /* -a: add pairs of macros */ case 'a': i = strlen(argv[1]) - 2; if (i % 6 != 0) usage(); /* look for empty macro slots */ for (i=0; br[i].opbr; i++) ; for (cp=argv[1]+3; cp[-1]; cp += 6) { char *tmp; if (i >= MAXBR) errx(1, "too many pairs"); if ((tmp = malloc(3)) == NULL) err(1, "malloc"); strlcpy(tmp, cp, 3); br[i].opbr = tmp; if ((tmp = malloc(3)) == NULL) err(1, "malloc"); strlcpy(tmp, cp+3, 3); br[i].clbr = tmp; addmac(br[i].opbr); /* knows pairs are also known cmds */ addmac(br[i].clbr); i++; } break; /* -c: add known commands */ case 'c': i = strlen(argv[1]) - 2; if (i % 3 != 0) usage(); for (cp=argv[1]+3; cp[-1]; cp += 3) { if (cp[2] && cp[2] != '.') usage(); strncpy(b1, cp, 2); b1[2] = '\0'; addmac(b1); } break; /* -f: ignore font changes */ case 'f': fflag = 1; break; /* -s: ignore size changes */ case 's': sflag = 1; break; default: usage(); } argc--; argv++; } nfiles = argc - 1; if (nfiles > 0) { for (i = 1; i < argc; i++) { cfilename = argv[i]; f = fopen(cfilename, "r"); if (f == NULL) warn("%s", cfilename); else { process(f); fclose(f); } } } else { cfilename = "stdin"; process(stdin); } exit(0); } static void usage(void) { fprintf(stderr, "usage: checknr [-a.xx.yy.xx.yy...] [-c.xx.xx.xx...] [-s] [-f] file\n"); exit(1); } static void process(FILE *f) { int i, n; char mac[64]; /* The current macro or nroff command */ char *line; size_t linecap; int pl; line = NULL; linecap = 0; stktop = -1; for (lineno = 1; getline(&line, &linecap, f) > 0; lineno++) { if (line[0] == '.') { /* * find and isolate the macro/command name. */ strncpy(mac, line+1, 4); if (isspace(mac[0])) { pe(lineno); printf("Empty command\n"); } else if (isspace(mac[1])) { mac[1] = 0; } else if (isspace(mac[2])) { mac[2] = 0; } else if (mac[0] != '\\' || mac[1] != '\"') { pe(lineno); printf("Command too long\n"); } /* * Is it a known command? */ checkknown(mac); /* * Should we add it? */ if (eq(mac, "de")) addcmd(line); chkcmd(line, mac); } /* * At this point we process the line looking * for \s and \f. */ for (i = 0; line[i]; i++) if (line[i] == '\\' && (i == 0 || line[i-1] != '\\')) { if (!sflag && line[++i] == 's') { pl = line[++i]; if (isdigit(pl)) { n = pl - '0'; pl = ' '; } else n = 0; while (isdigit(line[++i])) n = 10 * n + line[i] - '0'; i--; if (n == 0) { if (stktop >= 0 && stk[stktop].opno == SZ) { stktop--; } else { pe(lineno); printf("unmatched \\s0\n"); } } else { stk[++stktop].opno = SZ; stk[stktop].pl = pl; stk[stktop].parm = n; stk[stktop].lno = lineno; } } else if (!fflag && line[i] == 'f') { n = line[++i]; if (n == 'P') { if (stktop >= 0 && stk[stktop].opno == FT) { stktop--; } else { pe(lineno); printf("unmatched \\fP\n"); } } else { stk[++stktop].opno = FT; stk[stktop].pl = 1; stk[stktop].parm = n; stk[stktop].lno = lineno; } } } } free(line); /* * We've hit the end and look at all this stuff that hasn't been * matched yet! Complain, complain. */ for (i = stktop; i >= 0; i--) { complain(i); } } static void complain(int i) { pe(stk[i].lno); printf("Unmatched "); prop(i); printf("\n"); } static void prop(int i) { if (stk[i].pl == 0) printf(".%s", br[stk[i].opno].opbr); else switch(stk[i].opno) { case SZ: printf("\\s%c%d", stk[i].pl, stk[i].parm); break; case FT: printf("\\f%c", stk[i].parm); break; default: printf("Bug: stk[%d].opno = %d = .%s, .%s", i, stk[i].opno, br[stk[i].opno].opbr, br[stk[i].opno].clbr); } } static void chkcmd(const char *line __unused, const char *mac) { int i; /* * Check to see if it matches top of stack. */ if (stktop >= 0 && eq(mac, br[stk[stktop].opno].clbr)) stktop--; /* OK. Pop & forget */ else { /* No. Maybe it's an opener */ for (i=0; br[i].opbr; i++) { if (eq(mac, br[i].opbr)) { /* Found. Push it. */ stktop++; stk[stktop].opno = i; stk[stktop].pl = 0; stk[stktop].parm = 0; stk[stktop].lno = lineno; break; } /* * Maybe it's an unmatched closer. * NOTE: this depends on the fact * that none of the closers can be * openers too. */ if (eq(mac, br[i].clbr)) { nomatch(mac); break; } } } } static void nomatch(const char *mac) { int i, j; /* * Look for a match further down on stack * If we find one, it suggests that the stuff in * between is supposed to match itself. */ for (j=stktop; j>=0; j--) if (eq(mac,br[stk[j].opno].clbr)) { /* Found. Make a good diagnostic. */ if (j == stktop-2) { /* * Check for special case \fx..\fR and don't * complain. */ if (stk[j+1].opno==FT && stk[j+1].parm!='R' && stk[j+2].opno==FT && stk[j+2].parm=='R') { stktop = j -1; return; } /* * We have two unmatched frobs. Chances are * they were intended to match, so we mention * them together. */ pe(stk[j+1].lno); prop(j+1); printf(" does not match %d: ", stk[j+2].lno); prop(j+2); printf("\n"); } else for (i=j+1; i <= stktop; i++) { complain(i); } stktop = j-1; return; } /* Didn't find one. Throw this away. */ pe(lineno); printf("Unmatched .%s\n", mac); } /* eq: are two strings equal? */ static int eq(const char *s1, const char *s2) { return (strcmp(s1, s2) == 0); } /* print the first part of an error message, given the line number */ static void pe(int linen) { if (nfiles > 1) printf("%s: ", cfilename); printf("%d: ", linen); } static void checkknown(const char *mac) { if (eq(mac, ".")) return; if (binsrch(mac) >= 0) return; if (mac[0] == '\\' && mac[1] == '"') /* comments */ return; pe(lineno); printf("Unknown command: .%s\n", mac); } /* * We have a .de xx line in "line". Add xx to the list of known commands. */ static void addcmd(char *line) { char *mac; /* grab the macro being defined */ mac = line+4; while (isspace(*mac)) mac++; if (*mac == 0) { pe(lineno); printf("illegal define: %s\n", line); return; } mac[2] = 0; if (isspace(mac[1]) || mac[1] == '\\') mac[1] = 0; if (ncmds >= MAXCMDS) { printf("Only %d known commands allowed\n", MAXCMDS); exit(1); } addmac(mac); } /* * Add mac to the list. We should really have some kind of tree * structure here but this is a quick-and-dirty job and I just don't * have time to mess with it. (I wonder if this will come back to haunt * me someday?) Anyway, I claim that .de is fairly rare in user * nroff programs, and the register loop below is pretty fast. */ static void addmac(const char *mac) { const char **src, **dest, **loc; if (binsrch(mac) >= 0){ /* it's OK to redefine something */ #ifdef DEBUG printf("binsrch(%s) -> already in table\n", mac); #endif return; } /* binsrch sets slot as a side effect */ #ifdef DEBUG printf("binsrch(%s) -> %d\n", mac, slot); #endif loc = &knowncmds[slot]; src = &knowncmds[ncmds-1]; dest = src+1; while (dest > loc) *dest-- = *src--; if ((*loc = strdup(mac)) == NULL) err(1, "strdup"); ncmds++; #ifdef DEBUG printf("after: %s %s %s %s %s, %d cmds\n", knowncmds[slot-2], knowncmds[slot-1], knowncmds[slot], knowncmds[slot+1], knowncmds[slot+2], ncmds); #endif } /* * Do a binary search in knowncmds for mac. * If found, return the index. If not, return -1. */ static int binsrch(const char *mac) { const char *p; /* pointer to current cmd in list */ int d; /* difference if any */ int mid; /* mid point in binary search */ int top, bot; /* boundaries of bin search, inclusive */ top = ncmds-1; bot = 0; while (top >= bot) { mid = (top+bot)/2; p = knowncmds[mid]; d = p[0] - mac[0]; if (d == 0) d = p[1] - mac[1]; if (d == 0) return (mid); if (d < 0) bot = mid + 1; else top = mid - 1; } slot = bot; /* place it would have gone */ return (-1); } Index: head/usr.bin/comm/comm.c =================================================================== --- head/usr.bin/comm/comm.c (revision 303525) +++ head/usr.bin/comm/comm.c (revision 303526) @@ -1,249 +1,248 @@ /* * Copyright (c) 1989, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Case Larsen. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1989, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif #if 0 #ifndef lint static char sccsid[] = "From: @(#)comm.c 8.4 (Berkeley) 5/4/95"; #endif #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include -#define _WITH_GETLINE #include #include #include #include #include #include static int iflag; static const char *tabs[] = { "", "\t", "\t\t" }; static FILE *file(const char *); static wchar_t *convert(const char *); static void show(FILE *, const char *, const char *, char **, size_t *); static void usage(void); int main(int argc, char *argv[]) { int comp, read1, read2; int ch, flag1, flag2, flag3; FILE *fp1, *fp2; const char *col1, *col2, *col3; size_t line1len, line2len; char *line1, *line2; ssize_t n1, n2; wchar_t *tline1, *tline2; const char **p; (void) setlocale(LC_ALL, ""); flag1 = flag2 = flag3 = 1; while ((ch = getopt(argc, argv, "123i")) != -1) switch(ch) { case '1': flag1 = 0; break; case '2': flag2 = 0; break; case '3': flag3 = 0; break; case 'i': iflag = 1; break; case '?': default: usage(); } argc -= optind; argv += optind; if (argc != 2) usage(); fp1 = file(argv[0]); fp2 = file(argv[1]); /* for each column printed, add another tab offset */ p = tabs; col1 = col2 = col3 = NULL; if (flag1) col1 = *p++; if (flag2) col2 = *p++; if (flag3) col3 = *p; line1len = line2len = 0; line1 = line2 = NULL; n1 = n2 = -1; for (read1 = read2 = 1;;) { /* read next line, check for EOF */ if (read1) { n1 = getline(&line1, &line1len, fp1); if (n1 < 0 && ferror(fp1)) err(1, "%s", argv[0]); if (n1 > 0 && line1[n1 - 1] == '\n') line1[n1 - 1] = '\0'; } if (read2) { n2 = getline(&line2, &line2len, fp2); if (n2 < 0 && ferror(fp2)) err(1, "%s", argv[1]); if (n2 > 0 && line2[n2 - 1] == '\n') line2[n2 - 1] = '\0'; } /* if one file done, display the rest of the other file */ if (n1 < 0) { if (n2 >= 0 && col2 != NULL) show(fp2, argv[1], col2, &line2, &line2len); break; } if (n2 < 0) { if (n1 >= 0 && col1 != NULL) show(fp1, argv[0], col1, &line1, &line1len); break; } tline2 = NULL; if ((tline1 = convert(line1)) != NULL) tline2 = convert(line2); if (tline1 == NULL || tline2 == NULL) comp = strcmp(line1, line2); else comp = wcscoll(tline1, tline2); if (tline1 != NULL) free(tline1); if (tline2 != NULL) free(tline2); /* lines are the same */ if (!comp) { read1 = read2 = 1; if (col3 != NULL) (void)printf("%s%s\n", col3, line1); continue; } /* lines are different */ if (comp < 0) { read1 = 1; read2 = 0; if (col1 != NULL) (void)printf("%s%s\n", col1, line1); } else { read1 = 0; read2 = 1; if (col2 != NULL) (void)printf("%s%s\n", col2, line2); } } exit(0); } static wchar_t * convert(const char *str) { size_t n; wchar_t *buf, *p; if ((n = mbstowcs(NULL, str, 0)) == (size_t)-1) return (NULL); if (SIZE_MAX / sizeof(*buf) < n + 1) errx(1, "conversion buffer length overflow"); if ((buf = malloc((n + 1) * sizeof(*buf))) == NULL) err(1, "malloc"); if (mbstowcs(buf, str, n + 1) != n) errx(1, "internal mbstowcs() error"); if (iflag) { for (p = buf; *p != L'\0'; p++) *p = towlower(*p); } return (buf); } static void show(FILE *fp, const char *fn, const char *offset, char **bufp, size_t *buflenp) { ssize_t n; do { (void)printf("%s%s\n", offset, *bufp); if ((n = getline(bufp, buflenp, fp)) < 0) break; if (n > 0 && (*bufp)[n - 1] == '\n') (*bufp)[n - 1] = '\0'; } while (1); if (ferror(fp)) err(1, "%s", fn); } static FILE * file(const char *name) { FILE *fp; if (!strcmp(name, "-")) return (stdin); if ((fp = fopen(name, "r")) == NULL) { err(1, "%s", name); } return (fp); } static void usage(void) { (void)fprintf(stderr, "usage: comm [-123i] file1 file2\n"); exit(1); } Index: head/usr.bin/grep/grep.c =================================================================== --- head/usr.bin/grep/grep.c (revision 303525) +++ head/usr.bin/grep/grep.c (revision 303526) @@ -1,748 +1,747 @@ /* $NetBSD: grep.c,v 1.6 2011/04/18 03:48:23 joerg Exp $ */ /* $FreeBSD$ */ /* $OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $ */ /*- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav * Copyright (C) 2008-2009 Gabor Kovesdan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include -#define _WITH_GETLINE #include #include #include #include #include "fastmatch.h" #include "grep.h" #ifndef WITHOUT_NLS #include nl_catd catalog; #endif /* * Default messags to use when NLS is disabled or no catalogue * is found. */ const char *errstr[] = { "", /* 1*/ "(standard input)", /* 2*/ "cannot read bzip2 compressed file", /* 3*/ "unknown %s option", /* 4*/ "usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n", /* 5*/ "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n", /* 6*/ "\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n", /* 7*/ "\t[--null] [pattern] [file ...]\n", /* 8*/ "Binary file %s matches\n", /* 9*/ "%s (BSD grep) %s\n", }; /* Flags passed to regcomp() and regexec() */ int cflags = REG_NOSUB; int eflags = REG_STARTEND; /* Shortcut for matching all cases like empty regex */ bool matchall; /* Searching patterns */ unsigned int patterns; static unsigned int pattern_sz; struct pat *pattern; regex_t *r_pattern; fastmatch_t *fg_pattern; /* Filename exclusion/inclusion patterns */ unsigned int fpatterns, dpatterns; static unsigned int fpattern_sz, dpattern_sz; struct epat *dpattern, *fpattern; /* For regex errors */ char re_error[RE_ERROR_BUF + 1]; /* Command-line flags */ unsigned long long Aflag; /* -A x: print x lines trailing each match */ unsigned long long Bflag; /* -B x: print x lines leading each match */ bool Hflag; /* -H: always print file name */ bool Lflag; /* -L: only show names of files with no matches */ bool bflag; /* -b: show block numbers for each match */ bool cflag; /* -c: only show a count of matching lines */ bool hflag; /* -h: don't print filename headers */ bool iflag; /* -i: ignore case */ bool lflag; /* -l: only show names of files with matches */ bool mflag; /* -m x: stop reading the files after x matches */ long long mcount; /* count for -m */ long long mlimit; /* requested value for -m */ bool nflag; /* -n: show line numbers in front of matching lines */ bool oflag; /* -o: print only matching part */ bool qflag; /* -q: quiet mode (don't output anything) */ bool sflag; /* -s: silent mode (ignore errors) */ bool vflag; /* -v: only show non-matching lines */ bool wflag; /* -w: pattern must start and end on word boundaries */ bool xflag; /* -x: pattern must match entire line */ bool lbflag; /* --line-buffered */ bool nullflag; /* --null */ char *label; /* --label */ const char *color; /* --color */ int grepbehave = GREP_BASIC; /* -EFGP: type of the regex */ int binbehave = BINFILE_BIN; /* -aIU: handling of binary files */ int filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */ int devbehave = DEV_READ; /* -D: handling of devices */ int dirbehave = DIR_READ; /* -dRr: handling of directories */ int linkbehave = LINK_READ; /* -OpS: handling of symlinks */ bool dexclude, dinclude; /* --exclude-dir and --include-dir */ bool fexclude, finclude; /* --exclude and --include */ enum { BIN_OPT = CHAR_MAX + 1, COLOR_OPT, HELP_OPT, MMAP_OPT, LINEBUF_OPT, LABEL_OPT, NULL_OPT, R_EXCLUDE_OPT, R_INCLUDE_OPT, R_DEXCLUDE_OPT, R_DINCLUDE_OPT }; static inline const char *init_color(const char *); /* Housekeeping */ bool first = true; /* flag whether we are processing the first match */ bool prev; /* flag whether or not the previous line matched */ int tail; /* lines left to print */ bool file_err; /* file reading error */ /* * Prints usage information and returns 2. */ static void usage(void) { fprintf(stderr, getstr(4), getprogname()); fprintf(stderr, "%s", getstr(5)); fprintf(stderr, "%s", getstr(6)); fprintf(stderr, "%s", getstr(7)); exit(2); } static const char *optstr = "0123456789A:B:C:D:EFGHIJMLOPSRUVZabcd:e:f:hilm:nopqrsuvwxXy"; static const struct option long_options[] = { {"binary-files", required_argument, NULL, BIN_OPT}, {"help", no_argument, NULL, HELP_OPT}, {"mmap", no_argument, NULL, MMAP_OPT}, {"line-buffered", no_argument, NULL, LINEBUF_OPT}, {"label", required_argument, NULL, LABEL_OPT}, {"null", no_argument, NULL, NULL_OPT}, {"color", optional_argument, NULL, COLOR_OPT}, {"colour", optional_argument, NULL, COLOR_OPT}, {"exclude", required_argument, NULL, R_EXCLUDE_OPT}, {"include", required_argument, NULL, R_INCLUDE_OPT}, {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT}, {"include-dir", required_argument, NULL, R_DINCLUDE_OPT}, {"after-context", required_argument, NULL, 'A'}, {"text", no_argument, NULL, 'a'}, {"before-context", required_argument, NULL, 'B'}, {"byte-offset", no_argument, NULL, 'b'}, {"context", optional_argument, NULL, 'C'}, {"count", no_argument, NULL, 'c'}, {"devices", required_argument, NULL, 'D'}, {"directories", required_argument, NULL, 'd'}, {"extended-regexp", no_argument, NULL, 'E'}, {"regexp", required_argument, NULL, 'e'}, {"fixed-strings", no_argument, NULL, 'F'}, {"file", required_argument, NULL, 'f'}, {"basic-regexp", no_argument, NULL, 'G'}, {"no-filename", no_argument, NULL, 'h'}, {"with-filename", no_argument, NULL, 'H'}, {"ignore-case", no_argument, NULL, 'i'}, {"bz2decompress", no_argument, NULL, 'J'}, {"files-with-matches", no_argument, NULL, 'l'}, {"files-without-match", no_argument, NULL, 'L'}, {"max-count", required_argument, NULL, 'm'}, {"lzma", no_argument, NULL, 'M'}, {"line-number", no_argument, NULL, 'n'}, {"only-matching", no_argument, NULL, 'o'}, {"quiet", no_argument, NULL, 'q'}, {"silent", no_argument, NULL, 'q'}, {"recursive", no_argument, NULL, 'r'}, {"no-messages", no_argument, NULL, 's'}, {"binary", no_argument, NULL, 'U'}, {"unix-byte-offsets", no_argument, NULL, 'u'}, {"invert-match", no_argument, NULL, 'v'}, {"version", no_argument, NULL, 'V'}, {"word-regexp", no_argument, NULL, 'w'}, {"line-regexp", no_argument, NULL, 'x'}, {"xz", no_argument, NULL, 'X'}, {"decompress", no_argument, NULL, 'Z'}, {NULL, no_argument, NULL, 0} }; /* * Adds a searching pattern to the internal array. */ static void add_pattern(char *pat, size_t len) { /* Do not add further pattern is we already match everything */ if (matchall) return; /* Check if we can do a shortcut */ if (len == 0) { matchall = true; for (unsigned int i = 0; i < patterns; i++) { free(pattern[i].pat); } pattern = grep_realloc(pattern, sizeof(struct pat)); pattern[0].pat = NULL; pattern[0].len = 0; patterns = 1; return; } /* Increase size if necessary */ if (patterns == pattern_sz) { pattern_sz *= 2; pattern = grep_realloc(pattern, ++pattern_sz * sizeof(struct pat)); } if (len > 0 && pat[len - 1] == '\n') --len; /* pat may not be NUL-terminated */ pattern[patterns].pat = grep_malloc(len + 1); memcpy(pattern[patterns].pat, pat, len); pattern[patterns].len = len; pattern[patterns].pat[len] = '\0'; ++patterns; } /* * Adds a file include/exclude pattern to the internal array. */ static void add_fpattern(const char *pat, int mode) { /* Increase size if necessary */ if (fpatterns == fpattern_sz) { fpattern_sz *= 2; fpattern = grep_realloc(fpattern, ++fpattern_sz * sizeof(struct epat)); } fpattern[fpatterns].pat = grep_strdup(pat); fpattern[fpatterns].mode = mode; ++fpatterns; } /* * Adds a directory include/exclude pattern to the internal array. */ static void add_dpattern(const char *pat, int mode) { /* Increase size if necessary */ if (dpatterns == dpattern_sz) { dpattern_sz *= 2; dpattern = grep_realloc(dpattern, ++dpattern_sz * sizeof(struct epat)); } dpattern[dpatterns].pat = grep_strdup(pat); dpattern[dpatterns].mode = mode; ++dpatterns; } /* * Reads searching patterns from a file and adds them with add_pattern(). */ static void read_patterns(const char *fn) { struct stat st; FILE *f; char *line; size_t len; ssize_t rlen; if ((f = fopen(fn, "r")) == NULL) err(2, "%s", fn); if ((fstat(fileno(f), &st) == -1) || (S_ISDIR(st.st_mode))) { fclose(f); return; } len = 0; line = NULL; while ((rlen = getline(&line, &len, f)) != -1) add_pattern(line, line[0] == '\n' ? 0 : (size_t)rlen); free(line); if (ferror(f)) err(2, "%s", fn); fclose(f); } static inline const char * init_color(const char *d) { char *c; c = getenv("GREP_COLOR"); return (c != NULL && c[0] != '\0' ? c : d); } int main(int argc, char *argv[]) { char **aargv, **eargv, *eopts; char *ep; const char *pn; unsigned long long l; unsigned int aargc, eargc, i; int c, lastc, needpattern, newarg, prevoptind; setlocale(LC_ALL, ""); #ifndef WITHOUT_NLS catalog = catopen("grep", NL_CAT_LOCALE); #endif /* Check what is the program name of the binary. In this way we can have all the funcionalities in one binary without the need of scripting and using ugly hacks. */ pn = getprogname(); if (pn[0] == 'b' && pn[1] == 'z') { filebehave = FILE_BZIP; pn += 2; } else if (pn[0] == 'x' && pn[1] == 'z') { filebehave = FILE_XZ; pn += 2; } else if (pn[0] == 'l' && pn[1] == 'z') { filebehave = FILE_LZMA; pn += 2; } else if (pn[0] == 'z') { filebehave = FILE_GZIP; pn += 1; } switch (pn[0]) { case 'e': grepbehave = GREP_EXTENDED; break; case 'f': grepbehave = GREP_FIXED; break; } lastc = '\0'; newarg = 1; prevoptind = 1; needpattern = 1; eopts = getenv("GREP_OPTIONS"); /* support for extra arguments in GREP_OPTIONS */ eargc = 0; if (eopts != NULL && eopts[0] != '\0') { char *str; /* make an estimation of how many extra arguments we have */ for (unsigned int j = 0; j < strlen(eopts); j++) if (eopts[j] == ' ') eargc++; eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1)); eargc = 0; /* parse extra arguments */ while ((str = strsep(&eopts, " ")) != NULL) if (str[0] != '\0') eargv[eargc++] = grep_strdup(str); aargv = (char **)grep_calloc(eargc + argc + 1, sizeof(char *)); aargv[0] = argv[0]; for (i = 0; i < eargc; i++) aargv[i + 1] = eargv[i]; for (int j = 1; j < argc; j++, i++) aargv[i + 1] = argv[j]; aargc = eargc + argc; } else { aargv = argv; aargc = argc; } while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) != -1)) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (newarg || !isdigit(lastc)) Aflag = 0; else if (Aflag > LLONG_MAX / 10) { errno = ERANGE; err(2, NULL); } Aflag = Bflag = (Aflag * 10) + (c - '0'); break; case 'C': if (optarg == NULL) { Aflag = Bflag = 2; break; } /* FALLTHROUGH */ case 'A': /* FALLTHROUGH */ case 'B': errno = 0; l = strtoull(optarg, &ep, 10); if (((errno == ERANGE) && (l == ULLONG_MAX)) || ((errno == EINVAL) && (l == 0))) err(2, NULL); else if (ep[0] != '\0') { errno = EINVAL; err(2, NULL); } if (c == 'A') Aflag = l; else if (c == 'B') Bflag = l; else Aflag = Bflag = l; break; case 'a': binbehave = BINFILE_TEXT; break; case 'b': bflag = true; break; case 'c': cflag = true; break; case 'D': if (strcasecmp(optarg, "skip") == 0) devbehave = DEV_SKIP; else if (strcasecmp(optarg, "read") == 0) devbehave = DEV_READ; else errx(2, getstr(3), "--devices"); break; case 'd': if (strcasecmp("recurse", optarg) == 0) { Hflag = true; dirbehave = DIR_RECURSE; } else if (strcasecmp("skip", optarg) == 0) dirbehave = DIR_SKIP; else if (strcasecmp("read", optarg) == 0) dirbehave = DIR_READ; else errx(2, getstr(3), "--directories"); break; case 'E': grepbehave = GREP_EXTENDED; break; case 'e': { char *token; char *string = optarg; while ((token = strsep(&string, "\n")) != NULL) add_pattern(token, strlen(token)); } needpattern = 0; break; case 'F': grepbehave = GREP_FIXED; break; case 'f': read_patterns(optarg); needpattern = 0; break; case 'G': grepbehave = GREP_BASIC; break; case 'H': Hflag = true; break; case 'h': Hflag = false; hflag = true; break; case 'I': binbehave = BINFILE_SKIP; break; case 'i': case 'y': iflag = true; cflags |= REG_ICASE; break; case 'J': #ifdef WITHOUT_BZIP2 errno = EOPNOTSUPP; err(2, "bzip2 support was disabled at compile-time"); #endif filebehave = FILE_BZIP; break; case 'L': lflag = false; Lflag = true; break; case 'l': Lflag = false; lflag = true; break; case 'm': mflag = true; errno = 0; mlimit = mcount = strtoll(optarg, &ep, 10); if (((errno == ERANGE) && (mcount == LLONG_MAX)) || ((errno == EINVAL) && (mcount == 0))) err(2, NULL); else if (ep[0] != '\0') { errno = EINVAL; err(2, NULL); } break; case 'M': filebehave = FILE_LZMA; break; case 'n': nflag = true; break; case 'O': linkbehave = LINK_EXPLICIT; break; case 'o': oflag = true; cflags &= ~REG_NOSUB; break; case 'p': linkbehave = LINK_SKIP; break; case 'q': qflag = true; break; case 'S': linkbehave = LINK_READ; break; case 'R': case 'r': dirbehave = DIR_RECURSE; Hflag = true; break; case 's': sflag = true; break; case 'U': binbehave = BINFILE_BIN; break; case 'u': case MMAP_OPT: filebehave = FILE_MMAP; break; case 'V': printf(getstr(9), getprogname(), VERSION); exit(0); case 'v': vflag = true; break; case 'w': wflag = true; cflags &= ~REG_NOSUB; break; case 'x': xflag = true; cflags &= ~REG_NOSUB; break; case 'X': filebehave = FILE_XZ; break; case 'Z': filebehave = FILE_GZIP; break; case BIN_OPT: if (strcasecmp("binary", optarg) == 0) binbehave = BINFILE_BIN; else if (strcasecmp("without-match", optarg) == 0) binbehave = BINFILE_SKIP; else if (strcasecmp("text", optarg) == 0) binbehave = BINFILE_TEXT; else errx(2, getstr(3), "--binary-files"); break; case COLOR_OPT: color = NULL; if (optarg == NULL || strcasecmp("auto", optarg) == 0 || strcasecmp("tty", optarg) == 0 || strcasecmp("if-tty", optarg) == 0) { char *term; term = getenv("TERM"); if (isatty(STDOUT_FILENO) && term != NULL && strcasecmp(term, "dumb") != 0) color = init_color("01;31"); } else if (strcasecmp("always", optarg) == 0 || strcasecmp("yes", optarg) == 0 || strcasecmp("force", optarg) == 0) { color = init_color("01;31"); } else if (strcasecmp("never", optarg) != 0 && strcasecmp("none", optarg) != 0 && strcasecmp("no", optarg) != 0) errx(2, getstr(3), "--color"); cflags &= ~REG_NOSUB; break; case LABEL_OPT: label = optarg; break; case LINEBUF_OPT: lbflag = true; break; case NULL_OPT: nullflag = true; break; case R_INCLUDE_OPT: finclude = true; add_fpattern(optarg, INCL_PAT); break; case R_EXCLUDE_OPT: fexclude = true; add_fpattern(optarg, EXCL_PAT); break; case R_DINCLUDE_OPT: dinclude = true; add_dpattern(optarg, INCL_PAT); break; case R_DEXCLUDE_OPT: dexclude = true; add_dpattern(optarg, EXCL_PAT); break; case HELP_OPT: default: usage(); } lastc = c; newarg = optind != prevoptind; prevoptind = optind; } aargc -= optind; aargv += optind; /* Empty pattern file matches nothing */ if (!needpattern && (patterns == 0)) exit(1); /* Fail if we don't have any pattern */ if (aargc == 0 && needpattern) usage(); /* Process patterns from command line */ if (aargc != 0 && needpattern) { char *token; char *string = *aargv; while ((token = strsep(&string, "\n")) != NULL) add_pattern(token, strlen(token)); --aargc; ++aargv; } switch (grepbehave) { case GREP_BASIC: break; case GREP_FIXED: /* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */ cflags |= 0020; break; case GREP_EXTENDED: cflags |= REG_EXTENDED; break; default: /* NOTREACHED */ usage(); } fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern)); r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); /* Check if cheating is allowed (always is for fgrep). */ for (i = 0; i < patterns; ++i) { if (fastncomp(&fg_pattern[i], pattern[i].pat, pattern[i].len, cflags) != 0) { /* Fall back to full regex library */ c = regcomp(&r_pattern[i], pattern[i].pat, cflags); if (c != 0) { regerror(c, &r_pattern[i], re_error, RE_ERROR_BUF); errx(2, "%s", re_error); } } } if (lbflag) setlinebuf(stdout); if ((aargc == 0 || aargc == 1) && !Hflag) hflag = true; if (aargc == 0) exit(!procfile("-")); if (dirbehave == DIR_RECURSE) c = grep_tree(aargv); else for (c = 0; aargc--; ++aargv) { if ((finclude || fexclude) && !file_matching(*aargv)) continue; c+= procfile(*aargv); } #ifndef WITHOUT_NLS catclose(catalog); #endif /* Find out the correct return value according to the results and the command line option. */ exit(c ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1)); } Index: head/usr.bin/mandoc/Makefile =================================================================== --- head/usr.bin/mandoc/Makefile (revision 303525) +++ head/usr.bin/mandoc/Makefile (revision 303526) @@ -1,91 +1,90 @@ # $FreeBSD$ .include MDOCMLDIR= ${.CURDIR}/../../contrib/mdocml .PATH: ${MDOCMLDIR} PROG= mandoc MAN= mandoc.1 eqn.7 mandoc_char.7 tbl.7 man.7 mdoc.7 # roff.7 MLINKS= mandoc.1 mdocml.1 .if ${MK_MANDOCDB} != no && ${MK_MAN_UTILS} != no MAN+= apropos.1 makewhatis.8 MLINKS+= apropos.1 whatis.1 LINKS= ${BINDIR}/mandoc ${BINDIR}/whatis \ ${BINDIR}/mandoc ${BINDIR}/makewhatis \ ${BINDIR}/mandoc ${BINDIR}/apropos .endif LIBMAN_SRCS= man.c \ man_hash.c \ man_macro.c \ man_validate.c LIBMDOC_SRCS= att.c \ lib.c \ mdoc.c \ mdoc_argv.c \ mdoc_hash.c \ mdoc_macro.c \ mdoc_state.c \ mdoc_validate.c \ st.c \ LIBROFF_SRCS= eqn.c \ roff.c \ tbl.c \ tbl_data.c \ tbl_layout.c \ tbl_opts.c \ LIB_SRCS= ${LIBMAN_SRCS} \ ${LIBMDOC_SRCS} \ ${LIBROFF_SRCS} \ chars.c \ mandoc.c \ mandoc_aux.c \ mandoc_ohash.c \ msec.c \ preconv.c \ read.c HTML_SRCS= eqn_html.c \ html.c \ man_html.c \ mdoc_html.c \ tbl_html.c MAN_SRCS= mdoc_man.c TERM_SRCS= eqn_term.c \ man_term.c \ mdoc_term.c \ term.c \ term_ascii.c \ term_ps.c \ tbl_term.c DB_SRCS= mandocdb.c \ mansearch.c \ mansearch_const.c \ tag.c \ manpath.c SRCS= ${LIB_SRCS} \ ${HTML_SRCS} \ ${MAN_SRCS} \ ${TERM_SRCS} \ main.c \ out.c \ tree.c SRCS+= ${DB_SRCS} WARNS?= 2 CFLAGS+= -DHAVE_CONFIG_H \ - -D_WITH_GETLINE \ -I${.CURDIR}/../../lib/libopenbsd/ \ -I${.CURDIR}/../../contrib/sqlite3 LIBADD= openbsd sqlite3 z .include Index: head/usr.bin/sdiff/sdiff.c =================================================================== --- head/usr.bin/sdiff/sdiff.c (revision 303525) +++ head/usr.bin/sdiff/sdiff.c (revision 303526) @@ -1,1189 +1,1188 @@ /* $OpenBSD: sdiff.c,v 1.36 2015/12/29 19:04:46 gsoares Exp $ */ /* * Written by Raymond Lai . * Public domain. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include -#define _WITH_GETLINE #include #include #include #include #include "common.h" #include "extern.h" #define DIFF_PATH "/usr/bin/diff" #define WIDTH 126 /* * Each column must be at least one character wide, plus three * characters between the columns (space, [<|>], space). */ #define WIDTH_MIN 5 /* 3 kilobytes of chars */ #define MAX_CHECK 768 /* A single diff line. */ struct diffline { STAILQ_ENTRY(diffline) diffentries; char *left; char div; char *right; }; static void astrcat(char **, const char *); static void enqueue(char *, char, char *); static char *mktmpcpy(const char *); static int istextfile(FILE *); static void binexec(char *, char *, char *) __dead2; static void freediff(struct diffline *); static void int_usage(void); static int parsecmd(FILE *, FILE *, FILE *); static void printa(FILE *, size_t); static void printc(FILE *, size_t, FILE *, size_t); static void printcol(const char *, size_t *, const size_t); static void printd(FILE *, size_t); static void println(const char *, const char, const char *); static void processq(void); static void prompt(const char *, const char *); static void usage(void) __dead2; static char *xfgets(FILE *); static STAILQ_HEAD(, diffline) diffhead = STAILQ_HEAD_INITIALIZER(diffhead); static size_t line_width; /* width of a line (two columns and divider) */ static size_t width; /* width of each column */ static size_t file1ln, file2ln; /* line number of file1 and file2 */ static int Iflag = 0; /* ignore sets matching regexp */ static int lflag; /* print only left column for identical lines */ static int sflag; /* skip identical lines */ FILE *outfp; /* file to save changes to */ const char *tmpdir; /* TMPDIR or /tmp */ enum { HELP_OPT = CHAR_MAX + 1, NORMAL_OPT, FCASE_SENSITIVE_OPT, FCASE_IGNORE_OPT, FROMFILE_OPT, TOFILE_OPT, UNIDIR_OPT, STRIPCR_OPT, HORIZ_OPT, LEFTC_OPT, SUPCL_OPT, LF_OPT, /* the following groupings must be in sequence */ OLDGF_OPT, NEWGF_OPT, UNCGF_OPT, CHGF_OPT, OLDLF_OPT, NEWLF_OPT, UNCLF_OPT, /* end order-sensitive enums */ TSIZE_OPT, HLINES_OPT, LFILES_OPT, DIFFPROG_OPT, PIPE_FD, /* pid from the diff parent (if applicable) */ DIFF_PID, NOOP_OPT, }; static struct option longopts[] = { /* options only processed in sdiff */ { "left-column", no_argument, NULL, LEFTC_OPT }, { "suppress-common-lines", no_argument, NULL, 's' }, { "width", required_argument, NULL, 'w' }, { "output", required_argument, NULL, 'o' }, { "diff-program", required_argument, NULL, DIFFPROG_OPT }, { "pipe-fd", required_argument, NULL, PIPE_FD }, { "diff-pid", required_argument, NULL, DIFF_PID }, /* Options processed by diff. */ { "ignore-file-name-case", no_argument, NULL, FCASE_IGNORE_OPT }, { "no-ignore-file-name-case", no_argument, NULL, FCASE_SENSITIVE_OPT }, { "strip-trailing-cr", no_argument, NULL, STRIPCR_OPT }, { "tabsize", required_argument, NULL, TSIZE_OPT }, { "help", no_argument, NULL, HELP_OPT }, { "text", no_argument, NULL, 'a' }, { "ignore-blank-lines", no_argument, NULL, 'B' }, { "ignore-space-change", no_argument, NULL, 'b' }, { "minimal", no_argument, NULL, 'd' }, { "ignore-tab-expansion", no_argument, NULL, 'E' }, { "ignore-matching-lines", required_argument, NULL, 'I' }, { "ignore-case", no_argument, NULL, 'i' }, { "expand-tabs", no_argument, NULL, 't' }, { "speed-large-files", no_argument, NULL, 'H' }, { "ignore-all-space", no_argument, NULL, 'W' }, { NULL, 0, NULL, '\0'} }; static const char *help_msg[] = { "\nusage: sdiff [-abdilstW] [-I regexp] [-o outfile] [-w width] file1 file2\n", "\t-l, --left-column, Only print the left column for identical lines.", "\t-o OUTFILE, --output=OUTFILE, nteractively merge file1 and file2 into outfile.", "\t-s, --suppress-common-lines, Skip identical lines.", "\t-w WIDTH, --width=WIDTH, Print a maximum of WIDTH characters on each line.", "\tOptions passed to diff(1) are:", "\t\t-a, --text, Treat file1 and file2 as text files.", "\t\t-b, --ignore-trailing-cr, Ignore trailing blank spaces.", "\t\t-d, --minimal, Minimize diff size.", "\t\t-I RE, --ignore-matching-lines=RE, Ignore changes whose line matches RE.", "\t\t-i, --ignore-case, Do a case-insensitive comparison.", "\t\t-t, --expand-tabs Expand tabs to spaces.", "\t\t-W, --ignore-all-spaces, Ignore all spaces.", "\t\t--speed-large-files, Assume large file with scattered changes.", "\t\t--strip-trailing-cr, Strip trailing carriage return.", "\t\t--ignore-file-name-case, Ignore case of file names.", "\t\t--no-ignore-file-name-case, Do not ignore file name case", "\t\t--tabsize NUM, Change size of tabs (default 8.)", NULL, }; /* * Create temporary file if source_file is not a regular file. * Returns temporary file name if one was malloced, NULL if unnecessary. */ static char * mktmpcpy(const char *source_file) { struct stat sb; ssize_t rcount; int ifd, ofd; u_char buf[BUFSIZ]; char *target_file; /* Open input and output. */ ifd = open(source_file, O_RDONLY, 0); /* File was opened successfully. */ if (ifd != -1) { if (fstat(ifd, &sb) == -1) err(2, "error getting file status from %s", source_file); /* Regular file. */ if (S_ISREG(sb.st_mode)) { close(ifd); return (NULL); } } else { /* If ``-'' does not exist the user meant stdin. */ if (errno == ENOENT && strcmp(source_file, "-") == 0) ifd = STDIN_FILENO; else err(2, "error opening %s", source_file); } /* Not a regular file, so copy input into temporary file. */ if (asprintf(&target_file, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1) err(2, "asprintf"); if ((ofd = mkstemp(target_file)) == -1) { warn("error opening %s", target_file); goto FAIL; } while ((rcount = read(ifd, buf, sizeof(buf))) != -1 && rcount != 0) { ssize_t wcount; wcount = write(ofd, buf, (size_t)rcount); if (-1 == wcount || rcount != wcount) { warn("error writing to %s", target_file); goto FAIL; } } if (rcount == -1) { warn("error reading from %s", source_file); goto FAIL; } close(ifd); close(ofd); return (target_file); FAIL: unlink(target_file); exit(2); } int main(int argc, char **argv) { FILE *diffpipe=NULL, *file1, *file2; size_t diffargc = 0, wflag = WIDTH; int ch, fd[2] = {-1}, status; pid_t pid=0; pid_t ppid =-1; const char *outfile = NULL; struct option *popt; char **diffargv, *diffprog = DIFF_PATH, *filename1, *filename2, *tmp1, *tmp2, *s1, *s2; int i; /* * Process diff flags. */ /* * Allocate memory for diff arguments and NULL. * Each flag has at most one argument, so doubling argc gives an * upper limit of how many diff args can be passed. argv[0], * file1, and file2 won't have arguments so doubling them will * waste some memory; however we need an extra space for the * NULL at the end, so it sort of works out. */ if (!(diffargv = calloc(argc, sizeof(char **) * 2))) err(2, "main"); /* Add first argument, the program name. */ diffargv[diffargc++] = diffprog; /* create a dynamic string for merging single-switch options */ if ( asprintf(&diffargv[diffargc++], "-") < 0 ) err(2, "main"); while ((ch = getopt_long(argc, argv, "aBbdEHI:ilo:stWw:", longopts, NULL)) != -1) { const char *errstr; switch (ch) { /* only compatible --long-name-form with diff */ case FCASE_IGNORE_OPT: case FCASE_SENSITIVE_OPT: case STRIPCR_OPT: case TSIZE_OPT: case 'S': break; /* combine no-arg single switches */ case 'a': case 'B': case 'b': case 'd': case 'E': case 'i': case 't': case 'H': case 'W': for(popt = longopts; ch != popt->val && popt->name != NULL; popt++); diffargv[1] = realloc(diffargv[1], sizeof(char) * strlen(diffargv[1]) + 2); /* * In diff, the 'W' option is 'w' and the 'w' is 'W'. */ if (ch == 'W') sprintf(diffargv[1], "%sw", diffargv[1]); else sprintf(diffargv[1], "%s%c", diffargv[1], ch); break; case DIFFPROG_OPT: diffargv[0] = diffprog = optarg; break; case 'I': Iflag = 1; diffargv[diffargc++] = "-I"; diffargv[diffargc++] = optarg; break; case 'l': lflag = 1; break; case 'o': outfile = optarg; break; case 's': sflag = 1; break; case 'w': wflag = strtonum(optarg, WIDTH_MIN, INT_MAX, &errstr); if (errstr) errx(2, "width is %s: %s", errstr, optarg); break; case DIFF_PID: ppid = strtonum(optarg, 0, INT_MAX, &errstr); if (errstr) errx(2, "diff pid value is %s: %s", errstr, optarg); break; case HELP_OPT: for (i = 0; help_msg[i] != NULL; i++) printf("%s\n", help_msg[i]); exit(0); break; default: usage(); break; } } /* no single switches were used */ if (strcmp(diffargv[1], "-") == 0 ) { for ( i = 1; i < argc-1; i++) { diffargv[i] = diffargv[i+1]; } diffargv[diffargc-1] = NULL; diffargc--; } argc -= optind; argv += optind; if (argc != 2) usage(); if (outfile && (outfp = fopen(outfile, "w")) == NULL) err(2, "could not open: %s", optarg); if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0') tmpdir = _PATH_TMP; filename1 = argv[0]; filename2 = argv[1]; /* * Create temporary files for diff and sdiff to share if file1 * or file2 are not regular files. This allows sdiff and diff * to read the same inputs if one or both inputs are stdin. * * If any temporary files were created, their names would be * saved in tmp1 or tmp2. tmp1 should never equal tmp2. */ tmp1 = tmp2 = NULL; /* file1 and file2 are the same, so copy to same temp file. */ if (strcmp(filename1, filename2) == 0) { if ((tmp1 = mktmpcpy(filename1))) filename1 = filename2 = tmp1; /* Copy file1 and file2 into separate temp files. */ } else { if ((tmp1 = mktmpcpy(filename1))) filename1 = tmp1; if ((tmp2 = mktmpcpy(filename2))) filename2 = tmp2; } diffargv[diffargc++] = filename1; diffargv[diffargc++] = filename2; /* Add NULL to end of array to indicate end of array. */ diffargv[diffargc++] = NULL; /* Subtract column divider and divide by two. */ width = (wflag - 3) / 2; /* Make sure line_width can fit in size_t. */ if (width > (SIZE_MAX - 3) / 2) errx(2, "width is too large: %zu", width); line_width = width * 2 + 3; if (ppid == -1 ) { if (pipe(fd)) err(2, "pipe"); switch (pid = fork()) { case 0: /* child */ /* We don't read from the pipe. */ close(fd[0]); if (dup2(fd[1], STDOUT_FILENO) == -1) err(2, "child could not duplicate descriptor"); /* Free unused descriptor. */ close(fd[1]); execvp(diffprog, diffargv); err(2, "could not execute diff: %s", diffprog); break; case -1: err(2, "could not fork"); break; } /* parent */ /* We don't write to the pipe. */ close(fd[1]); /* Open pipe to diff command. */ if ((diffpipe = fdopen(fd[0], "r")) == NULL) err(2, "could not open diff pipe"); } if ((file1 = fopen(filename1, "r")) == NULL) err(2, "could not open %s", filename1); if ((file2 = fopen(filename2, "r")) == NULL) err(2, "could not open %s", filename2); if (!istextfile(file1) || !istextfile(file2)) { /* Close open files and pipe, delete temps */ fclose(file1); fclose(file2); if (diffpipe != NULL) fclose(diffpipe); if (tmp1) if (unlink(tmp1)) warn("Error deleting %s.", tmp1); if (tmp2) if (unlink(tmp2)) warn("Error deleting %s.", tmp2); free(tmp1); free(tmp2); binexec(diffprog, filename1, filename2); } /* Line numbers start at one. */ file1ln = file2ln = 1; /* Read and parse diff output. */ while (parsecmd(diffpipe, file1, file2) != EOF) ; fclose(diffpipe); /* Wait for diff to exit. */ if (waitpid(pid, &status, 0) == -1 || !WIFEXITED(status) || WEXITSTATUS(status) >= 2) err(2, "diff exited abnormally."); /* Delete and free unneeded temporary files. */ if (tmp1) if (unlink(tmp1)) warn("Error deleting %s.", tmp1); if (tmp2) if (unlink(tmp2)) warn("Error deleting %s.", tmp2); free(tmp1); free(tmp2); filename1 = filename2 = tmp1 = tmp2 = NULL; /* No more diffs, so print common lines. */ if (lflag) while ((s1 = xfgets(file1))) enqueue(s1, ' ', NULL); else for (;;) { s1 = xfgets(file1); s2 = xfgets(file2); if (s1 || s2) enqueue(s1, ' ', s2); else break; } fclose(file1); fclose(file2); /* Process unmodified lines. */ processq(); /* Return diff exit status. */ return (WEXITSTATUS(status)); } /* * When sdiff/zsdiff detects a binary file as input, executes them with * diff/zdiff to maintain the same behavior as GNU sdiff with binary input. */ static void binexec(char *diffprog, char *f1, char *f2) { char *args[] = {diffprog, f1, f2, (char *) 0}; execv(diffprog, args); /* If execv() fails, sdiff's execution will continue below. */ errx(1, "Could not execute diff process.\n"); } /* * Checks whether a file appears to be a text file. */ static int istextfile(FILE *f) { int ch, i; if (f == NULL) return (1); rewind(f); for (i = 0; i <= MAX_CHECK; i++) { ch = fgetc(f); if (ch == '\0') { rewind(f); return (0); } if (ch == EOF) break; } rewind(f); return (1); } /* * Prints an individual column (left or right), taking into account * that tabs are variable-width. Takes a string, the current column * the cursor is on the screen, and the maximum value of the column. * The column value is updated as we go along. */ static void printcol(const char *s, size_t *col, const size_t col_max) { for (; *s && *col < col_max; ++s) { size_t new_col; switch (*s) { case '\t': /* * If rounding to next multiple of eight causes * an integer overflow, just return. */ if (*col > SIZE_MAX - 8) return; /* Round to next multiple of eight. */ new_col = (*col / 8 + 1) * 8; /* * If printing the tab goes past the column * width, don't print it and just quit. */ if (new_col > col_max) return; *col = new_col; break; default: ++(*col); } putchar(*s); } } /* * Prompts user to either choose between two strings or edit one, both, * or neither. */ static void prompt(const char *s1, const char *s2) { char *cmd; /* Print command prompt. */ putchar('%'); /* Get user input. */ for (; (cmd = xfgets(stdin)); free(cmd)) { const char *p; /* Skip leading whitespace. */ for (p = cmd; isspace(*p); ++p) ; switch (*p) { case 'e': /* Skip `e'. */ ++p; if (eparse(p, s1, s2) == -1) goto USAGE; break; case 'l': case '1': /* Choose left column as-is. */ if (s1 != NULL) fprintf(outfp, "%s\n", s1); /* End of command parsing. */ break; case 'q': goto QUIT; case 'r': case '2': /* Choose right column as-is. */ if (s2 != NULL) fprintf(outfp, "%s\n", s2); /* End of command parsing. */ break; case 's': sflag = 1; goto PROMPT; case 'v': sflag = 0; /* FALLTHROUGH */ default: /* Interactive usage help. */ USAGE: int_usage(); PROMPT: putchar('%'); /* Prompt user again. */ continue; } free(cmd); return; } /* * If there was no error, we received an EOF from stdin, so we * should quit. */ QUIT: fclose(outfp); exit(0); } /* * Takes two strings, separated by a column divider. NULL strings are * treated as empty columns. If the divider is the ` ' character, the * second column is not printed (-l flag). In this case, the second * string must be NULL. When the second column is NULL, the divider * does not print the trailing space following the divider character. * * Takes into account that tabs can take multiple columns. */ static void println(const char *s1, const char div, const char *s2) { size_t col; /* Print first column. Skips if s1 == NULL. */ col = 0; if (s1) { /* Skip angle bracket and space. */ printcol(s1, &col, width); } /* Otherwise, we pad this column up to width. */ for (; col < width; ++col) putchar(' '); /* Only print left column. */ if (div == ' ' && !s2) { printf(" (\n"); return; } /* * Print column divider. If there is no second column, we don't * need to add the space for padding. */ if (!s2) { printf(" %c\n", div); return; } printf(" %c ", div); col += 3; /* Skip angle bracket and space. */ printcol(s2, &col, line_width); putchar('\n'); } /* * Reads a line from file and returns as a string. If EOF is reached, * NULL is returned. The returned string must be freed afterwards. */ static char * xfgets(FILE *file) { size_t linecap; ssize_t l; char *s; clearerr(file); linecap = 0; s = NULL; if ((l = getline(&s, &linecap, file)) == -1) { if (ferror(file)) err(2, "error reading file"); return (NULL); } if (s[l-1] == '\n') s[l-1] = '\0'; return (s); } /* * Parse ed commands from diffpipe and print lines from file1 (lines * to change or delete) or file2 (lines to add or change). * Returns EOF or 0. */ static int parsecmd(FILE *diffpipe, FILE *file1, FILE *file2) { size_t file1start, file1end, file2start, file2end, n; /* ed command line and pointer to characters in line */ char *line, *p, *q; const char *errstr; char c, cmd; /* Read ed command. */ if (!(line = xfgets(diffpipe))) return (EOF); p = line; /* Go to character after line number. */ while (isdigit(*p)) ++p; c = *p; *p++ = 0; file1start = strtonum(line, 0, INT_MAX, &errstr); if (errstr) errx(2, "file1 start is %s: %s", errstr, line); /* A range is specified for file1. */ if (c == ',') { q = p; /* Go to character after file2end. */ while (isdigit(*p)) ++p; c = *p; *p++ = 0; file1end = strtonum(q, 0, INT_MAX, &errstr); if (errstr) errx(2, "file1 end is %s: %s", errstr, line); if (file1start > file1end) errx(2, "invalid line range in file1: %s", line); } else file1end = file1start; cmd = c; /* Check that cmd is valid. */ if (!(cmd == 'a' || cmd == 'c' || cmd == 'd')) errx(2, "ed command not recognized: %c: %s", cmd, line); q = p; /* Go to character after line number. */ while (isdigit(*p)) ++p; c = *p; *p++ = 0; file2start = strtonum(q, 0, INT_MAX, &errstr); if (errstr) errx(2, "file2 start is %s: %s", errstr, line); /* * There should either be a comma signifying a second line * number or the line should just end here. */ if (c != ',' && c != '\0') errx(2, "invalid line range in file2: %c: %s", c, line); if (c == ',') { file2end = strtonum(p, 0, INT_MAX, &errstr); if (errstr) errx(2, "file2 end is %s: %s", errstr, line); if (file2start >= file2end) errx(2, "invalid line range in file2: %s", line); } else file2end = file2start; /* Appends happen _after_ stated line. */ if (cmd == 'a') { if (file1start != file1end) errx(2, "append cannot have a file1 range: %s", line); if (file1start == SIZE_MAX) errx(2, "file1 line range too high: %s", line); file1start = ++file1end; } /* * I'm not sure what the deal is with the line numbers for * deletes, though. */ else if (cmd == 'd') { if (file2start != file2end) errx(2, "delete cannot have a file2 range: %s", line); if (file2start == SIZE_MAX) errx(2, "file2 line range too high: %s", line); file2start = ++file2end; } /* * Continue reading file1 and file2 until we reach line numbers * specified by diff. Should only happen with -I flag. */ for (; file1ln < file1start && file2ln < file2start; ++file1ln, ++file2ln) { char *s1, *s2; if (!(s1 = xfgets(file1))) errx(2, "file1 shorter than expected"); if (!(s2 = xfgets(file2))) errx(2, "file2 shorter than expected"); /* If the -l flag was specified, print only left column. */ if (lflag) { free(s2); /* * XXX - If -l and -I are both specified, all * unchanged or ignored lines are shown with a * `(' divider. This matches GNU sdiff, but I * believe it is a bug. Just check out: * gsdiff -l -I '^$' samefile samefile. */ if (Iflag) enqueue(s1, '(', NULL); else enqueue(s1, ' ', NULL); } else enqueue(s1, ' ', s2); } /* Ignore deleted lines. */ for (; file1ln < file1start; ++file1ln) { char *s; if (!(s = xfgets(file1))) errx(2, "file1 shorter than expected"); enqueue(s, '(', NULL); } /* Ignore added lines. */ for (; file2ln < file2start; ++file2ln) { char *s; if (!(s = xfgets(file2))) errx(2, "file2 shorter than expected"); /* If -l flag was given, don't print right column. */ if (lflag) free(s); else enqueue(NULL, ')', s); } /* Process unmodified or skipped lines. */ processq(); switch (cmd) { case 'a': printa(file2, file2end); n = file2end - file2start + 1; break; case 'c': printc(file1, file1end, file2, file2end); n = file1end - file1start + 1 + 1 + file2end - file2start + 1; break; case 'd': printd(file1, file1end); n = file1end - file1start + 1; break; default: errx(2, "invalid diff command: %c: %s", cmd, line); } free(line); /* Skip to next ed line. */ while (n--) { if (!(line = xfgets(diffpipe))) errx(2, "diff ended early"); free(line); } return (0); } /* * Queues up a diff line. */ static void enqueue(char *left, char div, char *right) { struct diffline *diffp; if (!(diffp = malloc(sizeof(struct diffline)))) err(2, "enqueue"); diffp->left = left; diffp->div = div; diffp->right = right; STAILQ_INSERT_TAIL(&diffhead, diffp, diffentries); } /* * Free a diffline structure and its elements. */ static void freediff(struct diffline *diffp) { free(diffp->left); free(diffp->right); free(diffp); } /* * Append second string into first. Repeated appends to the same string * are cached, making this an O(n) function, where n = strlen(append). */ static void astrcat(char **s, const char *append) { /* Length of string in previous run. */ static size_t offset = 0; size_t newsiz; /* * String from previous run. Compared to *s to see if we are * dealing with the same string. If so, we can use offset. */ static const char *oldstr = NULL; char *newstr; /* * First string is NULL, so just copy append. */ if (!*s) { if (!(*s = strdup(append))) err(2, "astrcat"); /* Keep track of string. */ offset = strlen(*s); oldstr = *s; return; } /* * *s is a string so concatenate. */ /* Did we process the same string in the last run? */ /* * If this is a different string from the one we just processed * cache new string. */ if (oldstr != *s) { offset = strlen(*s); oldstr = *s; } /* Size = strlen(*s) + \n + strlen(append) + '\0'. */ newsiz = offset + 1 + strlen(append) + 1; /* Resize *s to fit new string. */ newstr = realloc(*s, newsiz); if (newstr == NULL) err(2, "astrcat"); *s = newstr; /* *s + offset should be end of string. */ /* Concatenate. */ strlcpy(*s + offset, "\n", newsiz - offset); strlcat(*s + offset, append, newsiz - offset); /* New string length should be exactly newsiz - 1 characters. */ /* Store generated string's values. */ offset = newsiz - 1; oldstr = *s; } /* * Process diff set queue, printing, prompting, and saving each diff * line stored in queue. */ static void processq(void) { struct diffline *diffp; char divc, *left, *right; /* Don't process empty queue. */ if (STAILQ_EMPTY(&diffhead)) return; /* Remember the divider. */ divc = STAILQ_FIRST(&diffhead)->div; left = NULL; right = NULL; /* * Go through set of diffs, concatenating each line in left or * right column into two long strings, `left' and `right'. */ STAILQ_FOREACH(diffp, &diffhead, diffentries) { /* * Print changed lines if -s was given, * print all lines if -s was not given. */ if (!sflag || diffp->div == '|' || diffp->div == '<' || diffp->div == '>') println(diffp->left, diffp->div, diffp->right); /* Append new lines to diff set. */ if (diffp->left) astrcat(&left, diffp->left); if (diffp->right) astrcat(&right, diffp->right); } /* Empty queue and free each diff line and its elements. */ while (!STAILQ_EMPTY(&diffhead)) { diffp = STAILQ_FIRST(&diffhead); STAILQ_REMOVE_HEAD(&diffhead, diffentries); freediff(diffp); } /* Write to outfp, prompting user if lines are different. */ if (outfp) switch (divc) { case ' ': case '(': case ')': fprintf(outfp, "%s\n", left); break; case '|': case '<': case '>': prompt(left, right); break; default: errx(2, "invalid divider: %c", divc); } /* Free left and right. */ free(left); free(right); } /* * Print lines following an (a)ppend command. */ static void printa(FILE *file, size_t line2) { char *line; for (; file2ln <= line2; ++file2ln) { if (!(line = xfgets(file))) errx(2, "append ended early"); enqueue(NULL, '>', line); } processq(); } /* * Print lines following a (c)hange command, from file1ln to file1end * and from file2ln to file2end. */ static void printc(FILE *file1, size_t file1end, FILE *file2, size_t file2end) { struct fileline { STAILQ_ENTRY(fileline) fileentries; char *line; }; STAILQ_HEAD(, fileline) delqhead = STAILQ_HEAD_INITIALIZER(delqhead); /* Read lines to be deleted. */ for (; file1ln <= file1end; ++file1ln) { struct fileline *linep; char *line1; /* Read lines from both. */ if (!(line1 = xfgets(file1))) errx(2, "error reading file1 in delete in change"); /* Add to delete queue. */ if (!(linep = malloc(sizeof(struct fileline)))) err(2, "printc"); linep->line = line1; STAILQ_INSERT_TAIL(&delqhead, linep, fileentries); } /* Process changed lines.. */ for (; !STAILQ_EMPTY(&delqhead) && file2ln <= file2end; ++file2ln) { struct fileline *del; char *add; /* Get add line. */ if (!(add = xfgets(file2))) errx(2, "error reading add in change"); del = STAILQ_FIRST(&delqhead); enqueue(del->line, '|', add); STAILQ_REMOVE_HEAD(&delqhead, fileentries); /* * Free fileline structure but not its elements since * they are queued up. */ free(del); } processq(); /* Process remaining lines to add. */ for (; file2ln <= file2end; ++file2ln) { char *add; /* Get add line. */ if (!(add = xfgets(file2))) errx(2, "error reading add in change"); enqueue(NULL, '>', add); } processq(); /* Process remaining lines to delete. */ while (!STAILQ_EMPTY(&delqhead)) { struct fileline *filep; filep = STAILQ_FIRST(&delqhead); enqueue(filep->line, '<', NULL); STAILQ_REMOVE_HEAD(&delqhead, fileentries); free(filep); } processq(); } /* * Print deleted lines from file, from file1ln to file1end. */ static void printd(FILE *file1, size_t file1end) { char *line1; /* Print out lines file1ln to line2. */ for (; file1ln <= file1end; ++file1ln) { if (!(line1 = xfgets(file1))) errx(2, "file1 ended early in delete"); enqueue(line1, '<', NULL); } processq(); } /* * Interactive mode usage. */ static void int_usage(void) { puts("e:\tedit blank diff\n" "eb:\tedit both diffs concatenated\n" "el:\tedit left diff\n" "er:\tedit right diff\n" "l | 1:\tchoose left diff\n" "r | 2:\tchoose right diff\n" "s:\tsilent mode--don't print identical lines\n" "v:\tverbose mode--print identical lines\n" "q:\tquit"); } static void usage(void) { fprintf(stderr, "usage: sdiff [-abdilstW] [-I regexp] [-o outfile] [-w width] file1" " file2\n"); exit(2); } Index: head/usr.bin/sed/main.c =================================================================== --- head/usr.bin/sed/main.c (revision 303525) +++ head/usr.bin/sed/main.c (revision 303526) @@ -1,529 +1,528 @@ /*- * Copyright (c) 2013 Johann 'Myrkraverk' Oskarsson. * Copyright (c) 1992 Diomidis Spinellis. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Diomidis Spinellis of Imperial College, University of London. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1992, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif #ifndef lint static const char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/3/94"; #endif #include #include #include #include #include #include #include #include #include #include #include #include -#define _WITH_GETLINE #include #include #include #include #include "defs.h" #include "extern.h" /* * Linked list of units (strings and files) to be compiled */ struct s_compunit { struct s_compunit *next; enum e_cut {CU_FILE, CU_STRING} type; const char *s; /* Pointer to string or fname */ }; /* * Linked list pointer to compilation units and pointer to current * next pointer. */ static struct s_compunit *script, **cu_nextp = &script; /* * Linked list of files to be processed */ struct s_flist { const char *fname; struct s_flist *next; }; /* * Linked list pointer to files and pointer to current * next pointer. */ static struct s_flist *files, **fl_nextp = &files; FILE *infile; /* Current input file */ FILE *outfile; /* Current output file */ int aflag, eflag, nflag; int rflags = 0; static int rval; /* Exit status */ static int ispan; /* Whether inplace editing spans across files */ /* * Current file and line number; line numbers restart across compilation * units, but span across input files. The latter is optional if editing * in place. */ const char *fname; /* File name. */ const char *outfname; /* Output file name */ static char oldfname[PATH_MAX]; /* Old file name (for in-place editing) */ static char tmpfname[PATH_MAX]; /* Temporary file name (for in-place editing) */ static const char *inplace; /* Inplace edit file extension. */ u_long linenum; static void add_compunit(enum e_cut, const char *); static void add_file(const char *); static void usage(void); int main(int argc, char *argv[]) { int c, fflag; (void) setlocale(LC_ALL, ""); fflag = 0; inplace = NULL; while ((c = getopt(argc, argv, "EI:ae:f:i:lnru")) != -1) switch (c) { case 'r': /* Gnu sed compat */ case 'E': rflags = REG_EXTENDED; break; case 'I': inplace = optarg; ispan = 1; /* span across input files */ break; case 'a': aflag = 1; break; case 'e': eflag = 1; add_compunit(CU_STRING, optarg); break; case 'f': fflag = 1; add_compunit(CU_FILE, optarg); break; case 'i': inplace = optarg; ispan = 0; /* don't span across input files */ break; case 'l': if(setvbuf(stdout, NULL, _IOLBF, 0) != 0) warnx("setting line buffered output failed"); break; case 'n': nflag = 1; break; case 'u': if(setvbuf(stdout, NULL, _IONBF, 0) != 0) warnx("setting unbuffered output failed"); break; default: case '?': usage(); } argc -= optind; argv += optind; /* First usage case; script is the first arg */ if (!eflag && !fflag && *argv) { add_compunit(CU_STRING, *argv); argv++; } compile(); /* Continue with first and start second usage */ if (*argv) for (; *argv; argv++) add_file(*argv); else add_file(NULL); process(); cfclose(prog, NULL); if (fclose(stdout)) err(1, "stdout"); exit(rval); } static void usage(void) { (void)fprintf(stderr, "usage: %s script [-Ealnru] [-i extension] [file ...]\n" "\t%s [-Ealnu] [-i extension] [-e script] ... [-f script_file]" " ... [file ...]\n", getprogname(), getprogname()); exit(1); } /* * Like fgets, but go through the chain of compilation units chaining them * together. Empty strings and files are ignored. */ const char * cu_fgets(int *more) { static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF; static FILE *f; /* Current open file */ static const char *s; /* Current pointer inside string */ static char string_ident[30], *lastresult; static size_t lastsize; char *p; const char *start; again: switch (state) { case ST_EOF: if (script == NULL) { if (more != NULL) *more = 0; return (NULL); } linenum = 0; switch (script->type) { case CU_FILE: if ((f = fopen(script->s, "r")) == NULL) err(1, "%s", script->s); fname = script->s; state = ST_FILE; goto again; case CU_STRING: if (((size_t)snprintf(string_ident, sizeof(string_ident), "\"%s\"", script->s)) >= sizeof(string_ident) - 1) (void)strcpy(string_ident + sizeof(string_ident) - 6, " ...\""); fname = string_ident; s = script->s; state = ST_STRING; goto again; } case ST_FILE: p = lastresult; if (getline(&p, &lastsize, f) != -1) { linenum++; if (linenum == 1 && p[0] == '#' && p[1] == 'n') nflag = 1; if (more != NULL) *more = !feof(f); return (lastresult = p); } else if (ferror(f)) err(1, "%s", script->s); script = script->next; (void)fclose(f); state = ST_EOF; goto again; case ST_STRING: if (linenum == 0 && s[0] == '#' && s[1] == 'n') nflag = 1; else if (s[0] == '\0') { state = ST_EOF; script = script->next; goto again; } start = s; for (;;) { switch (*s) { case '\0': state = ST_EOF; script = script->next; /* FALLTHROUGH */ case '\n': s++; linenum++; if (more != NULL) *more = 0; return (start); default: s++; } } } /* NOTREACHED */ return (NULL); } /* * Like fgets, but go through the list of files chaining them together. * Set len to the length of the line. */ int mf_fgets(SPACE *sp, enum e_spflag spflag) { struct stat sb; ssize_t len; char *dirbuf, *basebuf; static char *p = NULL; static size_t plen = 0; int c; static int firstfile; if (infile == NULL) { /* stdin? */ if (files->fname == NULL) { if (inplace != NULL) errx(1, "-I or -i may not be used with stdin"); infile = stdin; fname = "stdin"; outfile = stdout; outfname = "stdout"; } firstfile = 1; } for (;;) { if (infile != NULL && (c = getc(infile)) != EOF) { (void)ungetc(c, infile); break; } /* If we are here then either eof or no files are open yet */ if (infile == stdin) { sp->len = 0; return (0); } if (infile != NULL) { fclose(infile); if (*oldfname != '\0') { /* if there was a backup file, remove it */ unlink(oldfname); /* * Backup the original. Note that hard links * are not supported on all filesystems. */ if ((link(fname, oldfname) != 0) && (rename(fname, oldfname) != 0)) { warn("rename()"); if (*tmpfname) unlink(tmpfname); exit(1); } *oldfname = '\0'; } if (*tmpfname != '\0') { if (outfile != NULL && outfile != stdout) if (fclose(outfile) != 0) { warn("fclose()"); unlink(tmpfname); exit(1); } outfile = NULL; if (rename(tmpfname, fname) != 0) { /* this should not happen really! */ warn("rename()"); unlink(tmpfname); exit(1); } *tmpfname = '\0'; } outfname = NULL; } if (firstfile == 0) files = files->next; else firstfile = 0; if (files == NULL) { sp->len = 0; return (0); } fname = files->fname; if (inplace != NULL) { if (lstat(fname, &sb) != 0) err(1, "%s", fname); if (!(sb.st_mode & S_IFREG)) errx(1, "%s: %s %s", fname, "in-place editing only", "works for regular files"); if (*inplace != '\0') { strlcpy(oldfname, fname, sizeof(oldfname)); len = strlcat(oldfname, inplace, sizeof(oldfname)); if ((size_t)len > sizeof(oldfname)) errx(1, "%s: name too long", fname); } if ((dirbuf = strdup(fname)) == NULL || (basebuf = strdup(fname)) == NULL) err(1, "strdup"); len = snprintf(tmpfname, sizeof(tmpfname), "%s/.!%ld!%s", dirname(dirbuf), (long)getpid(), basename(basebuf)); free(dirbuf); free(basebuf); if ((size_t)len >= sizeof(tmpfname)) errx(1, "%s: name too long", fname); unlink(tmpfname); if (outfile != NULL && outfile != stdout) fclose(outfile); if ((outfile = fopen(tmpfname, "w")) == NULL) err(1, "%s", fname); fchown(fileno(outfile), sb.st_uid, sb.st_gid); fchmod(fileno(outfile), sb.st_mode & ALLPERMS); outfname = tmpfname; if (!ispan) { linenum = 0; resetstate(); } } else { outfile = stdout; outfname = "stdout"; } if ((infile = fopen(fname, "r")) == NULL) { warn("%s", fname); rval = 1; continue; } } /* * We are here only when infile is open and we still have something * to read from it. * * Use getline() so that we can handle essentially infinite input * data. The p and plen are static so each invocation gives * getline() the same buffer which is expanded as needed. */ len = getline(&p, &plen, infile); if (len == -1) err(1, "%s", fname); if (len != 0 && p[len - 1] == '\n') { sp->append_newline = 1; len--; } else if (!lastline()) { sp->append_newline = 1; } else { sp->append_newline = 0; } cspace(sp, p, len, spflag); linenum++; return (1); } /* * Add a compilation unit to the linked list */ static void add_compunit(enum e_cut type, const char *s) { struct s_compunit *cu; if ((cu = malloc(sizeof(struct s_compunit))) == NULL) err(1, "malloc"); cu->type = type; cu->s = s; cu->next = NULL; *cu_nextp = cu; cu_nextp = &cu->next; } /* * Add a file to the linked list */ static void add_file(const char *s) { struct s_flist *fp; if ((fp = malloc(sizeof(struct s_flist))) == NULL) err(1, "malloc"); fp->next = NULL; *fl_nextp = fp; fp->fname = s; fl_nextp = &fp->next; } static int next_files_have_lines(void) { struct s_flist *file; FILE *file_fd; int ch; file = files; while ((file = file->next) != NULL) { if ((file_fd = fopen(file->fname, "r")) == NULL) continue; if ((ch = getc(file_fd)) != EOF) { /* * This next file has content, therefore current * file doesn't contains the last line. */ ungetc(ch, file_fd); fclose(file_fd); return (1); } fclose(file_fd); } return (0); } int lastline(void) { int ch; if (feof(infile)) { return !( (inplace == NULL || ispan) && next_files_have_lines()); } if ((ch = getc(infile)) == EOF) { return !( (inplace == NULL || ispan) && next_files_have_lines()); } ungetc(ch, infile); return (0); } Index: head/usr.bin/soelim/soelim.c =================================================================== --- head/usr.bin/soelim/soelim.c (revision 303525) +++ head/usr.bin/soelim/soelim.c (revision 303526) @@ -1,178 +1,177 @@ /*- * Copyright (c) 2014 Baptiste Daroussin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include -#define _WITH_GETLINE #include #include #include #include #include #define C_OPTION 0x1 static StringList *includes; static void usage(void) { fprintf(stderr, "usage: soelim [-Crtv] [-I dir] [files]\n"); exit(EXIT_FAILURE); } static FILE * soelim_fopen(const char *name) { FILE *f; char path[PATH_MAX]; size_t i; if (strcmp(name, "-") == 0) return (stdin); if ((f = fopen(name, "r")) != NULL) return (f); if (*name == '/') { warn("can't open '%s'", name); return (NULL); } for (i = 0; i < includes->sl_cur; i++) { snprintf(path, sizeof(path), "%s/%s", includes->sl_str[i], name); if ((f = fopen(path, "r")) != NULL) return (f); } warn("can't open '%s'", name); return (f); } static int soelim_file(FILE *f, int flag) { char *line = NULL; char *walk, *cp; size_t linecap = 0; ssize_t linelen; if (f == NULL) return (1); while ((linelen = getline(&line, &linecap, f)) > 0) { if (strncmp(line, ".so", 3) != 0) { printf("%s", line); continue; } walk = line + 3; if (!isspace(*walk) && ((flag & C_OPTION) == 0)) { printf("%s", line); continue; } while (isspace(*walk)) walk++; cp = walk; while (*cp != '\0' && !isspace(*cp)) cp++; *cp = 0; if (cp < line + linelen) cp++; if (*walk == '\0') { printf("%s", line); continue; } if (soelim_file(soelim_fopen(walk), flag) == 1) { free(line); return (1); } if (*cp != '\0') printf("%s", cp); } free(line); fclose(f); return (0); } int main(int argc, char **argv) { int ch, i; int ret = 0; int flags = 0; includes = sl_init(); if (includes == NULL) err(EXIT_FAILURE, "sl_init()"); while ((ch = getopt(argc, argv, "CrtvI:")) != -1) { switch (ch) { case 'C': flags |= C_OPTION; break; case 'r': case 'v': case 't': /* stub compatibility with groff's soelim */ break; case 'I': sl_add(includes, optarg); break; default: sl_free(includes, 0); usage(); } } argc -= optind; argv += optind; if (argc == 0) ret = soelim_file(stdin, flags); for (i = 0; i < argc; i++) ret = soelim_file(soelim_fopen(argv[i]), flags); sl_free(includes, 0); return (ret); } Index: head/usr.bin/uniq/uniq.c =================================================================== --- head/usr.bin/uniq/uniq.c (revision 303525) +++ head/usr.bin/uniq/uniq.c (revision 303526) @@ -1,357 +1,356 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Case Larsen. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)uniq.c 8.3 (Berkeley) 5/4/95"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include -#define _WITH_GETLINE #include #include #include #include #include #include #include static int cflag, dflag, uflag, iflag; static int numchars, numfields, repeats; static FILE *file(const char *, const char *); static wchar_t *convert(const char *); static int inlcmp(const char *, const char *); static void show(FILE *, const char *); static wchar_t *skip(wchar_t *); static void obsolete(char *[]); static void usage(void); static void strerror_init(void) { /* * Cache NLS data before entering capability mode. * XXXPJD: There should be strerror_init() and strsignal_init() in libc. */ (void)catopen("libc", NL_CAT_LOCALE); } int main (int argc, char *argv[]) { wchar_t *tprev, *tthis; FILE *ifp, *ofp; int ch, comp; size_t prevbuflen, thisbuflen, b1; char *prevline, *thisline, *p; const char *ifn; cap_rights_t rights; (void) setlocale(LC_ALL, ""); obsolete(argv); while ((ch = getopt(argc, argv, "cdif:s:u")) != -1) switch (ch) { case 'c': cflag = 1; break; case 'd': dflag = 1; break; case 'i': iflag = 1; break; case 'f': numfields = strtol(optarg, &p, 10); if (numfields < 0 || *p) errx(1, "illegal field skip value: %s", optarg); break; case 's': numchars = strtol(optarg, &p, 10); if (numchars < 0 || *p) errx(1, "illegal character skip value: %s", optarg); break; case 'u': uflag = 1; break; case '?': default: usage(); } argc -= optind; argv += optind; /* If no flags are set, default is -d -u. */ if (cflag) { if (dflag || uflag) usage(); } else if (!dflag && !uflag) dflag = uflag = 1; if (argc > 2) usage(); ifp = stdin; ifn = "stdin"; ofp = stdout; if (argc > 0 && strcmp(argv[0], "-") != 0) ifp = file(ifn = argv[0], "r"); cap_rights_init(&rights, CAP_FSTAT, CAP_READ); if (cap_rights_limit(fileno(ifp), &rights) < 0 && errno != ENOSYS) err(1, "unable to limit rights for %s", ifn); cap_rights_init(&rights, CAP_FSTAT, CAP_WRITE); if (argc > 1) ofp = file(argv[1], "w"); else cap_rights_set(&rights, CAP_IOCTL); if (cap_rights_limit(fileno(ofp), &rights) < 0 && errno != ENOSYS) { err(1, "unable to limit rights for %s", argc > 1 ? argv[1] : "stdout"); } if (cap_rights_is_set(&rights, CAP_IOCTL)) { unsigned long cmd; cmd = TIOCGETA; /* required by isatty(3) in printf(3) */ if (cap_ioctls_limit(fileno(ofp), &cmd, 1) < 0 && errno != ENOSYS) { err(1, "unable to limit ioctls for %s", argc > 1 ? argv[1] : "stdout"); } } strerror_init(); if (cap_enter() < 0 && errno != ENOSYS) err(1, "unable to enter capability mode"); prevbuflen = thisbuflen = 0; prevline = thisline = NULL; if (getline(&prevline, &prevbuflen, ifp) < 0) { if (ferror(ifp)) err(1, "%s", ifn); exit(0); } tprev = convert(prevline); if (!cflag && uflag && dflag) show(ofp, prevline); tthis = NULL; while (getline(&thisline, &thisbuflen, ifp) >= 0) { if (tthis != NULL) free(tthis); tthis = convert(thisline); if (tthis == NULL && tprev == NULL) comp = inlcmp(thisline, prevline); else if (tthis == NULL || tprev == NULL) comp = 1; else comp = wcscoll(tthis, tprev); if (comp) { /* If different, print; set previous to new value. */ if (cflag || !dflag || !uflag) show(ofp, prevline); p = prevline; b1 = prevbuflen; prevline = thisline; prevbuflen = thisbuflen; if (tprev != NULL) free(tprev); tprev = tthis; if (!cflag && uflag && dflag) show(ofp, prevline); thisline = p; thisbuflen = b1; tthis = NULL; repeats = 0; } else ++repeats; } if (ferror(ifp)) err(1, "%s", ifn); if (cflag || !dflag || !uflag) show(ofp, prevline); exit(0); } static wchar_t * convert(const char *str) { size_t n; wchar_t *buf, *ret, *p; if ((n = mbstowcs(NULL, str, 0)) == (size_t)-1) return (NULL); if (SIZE_MAX / sizeof(*buf) < n + 1) errx(1, "conversion buffer length overflow"); if ((buf = malloc((n + 1) * sizeof(*buf))) == NULL) err(1, "malloc"); if (mbstowcs(buf, str, n + 1) != n) errx(1, "internal mbstowcs() error"); /* The last line may not end with \n. */ if (n > 0 && buf[n - 1] == L'\n') buf[n - 1] = L'\0'; /* If requested get the chosen fields + character offsets. */ if (numfields || numchars) { if ((ret = wcsdup(skip(buf))) == NULL) err(1, "wcsdup"); free(buf); } else ret = buf; if (iflag) { for (p = ret; *p != L'\0'; p++) *p = towlower(*p); } return (ret); } static int inlcmp(const char *s1, const char *s2) { int c1, c2; while (*s1 == *s2++) if (*s1++ == '\0') return (0); c1 = (unsigned char)*s1; c2 = (unsigned char)*(s2 - 1); /* The last line may not end with \n. */ if (c1 == '\n') c1 = '\0'; if (c2 == '\n') c2 = '\0'; return (c1 - c2); } /* * show -- * Output a line depending on the flags and number of repetitions * of the line. */ static void show(FILE *ofp, const char *str) { if (cflag) (void)fprintf(ofp, "%4d %s", repeats + 1, str); if ((dflag && repeats) || (uflag && !repeats)) (void)fprintf(ofp, "%s", str); } static wchar_t * skip(wchar_t *str) { int nchars, nfields; for (nfields = 0; *str != L'\0' && nfields++ != numfields; ) { while (iswblank(*str)) str++; while (*str != L'\0' && !iswblank(*str)) str++; } for (nchars = numchars; nchars-- && *str != L'\0'; ++str) ; return(str); } static FILE * file(const char *name, const char *mode) { FILE *fp; if ((fp = fopen(name, mode)) == NULL) err(1, "%s", name); return(fp); } static void obsolete(char *argv[]) { int len; char *ap, *p, *start; while ((ap = *++argv)) { /* Return if "--" or not an option of any form. */ if (ap[0] != '-') { if (ap[0] != '+') return; } else if (ap[1] == '-') return; if (!isdigit((unsigned char)ap[1])) continue; /* * Digit signifies an old-style option. Malloc space for dash, * new option and argument. */ len = strlen(ap); if ((start = p = malloc(len + 3)) == NULL) err(1, "malloc"); *p++ = '-'; *p++ = ap[0] == '+' ? 's' : 'f'; (void)strcpy(p, ap + 1); *argv = start; } } static void usage(void) { (void)fprintf(stderr, "usage: uniq [-c | -d | -u] [-i] [-f fields] [-s chars] [input [output]]\n"); exit(1); }