diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..18473358 Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index 2c2a517e..ee8632c7 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,9 @@ .*.sw? # Downloaded files: +/build/ +/release/ +/smallRelease/ /Frameworks/ /curl/ /file_cmds/ @@ -24,6 +27,8 @@ /libutil/ /shell_cmds/ /text_cmds/ +/network_cmds-543/ +/ssh_keygen_110/ # Precompiled Headers *.gch diff --git a/TODO b/TODO new file mode 100644 index 00000000..c25523b4 --- /dev/null +++ b/TODO @@ -0,0 +1,25 @@ +REVIEWS ! + +- still crashing randomly. But less often. + +- write Lua script for TeX format creation from fmtutil.cnf +- write Lua script for ls-lR generation (?) +- write Lua script for tlmgr update ? + +- add file coordinator / file presenter stuff. Done +- add dlopen / dlsym / dlclose stuff ? + Would open the way for local compilers. + +- e-mail Luigi Scarso + +- Python: numpy, matplotlib make more sense inside Pythonista, seriously +- Stop trying to make it happen inside Blink, it won't, seriously. OK. + +- keep adding strerror(errno) to all commands in ios_system. Except warnx, errx. +- swift programming? + +- move to Swift? Why? +- git swift mapping? Hard. + +- Jupyter ? + diff --git a/TODO2 b/TODO2 new file mode 100644 index 00000000..d98ac5bc --- /dev/null +++ b/TODO2 @@ -0,0 +1,38 @@ +procfile("-") +then grep_open(NULL) + +Now: grep is the first thread to reach phtread_start +And it has been "joined" +So the second thread never starts. Need to "not join". + +If I don't join, by the time grep starts, everything has been erased. + +So need to wait for the thread to be active, then release. + + +- replace STDIN_FILENO with fileno(thread_stdin). Can't use #define +- replace all isatty +- TODO: also TeX/LaTeX. Keep compatible? +- X move global variables to __thread for multi-calls. + +- todo: add ID of stream to close to params (in and out) +- don't close streams equal to thread_stdout, someone else will. +- don't close threads for system(), only for popen. Will be hard. +- easy to close out-stream after popen, harder to close in-stream. + +X __thread for all commands in all groups +! extern variables inside functions also +X continue testing +- create new patches for Python and Lua +- test if python now works with system(python) (ballsy) +X fileno(thread_stdout) does not exist. + +- TODO (now): +X ssh + command (easy-ish). +X mercurial calls subprocess.Popen(), it seems. +X popen does not call posix_popen +X dup2 inside execute_child. To state "p2cread == (new process) stdin" +- can use that. +- Need to think about what Popen does. + +X hg connects but doesn't read data / write ? diff --git a/awk/.gitignore b/awk/.gitignore new file mode 100644 index 00000000..95c43209 --- /dev/null +++ b/awk/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ diff --git a/awk/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/awk/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..706eedee --- /dev/null +++ b/awk/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios_system.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/awk/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from ios_system.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to awk/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/awk/Package.swift b/awk/Package.swift new file mode 100644 index 00000000..303ae444 --- /dev/null +++ b/awk/Package.swift @@ -0,0 +1,35 @@ +// swift-tools-version:5.2 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "awk", + // thread-local variables are only available with iOS 11+. This setting is required for compilation. + platforms: [.iOS(.v11)], + products: [ + // Products define the executables and libraries produced by a package, and make them visible to other packages. + .library( + name: "awk", + targets: ["awk"]), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0"), + // Depends on the local package, ios_system: + .package(path: "../ios_system") + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages which this package depends on. + .target( + name: "awk", + dependencies: ["ios_system"], + exclude: [], + cSettings: [.headerSearchPath("../"), + .headerSearchPath("./"), + ], + linkerSettings: [.linkedFramework("ios_system")] + ), + ] +) diff --git a/awk/README.md b/awk/README.md new file mode 100644 index 00000000..687d7480 --- /dev/null +++ b/awk/README.md @@ -0,0 +1,3 @@ +# awk + +A description of this package. diff --git a/awk/Sources/awk/awk.h b/awk/Sources/awk/awk.h new file mode 100644 index 00000000..b991a4ed --- /dev/null +++ b/awk/Sources/awk/awk.h @@ -0,0 +1,235 @@ +/**************************************************************** +Copyright (C) Lucent Technologies 1997 +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name Lucent Technologies or any of +its entities not be used in advertising or publicity pertaining +to distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +****************************************************************/ + +#include + +typedef double Awkfloat; + +/* unsigned char is more trouble than it's worth */ + +typedef unsigned char uschar; + +#define xfree(a) { if ((a) != NULL) { free((void *) (a)); (a) = NULL; } } + +#define NN(p) ((p) ? (p) : "(null)") /* guaranteed non-null for dprintf +*/ +#define DEBUG +#ifdef DEBUG + /* uses have to be doubly parenthesized */ +# define dprintf(x) if (dbg) fprintf x +#else +# define dprintf(x) +#endif + +extern __thread int compile_time; /* 1 if compiling, 0 if running */ +extern __thread int safe; /* 0 => unsafe, 1 => safe */ +extern __thread int Unix2003_compat; + +#define RECSIZE (8 * 1024) /* sets limit on records, fields, etc., etc. */ +extern __thread int recsize; /* size of current record, orig RECSIZE */ + +extern __thread char **FS; +extern __thread char **RS; +extern __thread char **ORS; +extern __thread char **OFS; +extern __thread char **OFMT; +extern __thread Awkfloat *NR; +extern __thread Awkfloat *FNR; +extern __thread Awkfloat *NF; +extern __thread char **FILENAME; +extern __thread char **SUBSEP; +extern __thread Awkfloat *RSTART; +extern __thread Awkfloat *RLENGTH; + +extern __thread char *record; /* points to $0 */ +extern __thread int lineno; /* line number in awk program */ +extern __thread int errorflag; /* 1 if error has occurred */ +extern __thread int donefld; /* 1 if record broken into fields */ +extern __thread int donerec; /* 1 if record is valid (no fld has changed */ +extern __thread char inputFS[]; /* FS at time of input, for field splitting */ + +extern __thread int dbg; + +extern __thread char *patbeg; /* beginning of pattern matched */ +extern __thread int patlen; /* length of pattern matched. set in b.c */ + +/* Cell: all information about a variable or constant */ + +typedef struct Cell { + uschar ctype; /* OCELL, OBOOL, OJUMP, etc. */ + uschar csub; /* CCON, CTEMP, CFLD, etc. */ + char *nval; /* name, for variables only */ + char *sval; /* string value */ + Awkfloat fval; /* value as number */ + int tval; /* type info: STR|NUM|ARR|FCN|FLD|CON|DONTFREE */ + struct Cell *cnext; /* ptr to next if chained */ +} Cell; + +typedef struct Array { /* symbol table array */ + int nelem; /* elements in table right now */ + int size; /* size of tab */ + Cell **tab; /* hash table pointers */ +} Array; + +#define NSYMTAB 50 /* initial size of a symbol table */ +extern __thread Array *symtab; + +extern __thread Cell *nrloc; /* NR */ +extern __thread Cell *fnrloc; /* FNR */ +extern __thread Cell *nfloc; /* NF */ +extern __thread Cell *rstartloc; /* RSTART */ +extern __thread Cell *rlengthloc; /* RLENGTH */ + +/* Cell.tval values: */ +#define NUM 01 /* number value is valid */ +#define STR 02 /* string value is valid */ +#define DONTFREE 04 /* string space is not freeable */ +#define CON 010 /* this is a constant */ +#define ARR 020 /* this is an array */ +#define FCN 040 /* this is a function name */ +#define FLD 0100 /* this is a field $1, $2, ... */ +#define REC 0200 /* this is $0 */ + + +/* function types */ +#define FLENGTH 1 +#define FSQRT 2 +#define FEXP 3 +#define FLOG 4 +#define FINT 5 +#define FSYSTEM 6 +#define FRAND 7 +#define FSRAND 8 +#define FSIN 9 +#define FCOS 10 +#define FATAN 11 +#define FTOUPPER 12 +#define FTOLOWER 13 +#define FFLUSH 14 + +/* Node: parse tree is made of nodes, with Cell's at bottom */ + +typedef struct Node { + int ntype; + struct Node *nnext; + int lineno; + int nobj; + int nnarg; + struct Node *narg[1]; /* variable: actual size set by calling malloc */ +} Node; + +#define NIL ((Node *) 0) + +extern __thread Node *winner; +extern __thread Node *nullstat; +extern __thread Node *nullnode; + +/* ctypes */ +#define OCELL 1 +#define OBOOL 2 +#define OJUMP 3 + +/* Cell subtypes: csub */ +#define CFREE 7 +#define CCOPY 6 +#define CCON 5 +#define CTEMP 4 +#define CNAME 3 +#define CVAR 2 +#define CFLD 1 +#define CUNK 0 + +/* bool subtypes */ +#define BTRUE 11 +#define BFALSE 12 + +/* jump subtypes */ +#define JEXIT 21 +#define JNEXT 22 +#define JBREAK 23 +#define JCONT 24 +#define JRET 25 +#define JNEXTFILE 26 + +/* node types */ +#define NVALUE 1 +#define NSTAT 2 +#define NEXPR 3 + + +extern __thread int pairstack[], paircnt; + +#define notlegal(n) (n <= FIRSTTOKEN || n >= LASTTOKEN || proctab[n-FIRSTTOKEN] == nullproc) +#define isvalue(n) ((n)->ntype == NVALUE) +#define isexpr(n) ((n)->ntype == NEXPR) +#define isjump(n) ((n)->ctype == OJUMP) +#define isexit(n) ((n)->csub == JEXIT) +#define isbreak(n) ((n)->csub == JBREAK) +#define iscont(n) ((n)->csub == JCONT) +#define isnext(n) ((n)->csub == JNEXT || (n)->csub == JNEXTFILE) +#define isret(n) ((n)->csub == JRET) +#define isrec(n) ((n)->tval & REC) +#define isfld(n) ((n)->tval & FLD) +#define isstr(n) ((n)->tval & STR) +#define isnum(n) ((n)->tval & NUM) +#define isarr(n) ((n)->tval & ARR) +#define isfcn(n) ((n)->tval & FCN) +#define istrue(n) ((n)->csub == BTRUE) +#define istemp(n) ((n)->csub == CTEMP) +#define isargument(n) ((n)->nobj == ARG) +/* #define freeable(p) (!((p)->tval & DONTFREE)) */ +#define freeable(p) ( ((p)->tval & (STR|DONTFREE)) == STR ) + +/* structures used by regular expression matching machinery, mostly b.c: */ + +#define NCHARS (256+3) /* 256 handles 8-bit chars; 128 does 7-bit */ + /* watch out in match(), etc. */ +#define NSTATES 32 + +typedef struct rrow { + long ltype; /* long avoids pointer warnings on 64-bit */ + union { + int i; + Node *np; + uschar *up; + } lval; /* because Al stores a pointer in it! */ + int *lfollow; +} rrow; + +typedef struct fa { + uschar gototab[NSTATES][NCHARS]; + uschar out[NSTATES]; + uschar *restr; + int *posns[NSTATES]; + int anchor; + int use; + int initstat; + int curstat; + int accept; + int reset; + struct rrow re[1]; /* variable: actual size set by calling malloc */ +} fa; + + +#include "proto.h" diff --git a/awk/Sources/awk/awk_main.c b/awk/Sources/awk/awk_main.c new file mode 100644 index 00000000..7e0f83cf --- /dev/null +++ b/awk/Sources/awk/awk_main.c @@ -0,0 +1,250 @@ +/**************************************************************** +Copyright (C) Lucent Technologies 1997 +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name Lucent Technologies or any of +its entities not be used in advertising or publicity pertaining +to distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +****************************************************************/ + +const char *version = "version 20070501"; + +#define DEBUG +#include +#include +#include +#include +#include +#include +#include "awk.h" +#include "ytab.h" + +// #ifdef __APPLE__ +// #include "get_compat.h" +// #else +#define COMPAT_MODE(func, mode) 1 +// #endif +#include "ios_error.h" + +extern char **environ; +extern __thread int nfields; + +__thread int dbg = 0; // Set to 1 for serious debugging +__thread char *cmdname; /* gets argv[0] for error messages */ +extern __thread FILE *yyin; /* lex input file */ +__thread char *lexprog; /* points to program argument if it exists */ +extern __thread int errorflag; /* non-zero if any syntax errors; set by yyerror */ +__thread int compile_time = 2; /* for error printing: */ + /* 2 = cmdline, 1 = compile, 0 = running */ + +#define MAX_PFILE 20 /* max number of -f's */ + +static __thread char *pfile[MAX_PFILE]; /* program filenames from -f's */ +static __thread int npfile = 0; /* number of filenames */ +static __thread int curpfile = 0; /* current filename */ + +__thread int safe = 0; /* 1 => "safe" mode */ +__thread int Unix2003_compat; + +static void initializeVariables() { + // initialize all flags: + cmdname = NULL; + extern __thread int infunc; + infunc = 0; /* = 1 if in arglist or body of func */ + extern __thread int inloop; + inloop = 0; /* = 1 if in while, for, do */ + + extern __thread int *setvec; + extern __thread int *tmpset; + if (setvec != 0) { /* first time through any RE */ + free(setvec); setvec = 0; + free(tmpset); tmpset = 0; + } + yyin = 0; + nfields = 2; // MAXFLD + npfile = 0; + curpfile = 0; + compile_time = 2; + errorflag = 0; + lexprog = 0; + extern __thread int awk_firsttime; + awk_firsttime = 1; + + extern __thread int lastfld; + lastfld = 0; /* last used field */ + extern __thread int argno; + argno = 1; /* current input argument number */ + if (symtab != NULL) { + free(symtab->tab); + free(symtab); + symtab = NULL; + } + // Variables from lib.c + if (record) { free(record); record = NULL;} + recsize = RECSIZE; + extern __thread char *fields; + if (fields) { free(fields); fields = NULL; } + extern __thread int fieldssize; + fieldssize = RECSIZE; + extern __thread Cell **fldtab; /* pointers to Cells */ + if (fldtab) { free(fldtab); fldtab = NULL; } +} + + +int awk_main(int argc, char *argv[]) +{ + const char *fs = NULL; + initializeVariables(); + + setlocale(LC_CTYPE, ""); + setlocale(LC_NUMERIC, "C"); /* for parsing cmdline & prog */ + cmdname = argv[0]; + if (argc == 1) { + fprintf(thread_stderr, + "usage: %s [-F fs] [-v var=value] [-f progfile | 'prog'] [file ...]\n", + cmdname); + exit(1); + } + Unix2003_compat = COMPAT_MODE("bin/awk", "unix2003"); + signal(SIGFPE, fpecatch); + yyin = NULL; + symtab = makesymtab(NSYMTAB/NSYMTAB); + while (argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0') { + if (strcmp(argv[1],"-version") == 0 || strcmp(argv[1],"--version") == 0) { + fprintf(thread_stdout, "awk %s\n", version); + exit(0); + break; + } + if (strncmp(argv[1], "--", 2) == 0) { /* explicit end of args */ + argc--; + argv++; + break; + } + switch (argv[1][1]) { + case 's': + if (strcmp(argv[1], "-safe") == 0) + safe = 1; + break; + case 'f': /* next argument is program filename */ + argc--; + argv++; + if (argc <= 1) + FATAL("no program filename"); + if (npfile >= MAX_PFILE - 1) + FATAL("too many -f options"); + pfile[npfile++] = argv[1]; + break; + case 'F': /* set field separator */ + if (argv[1][2] != 0) { /* arg is -Fsomething */ + if (argv[1][2] == 't' && argv[1][3] == 0) /* wart: t=>\t */ + fs = "\t"; + else if (argv[1][2] != 0) + fs = &argv[1][2]; + } else { /* arg is -F something */ + argc--; argv++; + if (argc > 1 && argv[1][0] == 't' && argv[1][1] == 0) /* wart: t=>\t */ + fs = "\t"; + else if (argc > 1 && argv[1][0] != 0) + fs = &argv[1][0]; + } + if (fs == NULL || *fs == '\0') + WARNING("field separator FS is empty"); + break; + case 'v': /* -v a=1 to be done NOW. one -v for each */ + if (argv[1][2] == '\0' && --argc > 1 && isclvar((++argv)[1])) + setclvar(argv[1]); + else + FATAL("invalid -v option"); + break; + case 'd': + dbg = atoi(&argv[1][2]); + if (dbg == 0) + dbg = 1; + fprintf(thread_stdout, "awk %s\n", version); + break; + default: + WARNING("unknown option %s ignored", argv[1]); + break; + } + argc--; + argv++; + } + /* argv[1] is now the first argument */ + if (npfile == 0) { /* no -f; first argument is program */ + if (argc <= 1) { + if (dbg) + exit(0); + FATAL("no program given"); + } + dprintf( (thread_stdout, "program = |%s|\n", argv[1]) ); + lexprog = argv[1]; + argc--; + argv++; + } + recinit(recsize); + syminit(); + compile_time = 1; + argv[0] = cmdname; /* put prog name at front of arglist */ + dprintf( (thread_stdout, "argc=%d, argv[0]=%s\n", argc, argv[0]) ); + arginit(argc, argv); + if (!safe) + envinit(environ); + yyparse(); + setlocale(LC_NUMERIC, ""); /* back to whatever it is locally */ + if (fs) + *FS = qstring(fs, '\0'); + dprintf( (thread_stdout, "errorflag=%d\n", errorflag) ); + if (errorflag == 0) { + compile_time = 0; + run(winner); + winner = NULL; + } else + bracecheck(); + return(errorflag); +} + +int pgetc(void) /* get 1 character from awk program */ +{ + int c; + + for (;;) { + if (yyin == NULL) { + if (curpfile >= npfile) + return EOF; + if (strcmp(pfile[curpfile], "-") == 0) + yyin = thread_stdin; + else if ((yyin = fopen(pfile[curpfile], "r")) == NULL) + FATAL("can't open file %s", pfile[curpfile]); + lineno = 1; + } + if ((c = getc(yyin)) != EOF) + return c; + if (yyin != thread_stdin) + fclose(yyin); + yyin = NULL; + curpfile++; + } +} + +char *cursource(void) /* current source file name */ +{ + if (npfile > 0) + return pfile[curpfile]; + else + return NULL; +} diff --git a/awk/Sources/awk/b.c b/awk/Sources/awk/b.c new file mode 100644 index 00000000..36975542 --- /dev/null +++ b/awk/Sources/awk/b.c @@ -0,0 +1,1271 @@ +/**************************************************************** +Copyright (C) Lucent Technologies 1997 +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name Lucent Technologies or any of +its entities not be used in advertising or publicity pertaining +to distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +****************************************************************/ + +/* lasciate ogne speranza, voi ch'intrate. */ + +#define DEBUG + +#include +#include +#include +#include +#include "awk.h" +#include "ytab.h" +#include +#include "ios_error.h" + +#define HAT (NCHARS+2) /* matches ^ in regular expr */ + /* NCHARS is 2**n */ +#define MAXLIN 22 + +#define type(v) (v)->nobj /* badly overloaded here */ +#define info(v) (v)->ntype /* badly overloaded here */ +#define left(v) (v)->narg[0] +#define right(v) (v)->narg[1] +#define parent(v) (v)->nnext + +#define LEAF case CCL: case NCCL: case CHAR: case DOT: case FINAL: case ALL: +#define ELEAF case EMPTYRE: /* empty string in regexp */ +#define UNARY case STAR: case PLUS: case QUEST: + +/* encoding in tree Nodes: + leaf (CCL, NCCL, CHAR, DOT, FINAL, ALL, EMPTYRE): + left is index, right contains value or pointer to value + unary (STAR, PLUS, QUEST): left is child, right is null + binary (CAT, OR): left and right are children + parent contains pointer to parent +*/ + + +__thread int *setvec; +__thread int *tmpset; +static __thread int maxsetvec = 0; + +static __thread int rtok; /* next token in current re */ +static __thread int rlxval; +static __thread uschar *rlxstr; +static __thread uschar *prestr; /* current position in current re */ +static __thread uschar *lastre; /* origin of last re */ +static __thread uschar *lastatom; /* origin of last Atom */ +static __thread uschar *starttok; +static __thread char *basestr; /* starts with original, replaced during + repetition processing */ +static __thread char *firstbasestr; + +static __thread FILE * replogfile = 0; + +static __thread int setcnt; +static __thread int poscnt; + +__thread char *patbeg; +__thread int patlen; + +#define NFA 20 /* cache this many dynamic fa's */ +static __thread fa *fatab[NFA]; +static __thread int nfatab = 0; /* entries in fatab */ + +fa *makedfa(const char *s, int anchor) /* returns dfa for reg expr s */ +{ + int i, use, nuse; + fa *pfa; + static __thread int now = 1; + + if (setvec == 0) { /* first time through any RE */ + maxsetvec = MAXLIN; + setvec = (int *) malloc(maxsetvec * sizeof(int)); + tmpset = (int *) malloc(maxsetvec * sizeof(int)); + if (setvec == 0 || tmpset == 0) + overflo("out of space initializing makedfa"); + } + + if (compile_time) /* a constant for sure */ + return mkdfa(s, anchor); + for (i = 0; i < nfatab; i++) /* is it there already? */ + if (fatab[i]->anchor == anchor + && strcmp((const char *) fatab[i]->restr, s) == 0) { + fatab[i]->use = now++; + return fatab[i]; + } + pfa = mkdfa(s, anchor); + if (nfatab < NFA) { /* room for another */ + fatab[nfatab] = pfa; + fatab[nfatab]->use = now++; + nfatab++; + return pfa; + } + use = fatab[0]->use; /* replace least-recently used */ + nuse = 0; + for (i = 1; i < nfatab; i++) + if (fatab[i]->use < use) { + use = fatab[i]->use; + nuse = i; + } + freefa(fatab[nuse]); + fatab[nuse] = pfa; + pfa->use = now++; + return pfa; +} + +fa *mkdfa(const char *s, int anchor) /* does the real work of making a dfa */ + /* anchor = 1 for anchored matches, else 0 */ +{ + Node *p, *p1; + fa *f; + + firstbasestr = (char *)s; + basestr = firstbasestr; + if (replogfile==0) { + /* disabled + replogfile = fopen("/tmp/repeatlog", "a"); + */ + } + p = reparse(s); + p1 = op2(CAT, op2(STAR, op2(ALL, NIL, NIL), NIL), p); + /* put ALL STAR in front of reg. exp. */ + p1 = op2(CAT, p1, op2(FINAL, NIL, NIL)); + /* put FINAL after reg. exp. */ + + poscnt = 0; + penter(p1); /* enter parent pointers and leaf indices */ + if ((f = (fa *) calloc(1, sizeof(fa) + poscnt*sizeof(rrow))) == NULL) + overflo("out of space for fa"); + f->accept = poscnt-1; /* penter has computed number of positions in re */ + cfoll(f, p1); /* set up follow sets */ + freetr(p1); + if ((f->posns[0] = (int *) calloc(1, *(f->re[0].lfollow)*sizeof(int))) == NULL) + overflo("out of space in makedfa"); + if ((f->posns[1] = (int *) calloc(1, sizeof(int))) == NULL) + overflo("out of space in makedfa"); + *f->posns[1] = 0; + f->initstat = makeinit(f, anchor); + f->anchor = anchor; + f->restr = (uschar *) tostring(s); + if (replogfile) { + fflush(replogfile); + fclose(replogfile); + replogfile=0; + } + if (firstbasestr != basestr) { + if (basestr) free(basestr); + } + return f; +} + +int makeinit(fa *f, int anchor) +{ + int i, k; + + f->curstat = 2; + f->out[2] = 0; + f->reset = 0; + k = *(f->re[0].lfollow); + xfree(f->posns[2]); + if ((f->posns[2] = (int *) calloc(1, (k+1)*sizeof(int))) == NULL) + overflo("out of space in makeinit"); + for (i=0; i <= k; i++) { + (f->posns[2])[i] = (f->re[0].lfollow)[i]; + } + if ((f->posns[2])[1] == f->accept) + f->out[2] = 1; + for (i=0; i < NCHARS; i++) + f->gototab[2][i] = 0; + f->curstat = cgoto(f, 2, HAT); + if (anchor) { + *f->posns[2] = k-1; /* leave out position 0 */ + for (i=0; i < k; i++) { + (f->posns[0])[i] = (f->posns[2])[i]; + } + + f->out[0] = f->out[2]; + if (f->curstat != 2) + --(*f->posns[f->curstat]); + } + return f->curstat; +} + +void penter(Node *p) /* set up parent pointers and leaf indices */ +{ + switch (type(p)) { + ELEAF + LEAF + info(p) = poscnt; + poscnt++; + break; + UNARY + penter(left(p)); + parent(left(p)) = p; + break; + case CAT: + case OR: + penter(left(p)); + penter(right(p)); + parent(left(p)) = p; + parent(right(p)) = p; + break; + default: /* can't happen */ + FATAL("can't happen: unknown type %d in penter", type(p)); + break; + } +} + +void freetr(Node *p) /* free parse tree */ +{ + switch (type(p)) { + ELEAF + LEAF + // fprintf(stderr, "Freeing leaf %x\n", p); fflush(stderr); + xfree(p); + break; + UNARY + freetr(left(p)); + // fprintf(stderr, "Freeing unary %x\n", p); fflush(stderr); + xfree(p); + break; + case CAT: + case OR: + freetr(left(p)); + freetr(right(p)); + // fprintf(stderr, "Freeing tree %x\n", p); fflush(stderr); + xfree(p); + break; + default: /* can't happen */ + FATAL("can't happen: unknown type %d in freetr", type(p)); + break; + } +} + +/* in the parsing of regular expressions, metacharacters like . have */ +/* to be seen literally; \056 is not a metacharacter. */ + +int hexstr(char **pp) /* find and eval hex string at pp, return new p */ +{ /* only pick up one 8-bit byte (2 chars) */ + uschar *p; + int n = 0; + int i; + + for (i = 0, p = (uschar *) *pp; i < 2 && isxdigit(*p); i++, p++) { + if (isdigit(*p)) + n = 16 * n + *p - '0'; + else if (*p >= 'a' && *p <= 'f') + n = 16 * n + *p - 'a' + 10; + else if (*p >= 'A' && *p <= 'F') + n = 16 * n + *p - 'A' + 10; + } + *pp = (char *) p; + return n; +} + +#define isoctdigit(c) ((c) >= '0' && (c) <= '7') /* multiple use of arg */ + +int quoted(char **pp) /* pick up next thing after a \\ */ + /* and increment *pp */ +{ + char *p = *pp; + int c; + + if ((c = *p++) == 't') + c = '\t'; + else if (c == 'n') + c = '\n'; + else if (c == 'f') + c = '\f'; + else if (c == 'r') + c = '\r'; + else if (c == 'b') + c = '\b'; + else if (c == '\\') + c = '\\'; + else if (c == 'x') { /* hexadecimal goo follows */ + c = hexstr(&p); /* this adds a null if number is invalid */ + } else if (isoctdigit(c)) { /* \d \dd \ddd */ + int n = c - '0'; + if (isoctdigit(*p)) { + n = 8 * n + *p++ - '0'; + if (isoctdigit(*p)) + n = 8 * n + *p++ - '0'; + } + c = n; + } /* else */ + /* c = c; */ + *pp = p; + return c; +} + +char *cclenter(const char *argp) /* add a character class */ +{ + int i, c, c2; + uschar *p = (uschar *) argp; + uschar *op, *bp; + static __thread uschar *buf = 0; + static __thread int bufsz = 100; + + op = p; + if (buf == 0 && (buf = (uschar *) malloc(bufsz)) == NULL) + FATAL("out of space for character class [%.10s...] 1", p); + bp = buf; + for (i = 0; (c = *p++) != 0; ) { + if (c == '\\') { + c = quoted((char **) &p); + } else if (c == '-' && i > 0 && bp[-1] != 0) { + if (*p != 0) { + c = bp[-1]; + c2 = *p++; + if (c2 == '\\') + c2 = quoted((char **) &p); + if (c > c2) { /* empty; ignore */ + bp--; + i--; + continue; + } + while (c < c2) { + if (!adjbuf((char **) &buf, &bufsz, bp-buf+2, 100, (char **) &bp, "cclenter1")) + FATAL("out of space for character class [%.10s...] 2", p); + *bp++ = ++c; + i++; + } + continue; + } + } + if (!adjbuf((char **) &buf, &bufsz, bp-buf+2, 100, (char **) &bp, "cclenter2")) + FATAL("out of space for character class [%.10s...] 3", p); + *bp++ = c; + i++; + } + *bp = 0; + dprintf( (thread_stdout, "cclenter: in = |%s|, out = |%s|\n", op, buf) ); + xfree(op); + return (char *) tostring((char *) buf); +} + +void overflo(const char *s) +{ + FATAL("regular expression too big: %.30s...", s); +} + +void cfoll(fa *f, Node *v) /* enter follow set of each leaf of vertex v into lfollow[leaf] */ +{ + int i; + int *p; + + switch (type(v)) { + ELEAF + LEAF + f->re[info(v)].ltype = type(v); + f->re[info(v)].lval.np = right(v); + while (f->accept >= maxsetvec) { /* guessing here! */ + maxsetvec *= 4; + setvec = (int *) realloc(setvec, maxsetvec * sizeof(int)); + tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int)); + if (setvec == 0 || tmpset == 0) + overflo("out of space in cfoll()"); + } + for (i = 0; i <= f->accept; i++) + setvec[i] = 0; + setcnt = 0; + follow(v); /* computes setvec and setcnt */ + if ((p = (int *) calloc(1, (setcnt+1)*sizeof(int))) == NULL) + overflo("out of space building follow set"); + f->re[info(v)].lfollow = p; + *p = setcnt; + for (i = f->accept; i >= 0; i--) + if (setvec[i] == 1) + *++p = i; + break; + UNARY + cfoll(f,left(v)); + break; + case CAT: + case OR: + cfoll(f,left(v)); + cfoll(f,right(v)); + break; + default: /* can't happen */ + FATAL("can't happen: unknown type %d in cfoll", type(v)); + } +} + +static int first(Node *p) /* collects initially active leaves of p into setvec */ + /* returns 0 if p matches empty string */ +{ + int b, lp; + + switch (type(p)) { + ELEAF + LEAF + lp = info(p); /* look for high-water mark of subscripts */ + while (setcnt >= maxsetvec || lp >= maxsetvec) { /* guessing here! */ + maxsetvec *= 4; + setvec = (int *) realloc(setvec, maxsetvec * sizeof(int)); + tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int)); + if (setvec == 0 || tmpset == 0) + overflo("out of space in first()"); + } + if (type(p) == EMPTYRE) { + setvec[lp] = 0; + return(0); + } + if (setvec[lp] != 1) { + setvec[lp] = 1; + setcnt++; + } + if (type(p) == CCL && (*(char *) right(p)) == '\0') + return(0); /* empty CCL */ + else return(1); + case PLUS: + if (first(left(p)) == 0) return(0); + return(1); + case STAR: + case QUEST: + first(left(p)); + return(0); + case CAT: + if (first(left(p)) == 0 && first(right(p)) == 0) return(0); + return(1); + case OR: + b = first(right(p)); + if (first(left(p)) == 0 || b == 0) return(0); + return(1); + } + FATAL("can't happen: unknown type %d in first", type(p)); /* can't happen */ + return(-1); +} + +void follow(Node *v) /* collects leaves that can follow v into setvec */ +{ + Node *p; + + if (type(v) == FINAL) + return; + p = parent(v); + switch (type(p)) { + case STAR: + case PLUS: + first(v); + follow(p); + return; + + case OR: + case QUEST: + follow(p); + return; + + case CAT: + if (v == left(p)) { /* v is left child of p */ + if (first(right(p)) == 0) { + follow(p); + return; + } + } else /* v is right child */ + follow(p); + return; + } +} + +int member(int c, const char *sarg) /* is c in s? */ +{ + uschar *s = (uschar *) sarg; + + while (*s) + if (c == *s++) + return(1); + return(0); +} + +int smatch(fa *f, const char *p0) /* shortest match ? */ +{ + int s, ns; + uschar *p = (uschar *) p0; + + s = f->reset ? makeinit(f,0) : f->initstat; + if (f->out[s]) + return(1); + do { + /* assert(*p < NCHARS); */ + if ((ns = f->gototab[s][*p]) != 0) + s = ns; + else + s = cgoto(f, s, *p); + if (f->out[s]) + return(1); + } while (*p++ != 0); + return(0); +} + +int pmatch(fa *f, const char *p0) /* longest match, for sub */ +{ + int s, ns; + uschar *p = (uschar *) p0; + uschar *q; + int i, k; + + /* s = f->reset ? makeinit(f,1) : f->initstat; */ + if (f->reset) { + f->initstat = s = makeinit(f,1); + } else { + s = f->initstat; + } + patbeg = (char *) p; + patlen = -1; + do { + q = p; + do { + if (f->out[s]) /* final state */ + patlen = q-p; + /* assert(*q < NCHARS); */ + if ((ns = f->gototab[s][*q]) != 0) + s = ns; + else + s = cgoto(f, s, *q); + if (s == 1) { /* no transition */ + if (patlen >= 0) { + patbeg = (char *) p; + return(1); + } + else + goto nextin; /* no match */ + } + } while (*q++ != 0); + if (f->out[s]) + patlen = q-p-1; /* don't count $ */ + if (patlen >= 0) { + patbeg = (char *) p; + return(1); + } + nextin: + s = 2; + if (f->reset) { + for (i = 2; i <= f->curstat; i++) + xfree(f->posns[i]); + k = *f->posns[0]; + if ((f->posns[2] = (int *) calloc(1, (k+1)*sizeof(int))) == NULL) + overflo("out of space in pmatch"); + for (i = 0; i <= k; i++) + (f->posns[2])[i] = (f->posns[0])[i]; + f->initstat = f->curstat = 2; + f->out[2] = f->out[0]; + for (i = 0; i < NCHARS; i++) + f->gototab[2][i] = 0; + } + } while (*p++ != 0); + return (0); +} + +int nematch(fa *f, const char *p0) /* non-empty match, for sub */ +{ + int s, ns; + uschar *p = (uschar *) p0; + uschar *q; + int i, k; + + /* s = f->reset ? makeinit(f,1) : f->initstat; */ + if (f->reset) { + f->initstat = s = makeinit(f,1); + } else { + s = f->initstat; + } + patlen = -1; + while (*p) { + q = p; + do { + if (f->out[s]) /* final state */ + patlen = q-p; + /* assert(*q < NCHARS); */ + if ((ns = f->gototab[s][*q]) != 0) + s = ns; + else + s = cgoto(f, s, *q); + if (s == 1) { /* no transition */ + if (patlen > 0) { + patbeg = (char *) p; + return(1); + } else + goto nnextin; /* no nonempty match */ + } + } while (*q++ != 0); + if (f->out[s]) + patlen = q-p-1; /* don't count $ */ + if (patlen > 0 ) { + patbeg = (char *) p; + return(1); + } + nnextin: + s = 2; + if (f->reset) { + for (i = 2; i <= f->curstat; i++) + xfree(f->posns[i]); + k = *f->posns[0]; + if ((f->posns[2] = (int *) calloc(1, (k+1)*sizeof(int))) == NULL) + overflo("out of state space"); + for (i = 0; i <= k; i++) + (f->posns[2])[i] = (f->posns[0])[i]; + f->initstat = f->curstat = 2; + f->out[2] = f->out[0]; + for (i = 0; i < NCHARS; i++) + f->gototab[2][i] = 0; + } + p++; + } + return (0); +} + +Node *reparse(const char *p) /* parses regular expression pointed to by p */ +{ /* uses relex() to scan regular expression */ + Node *np; + + dprintf( (thread_stdout, "reparse <%s>\n", p) ); + lastre = prestr = (uschar *) p; /* prestr points to string to be parsed */ + rtok = relex(); + /* GNU compatibility: an empty regexp matches anything */ + if (rtok == '\0') { + /* FATAL("empty regular expression"); previous */ + return(op2(EMPTYRE, NIL, NIL)); + } + np = regexp(); + if (rtok != '\0') + FATAL("syntax error in regular expression %s at %s", lastre, prestr); + return(np); +} + +Node *regexp(void) /* top-level parse of reg expr */ +{ + return (alt(concat(primary()))); +} + +Node *primary(void) +{ + Node *np; + int savelastatom; + + switch (rtok) { + case CHAR: + lastatom = starttok; + np = op2(CHAR, NIL, itonp(rlxval)); + rtok = relex(); + return (unary(np)); + case ALL: + rtok = relex(); + return (unary(op2(ALL, NIL, NIL))); + case EMPTYRE: + if (replogfile) { + fprintf(replogfile, + "returned EMPTYRE from primary\n"); + fflush(replogfile); + } + rtok = relex(); + + return (unary(op2(EMPTYRE, NIL, NIL))); + case DOT: + lastatom = starttok; + rtok = relex(); + return (unary(op2(DOT, NIL, NIL))); + case CCL: + lastatom = starttok; + np = op2(CCL, NIL, (Node*) cclenter((char *) rlxstr)); + rtok = relex(); + return (unary(np)); + case NCCL: + lastatom = starttok; + np = op2(NCCL, NIL, (Node *) cclenter((char *) rlxstr)); + rtok = relex(); + return (unary(np)); + case '^': + rtok = relex(); + return (unary(op2(CHAR, NIL, itonp(HAT)))); + case '$': + rtok = relex(); + return (unary(op2(CHAR, NIL, NIL))); + case '(': + lastatom = starttok; + savelastatom = (char *)starttok-basestr; /* Retain over recursion */ + rtok = relex(); + if (rtok == ')') { /* special pleading for () */ + rtok = relex(); + return unary(op2(CCL, NIL, (Node *) tostring(""))); + } + np = regexp(); + if (rtok == ')') { + lastatom = basestr+savelastatom; /* Restore */ + rtok = relex(); + return (unary(np)); + } + else + FATAL("syntax error in regular expression %s at %s", lastre, prestr); + default: + FATAL("illegal primary in regular expression %s at %s", lastre, prestr); + } + return 0; /*NOTREACHED*/ +} + +Node *concat(Node *np) +{ + switch (rtok) { + case CHAR: case DOT: case ALL: case CCL: case NCCL: case '$': case '(': + return (concat(op2(CAT, np, primary()))); + case EMPTYRE: + if (replogfile) { + fprintf(replogfile, + "returned EMPTYRE to concat\n"); + fflush(replogfile); + } + rtok = relex(); + return (concat(op2(CAT, op2(CCL, NIL, (Node *) tostring("")), + primary()))); + } + return (np); +} + +Node *alt(Node *np) +{ + if (rtok == OR) { + rtok = relex(); + return (alt(op2(OR, np, concat(primary())))); + } + return (np); +} + +Node *unary(Node *np) +{ + switch (rtok) { + case STAR: + rtok = relex(); + return (unary(op2(STAR, np, NIL))); + case PLUS: + rtok = relex(); + return (unary(op2(PLUS, np, NIL))); + case QUEST: + rtok = relex(); + return (unary(op2(QUEST, np, NIL))); + default: + return (np); + } +} + +/* + * Character class definitions conformant to the POSIX locale as + * defined in IEEE P1003.1 draft 7 of June 2001, assuming the source + * and operating character sets are both ASCII (ISO646) or supersets + * thereof. + * + * Note that to avoid overflowing the temporary buffer used in + * relex(), the expanded character class (prior to range expansion) + * must be less than twice the size of their full name. + */ + +/* Because isblank doesn't show up in any of the header files on any + * system i use, it's defined here. if some other locale has a richer + * definition of "blank", define HAS_ISBLANK and provide your own + * version. + * the parentheses here are an attempt to find a path through the maze + * of macro definition and/or function and/or version provided. thanks + * to nelson beebe for the suggestion; let's see if it works everywhere. + */ + +#if defined(__APPLE__) +#define HAS_ISBLANK +#endif +#ifndef HAS_ISBLANK + +int (isblank)(int c) +{ + return c==' ' || c=='\t'; +} + +#endif + +struct charclass { + const char *cc_name; + int cc_namelen; + int (*cc_func)(int); +} charclasses[] = { + { "alnum", 5, isalnum }, + { "alpha", 5, isalpha }, + { "blank", 5, isblank }, + { "cntrl", 5, iscntrl }, + { "digit", 5, isdigit }, + { "graph", 5, isgraph }, + { "lower", 5, islower }, + { "print", 5, isprint }, + { "punct", 5, ispunct }, + { "space", 5, isspace }, + { "upper", 5, isupper }, + { "xdigit", 6, isxdigit }, + { NULL, 0, NULL }, +}; + +#define REPEAT_SIMPLE 0 +#define REPEAT_PLUS_APPENDED 1 +#define REPEAT_WITH_Q 2 +#define REPEAT_ZERO 3 + +int replace_repeat(uschar * reptok, int reptoklen, uschar * atom, int atomlen, + int firstnum, int secondnum, int special_case) +{ + int i, j; + uschar *buf = 0; + int ret = 1; + int init_q = (firstnum==0); /* first added char will be ? */ + int n_q_reps = secondnum-firstnum; /* m>n, so reduce until {1,m-n} left */ + int prefix_length = (char *) reptok-basestr; /* prefix includes first rep */ + int suffix_length = strlen(reptok) - reptoklen; /* string after rep specifier */ + int size = prefix_length + suffix_length; + + if (firstnum > 1) { /* add room for reps 2 through firstnum */ + size += atomlen*(firstnum-1); + } + + /* Adjust size of buffer for special cases */ + if (special_case == REPEAT_PLUS_APPENDED) { + size++; /* for the final + */ + } else if (special_case == REPEAT_WITH_Q) { + size += init_q + (atomlen+1)* n_q_reps; + } else if (special_case == REPEAT_ZERO) { + size += 2; /* just a null ERE: () */ + } + if ((buf = (uschar *) malloc(size+1)) == NULL) + FATAL("out of space in reg expr %.10s..", lastre); + if (replogfile) { + fprintf(replogfile, "re before: len=%d,%s\n" + " : init_q=%d,n_q_reps=%d\n", + strlen(basestr),basestr, + init_q,n_q_reps); + fprintf(replogfile, "re prefix_length=%d,atomlen=%d\n", + prefix_length,atomlen); +/* + fprintf(replogfile, " new buf size: %d, atom=%s, atomlen=%d\n", + size, atom, atomlen); +*/ + fflush(replogfile); + } + memcpy(buf, basestr, prefix_length); /* copy prefix */ + j = prefix_length; + if (special_case == REPEAT_ZERO) { + j -= atomlen; + buf[j++] = '('; + buf[j++] = ')'; + } + for (i=1; i < firstnum; i++) { /* copy x reps */ + memcpy(&buf[j], atom, atomlen); + j += atomlen; + } + if (special_case == REPEAT_PLUS_APPENDED) { + buf[j++] = '+'; + } else if (special_case == REPEAT_WITH_Q) { + if (init_q) buf[j++] = '?'; + for (i=0; i < n_q_reps; i++) { /* copy x? reps */ + memcpy(&buf[j], atom, atomlen); + j += atomlen; + buf[j++] = '?'; + } + } + memcpy(&buf[j], reptok+reptoklen, suffix_length); + if (special_case == REPEAT_ZERO) { + buf[j+suffix_length] = '\0'; + } else { + buf[size] = '\0'; + } + if (replogfile) { + fprintf(replogfile, "re after : len=%d,%s\n",strlen(buf),buf); + fflush(replogfile); + } + /* free old basestr */ + if (firstbasestr != basestr) { + if (basestr) free(basestr); + } + basestr = (char *)buf; + prestr = buf + prefix_length; + if (special_case == REPEAT_ZERO) { + prestr -= atomlen; + ret++; + } + return ret; +} + +int repeat(uschar * reptok, int reptoklen, uschar * atom, int atomlen, + int firstnum, int secondnum) +{ + /* + In general, the repetition specifier or "bound" is replaced here + by an equivalent ERE string, repeating the immediately previous atom + and appending ? and + as needed. Note that the first copy of the + atom is left in place, except in the special_case of a zero-repeat + (i.e., {0}). + */ + int i, j; + if (secondnum < 0) { /* means {n,} -> repeat n-1 times followed by PLUS */ + if (firstnum < 2) { + /* 0 or 1: should be handled before you get here */ + if (replogfile) { + fprintf(replogfile, + "{%d, %d}, shouldn't be here\n", + firstnum, secondnum); + fflush(replogfile); + } + } else { + return replace_repeat(reptok, reptoklen, atom, atomlen, + firstnum, secondnum, REPEAT_PLUS_APPENDED); + } + } else if (firstnum == secondnum) { /* {n} or {n,n} -> simply repeat n-1 times */ + if (firstnum == 0) { /* {0} or {0,0} */ + /* This case is unusual because the resulting + replacement string might actually be SMALLER than + the original ERE */ + return replace_repeat(reptok, reptoklen, atom, atomlen, + firstnum, secondnum, REPEAT_ZERO); + } else { /* (firstnum >= 1) */ + return replace_repeat(reptok, reptoklen, atom, atomlen, + firstnum, secondnum, REPEAT_SIMPLE); + } + } else if (firstnum < secondnum) { /* {n,m} -> repeat n-1 times then alternate */ + /* x{n,m} => xx...x{1, m-n+1} => xx...x?x?x?..x? */ + return replace_repeat(reptok, reptoklen, atom, atomlen, + firstnum, secondnum, REPEAT_WITH_Q); + } else { /* Error - shouldn't be here (n>m) */ + if (replogfile) { + fprintf(replogfile, + "illegal ERE {%d,%d} shouldn't be here!\n", + firstnum,secondnum); + fflush(replogfile); + } + } + return 0; +} + +int relex(void) /* lexical analyzer for reparse */ +{ + int c, n; + int cflag; + static __thread uschar *buf = 0; + static __thread int bufsz = 100; + uschar *bp; + struct charclass *cc; + int i; + int num, m, commafound, digitfound; + uschar *startreptok; + +rescan: + starttok = prestr; + + switch (c = *prestr++) { + case '|': return OR; + case '*': return STAR; + case '+': return PLUS; + case '?': return QUEST; + case '.': return DOT; + case '\0': prestr--; return '\0'; + case '^': + case '$': + case '(': + case ')': + return c; + case '\\': + rlxval = quoted((char **) &prestr); + return CHAR; + default: + rlxval = c; + return CHAR; + case '[': + if (buf == 0 && (buf = (uschar *) malloc(bufsz)) == NULL) + FATAL("out of space in reg expr %.10s..", lastre); + bp = buf; + if (*prestr == '^') { + cflag = 1; + prestr++; + } + else + cflag = 0; + n = 2 * strlen((const char *) prestr)+1; + if (!adjbuf((char **) &buf, &bufsz, n, n, (char **) &bp, "relex1")) + FATAL("out of space for reg expr %.10s...", lastre); + for (; ; ) { + if ((c = *prestr++) == '\\') { + *bp++ = '\\'; + if ((c = *prestr++) == '\0') + FATAL("nonterminated character class %.20s...", lastre); + *bp++ = c; + /* } else if (c == '\n') { */ + /* FATAL("newline in character class %.20s...", lastre); */ + } else if (c == '[' && *prestr == ':') { + /* POSIX char class names, Dag-Erling Smorgrav, des@ofug.org */ + for (cc = charclasses; cc->cc_name; cc++) + if (strncmp((const char *) prestr + 1, (const char *) cc->cc_name, cc->cc_namelen) == 0) + break; + if (cc->cc_name != NULL && prestr[1 + cc->cc_namelen] == ':' && + prestr[2 + cc->cc_namelen] == ']') { + prestr += cc->cc_namelen + 3; + for (i = 0; i < NCHARS; i++) { + if (!adjbuf((char **) &buf, &bufsz, bp-buf+1, 100, (char **) &bp, "relex2")) + FATAL("out of space for reg expr %.10s...", lastre); + if (cc->cc_func(i)) { + *bp++ = i; + n++; + } + } + } else + *bp++ = c; + } else if (Unix2003_compat && c == '[' + && *prestr == '.') { + char collate_char; + prestr++; + collate_char = *prestr++; + if (*prestr == '.' && prestr[1] == ']') { + prestr += 2; + /* Found it: map via locale TBD: for + now, simply return this char. This + is sufficient to pass conformance + test awk.ex 156 + */ + if (*prestr == ']') { + prestr++; + rlxval = collate_char; + if (replogfile) { + fprintf(replogfile, + "[..] collate char=%c\n", + collate_char); + fflush(replogfile); + } + return CHAR; + } + } + } else if (Unix2003_compat && c == '[' + && *prestr == '=') { + char equiv_char; + prestr++; + equiv_char = *prestr++; + if (*prestr == '=' && prestr[1] == ']') { + prestr += 2; + /* Found it: map via locale TBD: for now + simply return this char. This is + sufficient to pass conformance test + awk.ex 156 + */ + if (*prestr == ']') { + prestr++; + rlxval = equiv_char; + if (replogfile) { + fprintf(replogfile, + "[==] equiv char=%c\n", + equiv_char); + fflush(replogfile); + } + return CHAR; + } + } + } else if (c == '\0') { + FATAL("nonterminated character class %.20s", lastre); + } else if (bp == buf) { /* 1st char is special */ + *bp++ = c; + } else if (c == ']') { + *bp++ = 0; + rlxstr = (uschar *) tostring((char *) buf); + if (replogfile) { + fprintf(replogfile, + "detecting []: cflag=%d, len=%d,%s\n", + cflag,strlen(rlxstr),rlxstr); + fflush(replogfile); + } + if (cflag == 0) + return CCL; + else + return NCCL; + } else + *bp++ = c; + } + break; + case '{': + if (Unix2003_compat && isdigit(*(prestr))) { + num = 0; /* Process as a repetition */ + n = -1; m = -1; + commafound = 0; + digitfound = 0; + startreptok = prestr-1; + /* Remember start of previous atom here ? */ + } else { /* just a { char, not a repetition */ + rlxval = c; + return CHAR; + } + for (; ; ) { + if ((c = *prestr++) == '}') { + if (commafound) { + if (digitfound) { /* {n,m} */ + m = num; + if (m 0) { + if ((n==0) && (m==0)) { + return EMPTYRE; + } + /* must rescan input for next token */ + goto rescan; + } + /* Failed to replace: eat up {...} characters + and treat like just PLUS */ + return PLUS; + } else if (c == '\0') { + FATAL("nonterminated character class %.20s", + lastre); + } else if (isdigit(c)) { + num = 10 * num + c - '0'; + digitfound = 1; + } else if (c == ',') { + if (commafound) + FATAL("illegal repetition expression: class %.20s", + lastre); + /* looking for {n,} or {n,m} */ + commafound = 1; + n = num; + digitfound = 0; /* reset */ + num = 0; + } else { + FATAL("illegal repetition expression: class %.20s", + lastre); + } + } + break; + } +} + +int cgoto(fa *f, int s, int c) +{ + int i, j, k; + int *p, *q; + + assert(c == HAT || c < NCHARS); + while (f->accept >= maxsetvec) { /* guessing here! */ + maxsetvec *= 4; + setvec = (int *) realloc(setvec, maxsetvec * sizeof(int)); + tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int)); + if (setvec == 0 || tmpset == 0) + overflo("out of space in cgoto()"); + } + for (i = 0; i <= f->accept; i++) + setvec[i] = 0; + setcnt = 0; + /* compute positions of gototab[s,c] into setvec */ + p = f->posns[s]; + for (i = 1; i <= *p; i++) { + if ((k = f->re[p[i]].ltype) != FINAL) { + if ((k == CHAR && c == ptoi(f->re[p[i]].lval.np)) + || (k == DOT && c != 0 && c != HAT) + || (k == ALL && c != 0) + || (k == EMPTYRE && c != 0) + || (k == CCL && member(c, (char *) f->re[p[i]].lval.up)) + || (k == NCCL && !member(c, (char *) f->re[p[i]].lval.up) && c != 0 && c != HAT)) { + q = f->re[p[i]].lfollow; + for (j = 1; j <= *q; j++) { + if (q[j] >= maxsetvec) { + maxsetvec *= 4; + setvec = (int *) realloc(setvec, maxsetvec * sizeof(int)); + tmpset = (int *) realloc(setvec, maxsetvec * sizeof(int)); + if (setvec == 0 || tmpset == 0) + overflo("cgoto overflow"); + } + if (setvec[q[j]] == 0) { + setcnt++; + setvec[q[j]] = 1; + } + } + } + } + } + /* determine if setvec is a previous state */ + tmpset[0] = setcnt; + j = 1; + for (i = f->accept; i >= 0; i--) + if (setvec[i]) { + tmpset[j++] = i; + } + /* tmpset == previous state? */ + for (i = 1; i <= f->curstat; i++) { + p = f->posns[i]; + if ((k = tmpset[0]) != p[0]) + goto different; + for (j = 1; j <= k; j++) + if (tmpset[j] != p[j]) + goto different; + /* setvec is state i */ + f->gototab[s][c] = i; + return i; + different:; + } + + /* add tmpset to current set of states */ + if (f->curstat >= NSTATES-1) { + f->curstat = 2; + f->reset = 1; + for (i = 2; i < NSTATES; i++) + xfree(f->posns[i]); + } else + ++(f->curstat); + for (i = 0; i < NCHARS; i++) + f->gototab[f->curstat][i] = 0; + xfree(f->posns[f->curstat]); + if ((p = (int *) calloc(1, (setcnt+1)*sizeof(int))) == NULL) + overflo("out of space in cgoto"); + + f->posns[f->curstat] = p; + f->gototab[s][c] = f->curstat; + for (i = 0; i <= setcnt; i++) + p[i] = tmpset[i]; + if (setvec[f->accept]) + f->out[f->curstat] = 1; + else + f->out[f->curstat] = 0; + return f->curstat; +} + + +void freefa(fa *f) /* free a finite automaton */ +{ + int i; + + if (f == NULL) + return; + for (i = 0; i <= f->curstat; i++) + xfree(f->posns[i]); + for (i = 0; i <= f->accept; i++) { + xfree(f->re[i].lfollow); + if (f->re[i].ltype == CCL || f->re[i].ltype == NCCL) + xfree((f->re[i].lval.np)); + } + xfree(f->restr); + xfree(f); +} diff --git a/awk/Sources/awk/lex.c b/awk/Sources/awk/lex.c new file mode 100644 index 00000000..9fe0e650 --- /dev/null +++ b/awk/Sources/awk/lex.c @@ -0,0 +1,586 @@ +/**************************************************************** +Copyright (C) Lucent Technologies 1997 +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name Lucent Technologies or any of +its entities not be used in advertising or publicity pertaining +to distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +****************************************************************/ + +#include +#include +#include +#include +#include "awk.h" +#include "ytab.h" +#include "ios_error.h" + + +extern __thread YYSTYPE yylval; +extern __thread int infunc; + +__thread int lineno = 1; +__thread int bracecnt = 0; +__thread int brackcnt = 0; +__thread int parencnt = 0; + +typedef struct Keyword { + const char *word; + int sub; + int type; +} Keyword; + +Keyword keywords[] ={ /* keep sorted: binary searched */ + { "BEGIN", XBEGIN, XBEGIN }, + { "END", XEND, XEND }, + { "NF", VARNF, VARNF }, + { "atan2", FATAN, BLTIN }, + { "break", BREAK, BREAK }, + { "close", CLOSE, CLOSE }, + { "continue", CONTINUE, CONTINUE }, + { "cos", FCOS, BLTIN }, + { "delete", DELETE, DELETE }, + { "do", DO, DO }, + { "else", ELSE, ELSE }, + { "exit", EXIT, EXIT }, + { "exp", FEXP, BLTIN }, + { "fflush", FFLUSH, BLTIN }, + { "for", FOR, FOR }, + { "func", FUNC, FUNC }, + { "function", FUNC, FUNC }, + { "getline", GETLINE, GETLINE }, + { "gsub", GSUB, GSUB }, + { "if", IF, IF }, + { "in", IN, IN }, + { "index", INDEX, INDEX }, + { "int", FINT, BLTIN }, + { "length", FLENGTH, BLTIN }, + { "log", FLOG, BLTIN }, + { "match", MATCHFCN, MATCHFCN }, + { "next", NEXT, NEXT }, + { "nextfile", NEXTFILE, NEXTFILE }, + { "print", PRINT, PRINT }, + { "printf", PRINTF, PRINTF }, + { "rand", FRAND, BLTIN }, + { "return", RETURN, RETURN }, + { "sin", FSIN, BLTIN }, + { "split", SPLIT, SPLIT }, + { "sprintf", SPRINTF, SPRINTF }, + { "sqrt", FSQRT, BLTIN }, + { "srand", FSRAND, BLTIN }, + { "sub", SUB, SUB }, + { "substr", SUBSTR, SUBSTR }, + { "system", FSYSTEM, BLTIN }, + { "tolower", FTOLOWER, BLTIN }, + { "toupper", FTOUPPER, BLTIN }, + { "while", WHILE, WHILE }, +}; + +#define RET(x) { if(dbg)fprintf(thread_stdout, "lex %s\n", tokname(x)); return(x); } + +int peek(void) +{ + int c = input(); + unput(c); + return c; +} + +int gettok(char **pbuf, int *psz) /* get next input token */ +{ + int c, retc; + char *buf = *pbuf; + int sz = *psz; + char *bp = buf; + + c = input(); + if (c == 0) + return 0; + buf[0] = c; + buf[1] = 0; + if (!isalnum(c) && c != '.' && c != '_') + return c; + + *bp++ = c; + if (isalpha(c) || c == '_') { /* it's a varname */ + for ( ; (c = input()) != 0; ) { + if (bp-buf >= sz) + if (!adjbuf(&buf, &sz, bp-buf+2, 100, &bp, "gettok")) + FATAL( "out of space for name %.10s...", buf ); + if (isalnum(c) || c == '_') + *bp++ = c; + else { + *bp = 0; + unput(c); + break; + } + } + *bp = 0; + retc = 'a'; /* alphanumeric */ + } else { /* maybe it's a number, but could be . */ + char *rem; + /* read input until can't be a number */ + for ( ; (c = input()) != 0; ) { + if (bp-buf >= sz) + if (!adjbuf(&buf, &sz, bp-buf+2, 100, &bp, "gettok")) + FATAL( "out of space for number %.10s...", buf ); + if (isdigit(c) || c == 'e' || c == 'E' + || c == '.' || c == '+' || c == '-') + *bp++ = c; + else { + unput(c); + break; + } + } + *bp = 0; + strtod(buf, &rem); /* parse the number */ + if (rem == buf) { /* it wasn't a valid number at all */ + buf[1] = 0; /* return one character as token */ + retc = buf[0]; /* character is its own type */ + unputstr(rem+1); /* put rest back for later */ + } else { /* some prefix was a number */ + unputstr(rem); /* put rest back for later */ + rem[0] = 0; /* truncate buf after number part */ + retc = '0'; /* type is number */ + } + } + *pbuf = buf; + *psz = sz; + return retc; +} + +int word(char *); +int string(void); +int regexpr(void); +int sc = 0; /* 1 => return a } right now */ +int reg = 0; /* 1 => return a REGEXPR now */ + +int yylex(void) +{ + int c; + static __thread char *buf = 0; + static __thread int bufsize = 5; /* BUG: setting this small causes core dump! */ + + if (buf == 0 && (buf = (char *) malloc(bufsize)) == NULL) + FATAL( "out of space in yylex" ); + if (sc) { + sc = 0; + RET('}'); + } + if (reg) { + reg = 0; + return regexpr(); + } + for (;;) { + c = gettok(&buf, &bufsize); + if (c == 0) + return 0; + if (isalpha(c) || c == '_') + return word(buf); + if (isdigit(c)) { + yylval.cp = setsymtab(buf, tostring(buf), atof(buf), CON|NUM, symtab); + /* should this also have STR set? */ + RET(NUMBER); + } + + yylval.i = c; + switch (c) { + case '\n': /* {EOL} */ + RET(NL); + case '\r': /* assume \n is coming */ + case ' ': /* {WS}+ */ + case '\t': + break; + case '#': /* #.* strip comments */ + while ((c = input()) != '\n' && c != 0) + ; + unput(c); + break; + case ';': + RET(';'); + case '\\': + if (peek() == '\n') { + input(); + } else if (peek() == '\r') { + input(); input(); /* \n */ + lineno++; + } else { + RET(c); + } + break; + case '&': + if (peek() == '&') { + input(); RET(AND); + } else + RET('&'); + case '|': + if (peek() == '|') { + input(); RET(BOR); + } else + RET('|'); + case '!': + if (peek() == '=') { + input(); yylval.i = NE; RET(NE); + } else if (peek() == '~') { + input(); yylval.i = NOTMATCH; RET(MATCHOP); + } else + RET(NOT); + case '~': + yylval.i = MATCH; + RET(MATCHOP); + case '<': + if (peek() == '=') { + input(); yylval.i = LE; RET(LE); + } else { + yylval.i = LT; RET(LT); + } + case '=': + if (peek() == '=') { + input(); yylval.i = EQ; RET(EQ); + } else { + yylval.i = ASSIGN; RET(ASGNOP); + } + case '>': + if (peek() == '=') { + input(); yylval.i = GE; RET(GE); + } else if (peek() == '>') { + input(); yylval.i = APPEND; RET(APPEND); + } else { + yylval.i = GT; RET(GT); + } + case '+': + if (peek() == '+') { + input(); yylval.i = INCR; RET(INCR); + } else if (peek() == '=') { + input(); yylval.i = ADDEQ; RET(ASGNOP); + } else + RET('+'); + case '-': + if (peek() == '-') { + input(); yylval.i = DECR; RET(DECR); + } else if (peek() == '=') { + input(); yylval.i = SUBEQ; RET(ASGNOP); + } else + RET('-'); + case '*': + if (peek() == '=') { /* *= */ + input(); yylval.i = MULTEQ; RET(ASGNOP); + } else if (peek() == '*') { /* ** or **= */ + input(); /* eat 2nd * */ + if (peek() == '=') { + input(); yylval.i = POWEQ; RET(ASGNOP); + } else { + RET(POWER); + } + } else + RET('*'); + case '/': + RET('/'); + case '%': + if (peek() == '=') { + input(); yylval.i = MODEQ; RET(ASGNOP); + } else + RET('%'); + case '^': + if (peek() == '=') { + input(); yylval.i = POWEQ; RET(ASGNOP); + } else + RET(POWER); + + case '$': + /* BUG: awkward, if not wrong */ + c = gettok(&buf, &bufsize); + if (isalpha(c)) { + if (strcmp(buf, "NF") == 0) { /* very special */ + unputstr("(NF)"); + RET(INDIRECT); + } + c = peek(); + if (c == '(' || c == '[' || (infunc && isarg(buf) >= 0)) { + unputstr(buf); + RET(INDIRECT); + } + yylval.cp = setsymtab(buf, "", 0.0, STR|NUM, symtab); + RET(IVAR); + } else if (c == 0) { /* */ + SYNTAX( "unexpected end of input after $" ); + RET(';'); + } else { + unputstr(buf); + RET(INDIRECT); + } + + case '}': + if (--bracecnt < 0) + SYNTAX( "extra }" ); + sc = 1; + RET(';'); + case ']': + if (--brackcnt < 0) + SYNTAX( "extra ]" ); + RET(']'); + case ')': + if (--parencnt < 0) + SYNTAX( "extra )" ); + RET(')'); + case '{': + bracecnt++; + RET('{'); + case '[': + brackcnt++; + RET('['); + case '(': + parencnt++; + RET('('); + + case '"': + return string(); /* BUG: should be like tran.c ? */ + + default: + RET(c); + } + } +} + +int string(void) +{ + int c, n; + char *s, *bp; + static __thread char *buf = 0; + static __thread int bufsz = 500; + + if (buf == 0 && (buf = (char *) malloc(bufsz)) == NULL) + FATAL("out of space for strings"); + for (bp = buf; (c = input()) != '"'; ) { + if (!adjbuf(&buf, &bufsz, bp-buf+2, 500, &bp, "string")) + FATAL("out of space for string %.10s...", buf); + switch (c) { + case '\n': + case '\r': + case 0: + SYNTAX( "non-terminated string %.10s...", buf ); + lineno++; + if (c == 0) /* hopeless */ + FATAL( "giving up" ); + break; + case '\\': + c = input(); + switch (c) { + case '"': *bp++ = '"'; break; + case 'n': *bp++ = '\n'; break; + case 't': *bp++ = '\t'; break; + case 'f': *bp++ = '\f'; break; + case 'r': *bp++ = '\r'; break; + case 'b': *bp++ = '\b'; break; + case 'v': *bp++ = '\v'; break; + case 'a': *bp++ = '\007'; break; + case '\\': *bp++ = '\\'; break; + + case '0': case '1': case '2': /* octal: \d \dd \ddd */ + case '3': case '4': case '5': case '6': case '7': + n = c - '0'; + if ((c = peek()) >= '0' && c < '8') { + n = 8 * n + input() - '0'; + if ((c = peek()) >= '0' && c < '8') + n = 8 * n + input() - '0'; + } + *bp++ = n; + break; + + case 'x': /* hex \x0-9a-fA-F + */ + { char xbuf[100], *px; + for (px = xbuf; (c = input()) != 0 && px-xbuf < 100-2; ) { + if (isdigit(c) + || (c >= 'a' && c <= 'f') + || (c >= 'A' && c <= 'F')) + *px++ = c; + else + break; + } + *px = 0; + unput(c); + sscanf(xbuf, "%x", &n); + *bp++ = n; + break; + } + + default: + *bp++ = c; + break; + } + break; + default: + *bp++ = c; + break; + } + } + *bp = 0; + s = tostring(buf); + *bp++ = ' '; *bp++ = 0; + yylval.cp = setsymtab(buf, s, 0.0, CON|STR|DONTFREE, symtab); + RET(STRING); +} + + +int binsearch(char *w, Keyword *kp, int n) +{ + int cond, low, mid, high; + + low = 0; + high = n - 1; + while (low <= high) { + mid = (low + high) / 2; + if ((cond = strcmp(w, kp[mid].word)) < 0) + high = mid - 1; + else if (cond > 0) + low = mid + 1; + else + return mid; + } + return -1; +} + +int word(char *w) +{ + Keyword *kp = NULL; + int c, n; + + n = binsearch(w, keywords, sizeof(keywords)/sizeof(keywords[0])); +/* BUG: this ought to be inside the if; in theory could fault (daniel barrett) */ + // kp = keywords + n; + // Indeed, it did fail with LLVM + Clang. I moved it inside the if. NH. + if (n != -1) { /* found in table */ + kp = keywords + n; + yylval.i = kp->sub; + switch (kp->type) { /* special handling */ + case BLTIN: + if (kp->sub == FSYSTEM && safe) + SYNTAX( "system is unsafe" ); + RET(kp->type); + case FUNC: + if (infunc) + SYNTAX( "illegal nested function" ); + RET(kp->type); + case RETURN: + if (!infunc) + SYNTAX( "return not in function" ); + RET(kp->type); + case VARNF: + yylval.cp = setsymtab("NF", "", 0.0, NUM, symtab); + RET(VARNF); + default: + RET(kp->type); + } + } + c = peek(); /* look for '(' */ + if (c != '(' && infunc && (n=isarg(w)) >= 0) { + yylval.i = n; + RET(ARG); + } else { + yylval.cp = setsymtab(w, "", 0.0, STR|NUM|DONTFREE, symtab); + if (c == '(') { + RET(CALL); + } else { + RET(VAR); + } + } +} + +void startreg(void) /* next call to yylex will return a regular expression */ +{ + reg = 1; +} + +int regexpr(void) +{ + int c; + static __thread char *buf = 0; + static __thread int bufsz = 500; + char *bp; + + if (buf == 0 && (buf = (char *) malloc(bufsz)) == NULL) + FATAL("out of space for rex expr"); + bp = buf; + for ( ; (c = input()) != '/' && c != 0; ) { + if (!adjbuf(&buf, &bufsz, bp-buf+3, 500, &bp, "regexpr")) + FATAL("out of space for reg expr %.10s...", buf); + if (c == '\n') { + SYNTAX( "newline in regular expression %.10s...", buf ); + unput('\n'); + break; + } else if (c == '\\') { + *bp++ = '\\'; + *bp++ = input(); + } else { + *bp++ = c; + } + } + *bp = 0; + if (c == 0) + SYNTAX("non-terminated regular expression %.10s...", buf); + yylval.s = tostring(buf); + unput('/'); + RET(REGEXPR); +} + +/* low-level lexical stuff, sort of inherited from lex */ + +char ebuf[300]; +char *ep = ebuf; +static char yysbuf[100]; /* pushback buffer */ +static char *yysptr = yysbuf; +__thread FILE *yyin = 0; + +int input(void) /* get next lexical input character */ +{ + int c; + extern __thread char *lexprog; + + if (yysptr > yysbuf) + c = (uschar)*--yysptr; + else if (lexprog != NULL) { /* awk '...' */ + if ((c = (uschar)*lexprog) != 0) + lexprog++; + } else /* awk -f ... */ + c = pgetc(); + if (c == '\n') + lineno++; + else if (c == EOF) + c = 0; + if (ep >= ebuf + sizeof ebuf) + ep = ebuf; + return *ep++ = c; +} + +void unput(int c) /* put lexical character back on input */ +{ + if (c == '\n') + lineno--; + if (yysptr >= yysbuf + sizeof(yysbuf)) + FATAL("pushed back too much: %.20s...", yysbuf); + *yysptr++ = c; + if (--ep < ebuf) + ep = ebuf + sizeof(ebuf) - 1; +} + +void unputstr(const char *s) /* put a string back on input */ +{ + int i; + + for (i = strlen(s)-1; i >= 0; i--) + unput(s[i]); +} diff --git a/awk/Sources/awk/lib.c b/awk/Sources/awk/lib.c new file mode 100644 index 00000000..0e10e404 --- /dev/null +++ b/awk/Sources/awk/lib.c @@ -0,0 +1,705 @@ +/**************************************************************** +Copyright (C) Lucent Technologies 1997 +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name Lucent Technologies or any of +its entities not be used in advertising or publicity pertaining +to distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +****************************************************************/ + +#define DEBUG +#include +#include +#include +#include +#include +#include +#include "awk.h" +#include "ytab.h" +#include "ios_error.h" + +static __thread FILE *infile = NULL; +static __thread char *file = ""; +__thread char *record; +__thread int recsize = RECSIZE; +__thread char *fields; +__thread int fieldssize = RECSIZE; + +__thread Cell **fldtab; /* pointers to Cells */ +__thread char inputFS[100] = " "; + +#define MAXFLD 2 +__thread int nfields = MAXFLD; /* last allocated slot for $i */ + +__thread int donefld; /* 1 = implies rec broken into fields */ +__thread int donerec; /* 1 = record is valid (no flds have changed) */ + +__thread int lastfld = 0; /* last used field */ +__thread int argno = 1; /* current input argument number */ +extern __thread Awkfloat *ARGC; + +static __thread Cell dollar0 = { OCELL, CFLD, NULL, "", 0.0, REC|STR|DONTFREE }; +static __thread Cell dollar1 = { OCELL, CFLD, NULL, "", 0.0, FLD|STR|DONTFREE }; + +void recinit(unsigned int n) +{ + if ( (record = (char*)calloc(n, sizeof(char*))) == NULL + || (fields = (char*)calloc(n+1, sizeof(char *))) == NULL + || (fldtab = (Cell **)calloc(nfields+1, sizeof(Cell*))) == NULL + || (fldtab[0] = (Cell *) calloc(1, sizeof(Cell))) == NULL ) + FATAL("out of space for $0 and fields"); +/* if ( (record = (char *) malloc(n)) == NULL + || (fields = (char *) malloc(n+1)) == NULL + || (fldtab = (Cell **) malloc((nfields+1) * sizeof(Cell *))) == NULL + || (fldtab[0] = (Cell *) malloc(sizeof(Cell))) == NULL ) + FATAL("out of space for $0 and fields"); */ + *fldtab[0] = dollar0; + fldtab[0]->sval = record; + fldtab[0]->nval = tostring("0"); + makefields(1, nfields); +} + +void makefields(int n1, int n2) /* create $n1..$n2 inclusive */ +{ + char temp[50]; + int i; + + for (i = n1; i <= n2; i++) { + fldtab[i] = (Cell *) malloc(sizeof (struct Cell)); + if (fldtab[i] == NULL) + FATAL("out of space in makefields %d", i); + *fldtab[i] = dollar1; + sprintf(temp, "%d", i); + fldtab[i]->nval = tostring(temp); + } +} + +void initgetrec(void) +{ + int i; + char *p; + + for (i = 1; i < *ARGC; i++) { + if (!isclvar(p = getargv(i))) { /* find 1st real filename */ + setsval(lookup("FILENAME", symtab), getargv(i)); + return; + } + setclvar(p); /* a commandline assignment before filename */ + argno++; + } + infile = thread_stdin; /* no filenames, so use stdin */ +} + +__thread int awk_firsttime = 1; + +int getrec(char **pbuf, int *pbufsize, int isrecord) /* get next input record */ +{ /* note: cares whether buf == record */ + int c; + char *buf = *pbuf; + uschar saveb0; + int bufsize = *pbufsize, savebufsize = bufsize; + + if (awk_firsttime) { + awk_firsttime = 0; + initgetrec(); + } + dprintf( (thread_stdout, "RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n", + *RS, *FS, *ARGC, *FILENAME) ); + if (isrecord) { + donefld = 0; + donerec = 1; + } + saveb0 = buf[0]; + buf[0] = 0; + while (argno < *ARGC || infile == thread_stdin) { + dprintf( (thread_stdout, "argno=%d, file=|%s|\n", argno, file) ); + if (infile == NULL) { /* have to open a new file */ + file = getargv(argno); + if (*file == '\0') { /* it's been zapped */ + argno++; + continue; + } + if (isclvar(file)) { /* a var=value arg */ + setclvar(file); + argno++; + continue; + } + *FILENAME = file; + dprintf( (thread_stdout, "opening file %s\n", file) ); + if (*file == '-' && *(file+1) == '\0') + infile = thread_stdin; + else if ((infile = fopen(file, "r")) == NULL) + FATAL("can't open file %s", file); + setfval(fnrloc, 0.0); + } + c = readrec(&buf, &bufsize, infile); + if (c != 0 || buf[0] != '\0') { /* normal record */ + if (isrecord) { + if (freeable(fldtab[0])) + xfree(fldtab[0]->sval); + fldtab[0]->sval = buf; /* buf == record */ + fldtab[0]->tval = REC | STR | DONTFREE; + if (is_number(fldtab[0]->sval)) { + fldtab[0]->fval = atof(fldtab[0]->sval); + fldtab[0]->tval |= NUM; + } + } + setfval(nrloc, nrloc->fval+1); + setfval(fnrloc, fnrloc->fval+1); + if (donefld == 0) + fldbld(); + *pbuf = buf; + *pbufsize = bufsize; + return 1; + } + /* EOF arrived on this file; set up next */ + if (infile != thread_stdin) + fclose(infile); + else getc(infile); // OpenTerm issue: EOF is followed by \n, we need to clear the queue. + infile = NULL; + argno++; + } + buf[0] = saveb0; + *pbuf = buf; + *pbufsize = savebufsize; + return 0; /* true end of file */ +} + +void nextfile(void) +{ + if (infile != NULL && infile != thread_stdin) + fclose(infile); + infile = NULL; + argno++; +} + +int readrec(char **pbuf, int *pbufsize, FILE *inf) /* read one record into buf */ +{ + int sep, c; + char *rr, *buf = *pbuf; + int bufsize = *pbufsize; + + if (strlen(*FS) >= sizeof(inputFS)) + FATAL("field separator %.10s... is too long", *FS); + strcpy(inputFS, *FS); /* for subsequent field splitting */ + if ((sep = **RS) == 0) { + sep = '\n'; + while ((c=getc(inf)) == '\n' && c != EOF) /* skip leading \n's */ + ; + if (c != EOF) + ungetc(c, inf); + } + for (rr = buf; ; ) { + for (; (c=getc(inf)) != sep && c != EOF; ) { + if (rr-buf+1 > bufsize) + if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 1")) + FATAL("input record `%.30s...' too long", buf); + *rr++ = c; + } + if (**RS == sep || c == EOF) + break; + if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */ + break; + if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, "readrec 2")) + FATAL("input record `%.30s...' too long", buf); + *rr++ = '\n'; + *rr++ = c; + } + if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3")) + FATAL("input record `%.30s...' too long", buf); + *rr = 0; + dprintf( (thread_stdout, "readrec saw <%s>, returns %d\n", buf, c == EOF && rr == buf ? 0 : 1) ); + *pbuf = buf; + *pbufsize = bufsize; + return c == EOF && rr == buf ? 0 : 1; +} + +char *getargv(int n) /* get ARGV[n] */ +{ + Cell *x; + char *s, temp[50]; + extern __thread Array *ARGVtab; + + sprintf(temp, "%d", n); + x = setsymtab(temp, "", 0.0, STR, ARGVtab); + s = getsval(x); + dprintf( (thread_stdout, "getargv(%d) returns |%s|\n", n, s) ); + return s; +} + +void setclvar(char *s) /* set var=value from s */ +{ + char *p; + Cell *q; + + for (p=s; *p != '='; p++) + ; + *p++ = 0; + p = qstring(p, '\0'); + q = setsymtab(s, p, 0.0, STR, symtab); + setsval(q, p); + if (is_number(q->sval)) { + q->fval = atof(q->sval); + q->tval |= NUM; + } + dprintf( (thread_stdout, "command line set %s to |%s|\n", s, p) ); +} + + +void fldbld(void) /* create fields from current record */ +{ + /* this relies on having fields[] the same length as $0 */ + /* the fields are all stored in this one array with \0's */ + char *r, *fr, sep; + Cell *p; + int i, j, n; + + if (donefld) + return; + if (!isstr(fldtab[0])) + getsval(fldtab[0]); + r = fldtab[0]->sval; + n = strlen(r); + if (n > fieldssize) { + xfree(fields); + if ((fields = (char *) malloc(n+1)) == NULL) + FATAL("out of space for fields in fldbld %d", n); + fieldssize = n; + } + fr = fields; + i = 0; /* number of fields accumulated here */ + if (strlen(inputFS) > 1) { /* it's a regular expression */ + i = refldbld(r, inputFS); + } else if ((sep = *inputFS) == ' ') { /* default whitespace */ + for (i = 0; ; ) { + while (*r == ' ' || *r == '\t' || *r == '\n') + r++; + if (*r == 0) + break; + i++; + if (i > nfields) + growfldtab(i); + if (freeable(fldtab[i])) + xfree(fldtab[i]->sval); + fldtab[i]->sval = fr; + fldtab[i]->tval = FLD | STR | DONTFREE; + do + *fr++ = *r++; + while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0'); + *fr++ = 0; + } + *fr = 0; + } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */ + for (i = 0; *r != 0; r++) { + char buf[2]; + i++; + if (i > nfields) + growfldtab(i); + if (freeable(fldtab[i])) + xfree(fldtab[i]->sval); + buf[0] = *r; + buf[1] = 0; + fldtab[i]->sval = tostring(buf); + fldtab[i]->tval = FLD | STR; + } + *fr = 0; + } else if (*r != 0) { /* if 0, it's a null field */ + /* subtlecase : if length(FS) == 1 && length(RS > 0) + * \n is NOT a field separator (cf awk book 61,84). + * this variable is tested in the inner while loop. + */ + int rtest = '\n'; /* normal case */ + if (strlen(*RS) > 0) + rtest = '\0'; + for (;;) { + i++; + if (i > nfields) + growfldtab(i); + if (freeable(fldtab[i])) + xfree(fldtab[i]->sval); + fldtab[i]->sval = fr; + fldtab[i]->tval = FLD | STR | DONTFREE; + while (*r != sep && *r != rtest && *r != '\0') /* \n is always a separator */ + *fr++ = *r++; + *fr++ = 0; + if (*r++ == 0) + break; + } + *fr = 0; + } + if (i > nfields) + FATAL("record `%.30s...' has too many fields; can't happen", r); + cleanfld(i+1, lastfld); /* clean out junk from previous record */ + lastfld = i; + donefld = 1; + for (j = 1; j <= lastfld; j++) { + p = fldtab[j]; + if(is_number(p->sval)) { + p->fval = atof(p->sval); + p->tval |= NUM; + } + } + setfval(nfloc, (Awkfloat) lastfld); + if (dbg) { + for (j = 0; j <= lastfld; j++) { + p = fldtab[j]; + fprintf(thread_stdout, "field %d (%s): |%s|\n", j, p->nval, p->sval); + } + } +} + +void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */ +{ /* nvals remain intact */ + Cell *p; + int i; + + for (i = n1; i <= n2; i++) { + p = fldtab[i]; + if (freeable(p)) + xfree(p->sval); + p->sval = ""; + p->tval = FLD | STR | DONTFREE; + } +} + +void newfld(int n) /* add field n after end of existing lastfld */ +{ + if (n > nfields) + growfldtab(n); + cleanfld(lastfld+1, n); + lastfld = n; + setfval(nfloc, (Awkfloat) n); +} + +Cell *fieldadr(int n) /* get nth field */ +{ + if (n < 0) + FATAL("trying to access out of range field %d", n); + if (n > nfields) /* fields after NF are empty */ + growfldtab(n); /* but does not increase NF */ + return(fldtab[n]); +} + +void growfldtab(int n) /* make new fields up to at least $n */ +{ + int nf = 2 * nfields; + size_t s; + + if (n > nf) + nf = n; + s = (nf+1) * (sizeof (struct Cell *)); /* freebsd: how much do we need? */ + if (s / sizeof(struct Cell *) - 1 == nf) /* didn't overflow */ + fldtab = (Cell **) realloc(fldtab, s); + else /* overflow sizeof int */ + xfree(fldtab); /* make it null */ + if (fldtab == NULL) + FATAL("out of space creating %d fields", nf); + makefields(nfields+1, nf); + nfields = nf; +} + +int refldbld(const char *rec, const char *fs) /* build fields from reg expr in FS */ +{ + /* this relies on having fields[] the same length as $0 */ + /* the fields are all stored in this one array with \0's */ + char *fr; + int i, tempstat, n; + fa *pfa; + + n = strlen(rec); + if (n > fieldssize) { + xfree(fields); + if ((fields = (char *) malloc(n+1)) == NULL) + FATAL("out of space for fields in refldbld %d", n); + fieldssize = n; + } + fr = fields; + *fr = '\0'; + if (*rec == '\0') + return 0; + pfa = makedfa(fs, 1); + dprintf( (thread_stdout, "into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) ); + tempstat = pfa->initstat; + for (i = 1; ; i++) { + if (i > nfields) + growfldtab(i); + if (freeable(fldtab[i])) + xfree(fldtab[i]->sval); + fldtab[i]->tval = FLD | STR | DONTFREE; + fldtab[i]->sval = fr; + dprintf( (thread_stdout, "refldbld: i=%d\n", i) ); + if (nematch(pfa, rec)) { + pfa->initstat = 2; /* horrible coupling to b.c */ + dprintf( (thread_stdout, "match %s (%d chars)\n", patbeg, patlen) ); + strncpy(fr, rec, patbeg-rec); + fr += patbeg - rec + 1; + *(fr-1) = '\0'; + rec = patbeg + patlen; + } else { + dprintf( (thread_stdout, "no match %s\n", rec) ); + strcpy(fr, rec); + pfa->initstat = tempstat; + break; + } + } + return i; +} + +void recbld(void) /* create $0 from $1..$NF if necessary */ +{ + int i; + char *r, *p; + + if (donerec == 1) + return; + r = record; + for (i = 1; i <= *NF; i++) { + p = getsval(fldtab[i]); + if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1")) + FATAL("created $0 `%.30s...' too long", record); + while ((*r = *p++) != 0) + r++; + if (i < *NF) { + if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2")) + FATAL("created $0 `%.30s...' too long", record); + for (p = *OFS; (*r = *p++) != 0; ) + r++; + } + } + if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3")) + FATAL("built giant record `%.30s...'", record); + *r = '\0'; + dprintf( (thread_stdout, "in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) ); + + if (freeable(fldtab[0])) + xfree(fldtab[0]->sval); + fldtab[0]->tval = REC | STR | DONTFREE; + fldtab[0]->sval = record; + + dprintf( (thread_stdout, "in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) ); + dprintf( (thread_stdout, "recbld = |%s|\n", record) ); + donerec = 1; +} + +__thread int errorflag = 0; + +void yyerror(const char *s) +{ + SYNTAX("%s", s); +} + +void SYNTAX(const char *fmt, ...) +{ + extern __thread char *cmdname, *curfname; + static __thread int been_here = 0; + va_list varg; + + if (been_here++ > 2) + return; + fprintf(thread_stderr, "%s: ", cmdname); + va_start(varg, fmt); + vfprintf(thread_stderr, fmt, varg); + va_end(varg); + fprintf(thread_stderr, " at source line %d", lineno); + if (curfname != NULL) + fprintf(thread_stderr, " in function %s", curfname); + if (compile_time == 1 && cursource() != NULL) + fprintf(thread_stderr, " source file %s", cursource()); + fprintf(thread_stderr, "\n"); + errorflag = 2; + eprint(); +} + +void fpecatch(int n) +{ + FATAL("floating point exception %d", n); +} + +extern __thread int bracecnt, brackcnt, parencnt; + +void bracecheck(void) +{ + int c; + static __thread int beenhere = 0; + + if (beenhere++) + return; + while ((c = input()) != EOF && c != '\0') + bclass(c); + bcheck2(bracecnt, '{', '}'); + bcheck2(brackcnt, '[', ']'); + bcheck2(parencnt, '(', ')'); +} + +void bcheck2(int n, int c1, int c2) +{ + if (n == 1) + fprintf(thread_stderr, "\tmissing %c\n", c2); + else if (n > 1) + fprintf(thread_stderr, "\t%d missing %c's\n", n, c2); + else if (n == -1) + fprintf(thread_stderr, "\textra %c\n", c2); + else if (n < -1) + fprintf(thread_stderr, "\t%d extra %c's\n", -n, c2); +} + +void FATAL(const char *fmt, ...) +{ + extern __thread char *cmdname; + va_list varg; + + fflush(thread_stdout); + fprintf(thread_stderr, "%s: ", cmdname); + va_start(varg, fmt); + vfprintf(thread_stderr, fmt, varg); + va_end(varg); + error(); + // if (dbg > 1) /* core dump if serious debugging on */ + // abort(); + exit(2); +} + +void WARNING(const char *fmt, ...) +{ + extern __thread char *cmdname; + va_list varg; + + fflush(thread_stdout); + fprintf(thread_stderr, "%s: ", cmdname); + va_start(varg, fmt); + vfprintf(thread_stderr, fmt, varg); + va_end(varg); + error(); +} + +void error() +{ + extern __thread Node *curnode; + + fprintf(thread_stderr, "\n"); + if (compile_time != 2 && NR && *NR > 0) { + fprintf(thread_stderr, " input record number %d", (int) (*FNR)); + if (strcmp(*FILENAME, "-") != 0) + fprintf(thread_stderr, ", file %s", *FILENAME); + fprintf(thread_stderr, "\n"); + } + if (compile_time != 2 && curnode) + fprintf(thread_stderr, " source line number %d", curnode->lineno); + else if (compile_time != 2 && lineno) + fprintf(thread_stderr, " source line number %d", lineno); + if (compile_time == 1 && cursource() != NULL) + fprintf(thread_stderr, " source file %s", cursource()); + fprintf(thread_stderr, "\n"); + eprint(); +} + +void eprint(void) /* try to print context around error */ +{ + char *p, *q; + int c; + static __thread int been_here = 0; + extern char ebuf[], *ep; + + if (compile_time == 2 || compile_time == 0 || been_here++ > 0) + return; + p = ep - 1; + if (p > ebuf && *p == '\n') + p--; + for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--) + ; + while (*p == '\n') + p++; + fprintf(thread_stderr, " context is\n\t"); + for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--) + ; + for ( ; p < q; p++) + if (*p) + putc(*p, thread_stderr); + fprintf(thread_stderr, " >>> "); + for ( ; p < ep; p++) + if (*p) + putc(*p, thread_stderr); + fprintf(thread_stderr, " <<< "); + if (*ep) + while ((c = input()) != '\n' && c != '\0' && c != EOF) { + putc(c, thread_stderr); + bclass(c); + } + putc('\n', thread_stderr); + ep = ebuf; +} + +void bclass(int c) +{ + switch (c) { + case '{': bracecnt++; break; + case '}': bracecnt--; break; + case '[': brackcnt++; break; + case ']': brackcnt--; break; + case '(': parencnt++; break; + case ')': parencnt--; break; + } +} + +double errcheck(double x, const char *s) +{ + + if (errno == EDOM) { + errno = 0; + WARNING("%s argument out of domain", s); + x = 1; + } else if (errno == ERANGE) { + errno = 0; + WARNING("%s result out of range", s); + x = 1; + } + return x; +} + +int isclvar(const char *s) /* is s of form var=something ? */ +{ + const char *os = s; + + if (!isalpha((uschar) *s) && *s != '_') + return 0; + for ( ; *s; s++) + if (!(isalnum((uschar) *s) || *s == '_')) + break; + return *s == '=' && s > os; +} + +/* strtod is supposed to be a proper test of what's a valid number */ +/* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */ +/* wrong: violates 4.10.1.4 of ansi C standard */ + +#include +int is_number(const char *s) +{ + double r; + char *ep; + errno = 0; + r = strtod(s, &ep); + if (ep == s || r == HUGE_VAL || errno == ERANGE) + return 0; + while (*ep == ' ' || *ep == '\t' || *ep == '\n') + ep++; + if (*ep == '\0') + return 1; + else + return 0; +} diff --git a/awk/Sources/awk/parse.c b/awk/Sources/awk/parse.c new file mode 100644 index 00000000..e42b17b6 --- /dev/null +++ b/awk/Sources/awk/parse.c @@ -0,0 +1,279 @@ +/**************************************************************** +Copyright (C) Lucent Technologies 1997 +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name Lucent Technologies or any of +its entities not be used in advertising or publicity pertaining +to distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +****************************************************************/ + +#define DEBUG +#include +#include +#include +#include "awk.h" +#include "ytab.h" +#include "ios_error.h" + +Node *nodealloc(int n) +{ + Node *x; + + x = (Node *) malloc(sizeof(Node) + (n-1)*sizeof(Node *)); + if (x == NULL) + FATAL("out of space in nodealloc"); + memset(x, 0, sizeof(Node) + (n-1)*sizeof(Node *)); + x->nnarg = n; + x->nnext = NULL; + x->lineno = lineno; + return(x); +} + +Node *exptostat(Node *a) +{ + a->ntype = NSTAT; + return(a); +} + +Node *node1(int a, Node *b) +{ + Node *x; + + x = nodealloc(1); + x->nobj = a; + x->narg[0]=b; + return(x); +} + +Node *node2(int a, Node *b, Node *c) +{ + Node *x; + + x = nodealloc(2); + x->nobj = a; + x->narg[0] = b; + x->narg[1] = c; + return(x); +} + +Node *node3(int a, Node *b, Node *c, Node *d) +{ + Node *x; + + x = nodealloc(3); + x->nobj = a; + x->narg[0] = b; + x->narg[1] = c; + x->narg[2] = d; + return(x); +} + +Node *node4(int a, Node *b, Node *c, Node *d, Node *e) +{ + Node *x; + + x = nodealloc(4); + x->nobj = a; + x->narg[0] = b; + x->narg[1] = c; + x->narg[2] = d; + x->narg[3] = e; + return(x); +} + +Node *stat1(int a, Node *b) +{ + Node *x; + + x = node1(a,b); + x->ntype = NSTAT; + return(x); +} + +Node *stat2(int a, Node *b, Node *c) +{ + Node *x; + + x = node2(a,b,c); + x->ntype = NSTAT; + return(x); +} + +Node *stat3(int a, Node *b, Node *c, Node *d) +{ + Node *x; + + x = node3(a,b,c,d); + x->ntype = NSTAT; + return(x); +} + +Node *stat4(int a, Node *b, Node *c, Node *d, Node *e) +{ + Node *x; + + x = node4(a,b,c,d,e); + x->ntype = NSTAT; + return(x); +} + +Node *op1(int a, Node *b) +{ + Node *x; + + x = node1(a,b); + x->ntype = NEXPR; + return(x); +} + +Node *op2(int a, Node *b, Node *c) +{ + Node *x; + + x = node2(a,b,c); + x->ntype = NEXPR; + return(x); +} + +Node *op3(int a, Node *b, Node *c, Node *d) +{ + Node *x; + + x = node3(a,b,c,d); + x->ntype = NEXPR; + return(x); +} + +Node *op4(int a, Node *b, Node *c, Node *d, Node *e) +{ + Node *x; + + x = node4(a,b,c,d,e); + x->ntype = NEXPR; + return(x); +} + +Node *celltonode(Cell *a, int b) +{ + Node *x; + + a->ctype = OCELL; + a->csub = b; + x = node1(0, (Node *) a); + x->ntype = NVALUE; + return(x); +} + +Node *rectonode(void) /* make $0 into a Node */ +{ + extern __thread Cell *literal0; + return op1(INDIRECT, celltonode(literal0, CUNK)); +} + +Node *makearr(Node *p) +{ + Cell *cp; + + if (isvalue(p)) { + cp = (Cell *) (p->narg[0]); + if (isfcn(cp)) + SYNTAX( "%s is a function, not an array", cp->nval ); + else if (!isarr(cp)) { + xfree(cp->sval); + cp->sval = (char *) makesymtab(NSYMTAB); + cp->tval = ARR; + } + } + return p; +} + +#define PA2NUM 50 /* max number of pat,pat patterns allowed */ +__thread int paircnt; /* number of them in use */ +__thread int pairstack[PA2NUM]; /* state of each pat,pat */ + +Node *pa2stat(Node *a, Node *b, Node *c) /* pat, pat {...} */ +{ + Node *x; + + x = node4(PASTAT2, a, b, c, itonp(paircnt)); + if (paircnt++ >= PA2NUM) + SYNTAX( "limited to %d pat,pat statements", PA2NUM ); + x->ntype = NSTAT; + return(x); +} + +Node *linkum(Node *a, Node *b) +{ + Node *c; + + if (errorflag) /* don't link things that are wrong */ + return a; + if (a == NULL) + return(b); + else if (b == NULL) + return(a); + for (c = a; c->nnext != NULL; c = c->nnext) + ; + c->nnext = b; + return(a); +} + +void defn(Cell *v, Node *vl, Node *st) /* turn on FCN bit in definition, */ +{ /* body of function, arglist */ + Node *p; + int n; + + if (isarr(v)) { + SYNTAX( "`%s' is an array name and a function name", v->nval ); + return; + } + if (isarg(v->nval) != -1) { + SYNTAX( "`%s' is both function name and argument name", v->nval ); + return; + } + + v->tval = FCN; + v->sval = (char *) st; + n = 0; /* count arguments */ + for (p = vl; p; p = p->nnext) + n++; + v->fval = n; + dprintf( (thread_stdout, "defining func %s (%d args)\n", v->nval, n) ); +} + +int isarg(const char *s) /* is s in argument list for current function? */ +{ /* return -1 if not, otherwise arg # */ + extern __thread Node *arglist; + Node *p = arglist; + int n; + + for (n = 0; p != 0; p = p->nnext, n++) + if (strcmp(((Cell *)(p->narg[0]))->nval, s) == 0) + return n; + return -1; +} + +int ptoi(void *p) /* convert pointer to integer */ +{ + return (int) (long) p; /* swearing that p fits, of course */ +} + +Node *itonp(int i) /* and vice versa */ +{ + return (Node *) (long) i; +} diff --git a/awk/Sources/awk/proctab.c b/awk/Sources/awk/proctab.c new file mode 100644 index 00000000..d409cc08 --- /dev/null +++ b/awk/Sources/awk/proctab.c @@ -0,0 +1,212 @@ +#include +#include "awk.h" +#include "ytab.h" +#include "ios_error.h" + +static char *printname[95] = { + (char *) "FIRSTTOKEN", /* 258 */ + (char *) "PROGRAM", /* 259 */ + (char *) "PASTAT", /* 260 */ + (char *) "PASTAT2", /* 261 */ + (char *) "XBEGIN", /* 262 */ + (char *) "XEND", /* 263 */ + (char *) "NL", /* 264 */ + (char *) "ARRAY", /* 265 */ + (char *) "MATCH", /* 266 */ + (char *) "NOTMATCH", /* 267 */ + (char *) "MATCHOP", /* 268 */ + (char *) "FINAL", /* 269 */ + (char *) "DOT", /* 270 */ + (char *) "ALL", /* 271 */ + (char *) "CCL", /* 272 */ + (char *) "NCCL", /* 273 */ + (char *) "CHAR", /* 274 */ + (char *) "OR", /* 275 */ + (char *) "STAR", /* 276 */ + (char *) "QUEST", /* 277 */ + (char *) "PLUS", /* 278 */ + (char *) "EMPTYRE", /* 279 */ + (char *) "IGNORE_PRIOR_ATOM", /* 280 */ + (char *) "AND", /* 281 */ + (char *) "BOR", /* 282 */ + (char *) "APPEND", /* 283 */ + (char *) "EQ", /* 284 */ + (char *) "GE", /* 285 */ + (char *) "GT", /* 286 */ + (char *) "LE", /* 287 */ + (char *) "LT", /* 288 */ + (char *) "NE", /* 289 */ + (char *) "IN", /* 290 */ + (char *) "ARG", /* 291 */ + (char *) "BLTIN", /* 292 */ + (char *) "BREAK", /* 293 */ + (char *) "CLOSE", /* 294 */ + (char *) "CONTINUE", /* 295 */ + (char *) "DELETE", /* 296 */ + (char *) "DO", /* 297 */ + (char *) "EXIT", /* 298 */ + (char *) "FOR", /* 299 */ + (char *) "FUNC", /* 300 */ + (char *) "SUB", /* 301 */ + (char *) "GSUB", /* 302 */ + (char *) "IF", /* 303 */ + (char *) "INDEX", /* 304 */ + (char *) "LSUBSTR", /* 305 */ + (char *) "MATCHFCN", /* 306 */ + (char *) "NEXT", /* 307 */ + (char *) "NEXTFILE", /* 308 */ + (char *) "ADD", /* 309 */ + (char *) "MINUS", /* 310 */ + (char *) "MULT", /* 311 */ + (char *) "DIVIDE", /* 312 */ + (char *) "MOD", /* 313 */ + (char *) "ASSIGN", /* 314 */ + (char *) "ASGNOP", /* 315 */ + (char *) "ADDEQ", /* 316 */ + (char *) "SUBEQ", /* 317 */ + (char *) "MULTEQ", /* 318 */ + (char *) "DIVEQ", /* 319 */ + (char *) "MODEQ", /* 320 */ + (char *) "POWEQ", /* 321 */ + (char *) "PRINT", /* 322 */ + (char *) "PRINTF", /* 323 */ + (char *) "SPRINTF", /* 324 */ + (char *) "ELSE", /* 325 */ + (char *) "INTEST", /* 326 */ + (char *) "CONDEXPR", /* 327 */ + (char *) "POSTINCR", /* 328 */ + (char *) "PREINCR", /* 329 */ + (char *) "POSTDECR", /* 330 */ + (char *) "PREDECR", /* 331 */ + (char *) "VAR", /* 332 */ + (char *) "IVAR", /* 333 */ + (char *) "VARNF", /* 334 */ + (char *) "CALL", /* 335 */ + (char *) "NUMBER", /* 336 */ + (char *) "STRING", /* 337 */ + (char *) "REGEXPR", /* 338 */ + (char *) "GETLINE", /* 339 */ + (char *) "SUBSTR", /* 340 */ + (char *) "SPLIT", /* 341 */ + (char *) "RETURN", /* 342 */ + (char *) "WHILE", /* 343 */ + (char *) "CAT", /* 344 */ + (char *) "UPLUS", /* 345 */ + (char *) "UMINUS", /* 346 */ + (char *) "NOT", /* 347 */ + (char *) "POWER", /* 348 */ + (char *) "INCR", /* 349 */ + (char *) "DECR", /* 350 */ + (char *) "INDIRECT", /* 351 */ + (char *) "LASTTOKEN", /* 352 */ +}; + + +Cell *(*proctab[95])(Node **, int) = { + nullproc, /* FIRSTTOKEN */ + program, /* PROGRAM */ + pastat, /* PASTAT */ + dopa2, /* PASTAT2 */ + nullproc, /* XBEGIN */ + nullproc, /* XEND */ + nullproc, /* NL */ + array, /* ARRAY */ + matchop, /* MATCH */ + matchop, /* NOTMATCH */ + nullproc, /* MATCHOP */ + nullproc, /* FINAL */ + nullproc, /* DOT */ + nullproc, /* ALL */ + nullproc, /* CCL */ + nullproc, /* NCCL */ + nullproc, /* CHAR */ + nullproc, /* OR */ + nullproc, /* STAR */ + nullproc, /* QUEST */ + nullproc, /* PLUS */ + nullproc, /* EMPTYRE */ + nullproc, /* IGNORE_PRIOR_ATOM */ + boolop, /* AND */ + boolop, /* BOR */ + nullproc, /* APPEND */ + relop, /* EQ */ + relop, /* GE */ + relop, /* GT */ + relop, /* LE */ + relop, /* LT */ + relop, /* NE */ + instat, /* IN */ + arg, /* ARG */ + bltin, /* BLTIN */ + jump, /* BREAK */ + closefile, /* CLOSE */ + jump, /* CONTINUE */ + awkdelete, /* DELETE */ + dostat, /* DO */ + jump, /* EXIT */ + forstat, /* FOR */ + nullproc, /* FUNC */ + sub, /* SUB */ + gsub, /* GSUB */ + ifstat, /* IF */ + sindex, /* INDEX */ + nullproc, /* LSUBSTR */ + matchop, /* MATCHFCN */ + jump, /* NEXT */ + jump, /* NEXTFILE */ + arith, /* ADD */ + arith, /* MINUS */ + arith, /* MULT */ + arith, /* DIVIDE */ + arith, /* MOD */ + assign, /* ASSIGN */ + nullproc, /* ASGNOP */ + assign, /* ADDEQ */ + assign, /* SUBEQ */ + assign, /* MULTEQ */ + assign, /* DIVEQ */ + assign, /* MODEQ */ + assign, /* POWEQ */ + printstat, /* PRINT */ + awkprintf, /* PRINTF */ + awksprintf, /* SPRINTF */ + nullproc, /* ELSE */ + intest, /* INTEST */ + condexpr, /* CONDEXPR */ + incrdecr, /* POSTINCR */ + incrdecr, /* PREINCR */ + incrdecr, /* POSTDECR */ + incrdecr, /* PREDECR */ + nullproc, /* VAR */ + nullproc, /* IVAR */ + getnf, /* VARNF */ + call, /* CALL */ + nullproc, /* NUMBER */ + nullproc, /* STRING */ + nullproc, /* REGEXPR */ + awk_getline, /* GETLINE */ + substr, /* SUBSTR */ + split, /* SPLIT */ + jump, /* RETURN */ + whilestat, /* WHILE */ + cat, /* CAT */ + arith, /* UPLUS */ + arith, /* UMINUS */ + boolop, /* NOT */ + arith, /* POWER */ + nullproc, /* INCR */ + nullproc, /* DECR */ + indirect, /* INDIRECT */ + nullproc, /* LASTTOKEN */ +}; + +char *tokname(int n) +{ + static __thread char buf[100]; + + if (n < FIRSTTOKEN || n > LASTTOKEN) { + sprintf(buf, "token %d", n); + return buf; + } + return printname[n-FIRSTTOKEN]; +} diff --git a/awk/Sources/awk/proto.h b/awk/Sources/awk/proto.h new file mode 100644 index 00000000..c7705645 --- /dev/null +++ b/awk/Sources/awk/proto.h @@ -0,0 +1,196 @@ +/**************************************************************** +Copyright (C) Lucent Technologies 1997 +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name Lucent Technologies or any of +its entities not be used in advertising or publicity pertaining +to distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +****************************************************************/ + +extern int yywrap(void); +extern void setfname(Cell *); +extern int constnode(Node *); +extern char *strnode(Node *); +extern Node *notnull(Node *); +extern int yyparse(void); + +extern int yylex(void); +extern void startreg(void); +extern int input(void); +extern void unput(int); +extern void unputstr(const char *); +extern int yylook(void); +extern int yyback(int *, int); +extern int yyinput(void); + +extern fa *makedfa(const char *, int); +extern fa *mkdfa(const char *, int); +extern int makeinit(fa *, int); +extern void penter(Node *); +extern void freetr(Node *); +extern int hexstr(char **); +extern int quoted(char **); +extern char *cclenter(const char *); +extern void overflo(const char *); +extern void cfoll(fa *, Node *); +// extern int first(Node *); +extern void follow(Node *); +extern int member(int, const char *); +extern int smatch(fa *, const char *); // was match +extern int pmatch(fa *, const char *); +extern int nematch(fa *, const char *); +extern Node *reparse(const char *); +extern Node *regexp(void); +extern Node *primary(void); +extern Node *concat(Node *); +extern Node *alt(Node *); +extern Node *unary(Node *); +extern int relex(void); +extern int cgoto(fa *, int, int); +extern void freefa(fa *); + +extern int pgetc(void); +extern char *cursource(void); + +extern Node *nodealloc(int); +extern Node *exptostat(Node *); +extern Node *node1(int, Node *); +extern Node *node2(int, Node *, Node *); +extern Node *node3(int, Node *, Node *, Node *); +extern Node *node4(int, Node *, Node *, Node *, Node *); +extern Node *stat3(int, Node *, Node *, Node *); +extern Node *op2(int, Node *, Node *); +extern Node *op1(int, Node *); +extern Node *stat1(int, Node *); +extern Node *op3(int, Node *, Node *, Node *); +extern Node *op4(int, Node *, Node *, Node *, Node *); +extern Node *stat2(int, Node *, Node *); +extern Node *stat4(int, Node *, Node *, Node *, Node *); +extern Node *celltonode(Cell *, int); +extern Node *rectonode(void); +extern Node *makearr(Node *); +extern Node *pa2stat(Node *, Node *, Node *); +extern Node *linkum(Node *, Node *); +extern void defn(Cell *, Node *, Node *); +extern int isarg(const char *); +extern char *tokname(int); +extern Cell *(*proctab[])(Node **, int); +extern int ptoi(void *); +extern Node *itonp(int); + +extern void syminit(void); +extern void arginit(int, char **); +extern void envinit(char **); +extern Array *makesymtab(int); +extern void freesymtab(Cell *); +extern void freeelem(Cell *, const char *); +extern Cell *setsymtab(const char *, const char *, double, unsigned int, Array *); +extern int hash(const char *, int); +extern void rehash(Array *); +extern Cell *lookup(const char *, Array *); +extern double setfval(Cell *, double); +extern void funnyvar(Cell *, const char *); +extern char *setsval(Cell *, const char *); +extern double getfval(Cell *); +extern char *getsval(Cell *); +extern char *getpssval(Cell *); /* for print */ +extern char *tostring(const char *); +extern char *qstring(const char *, int); + +extern void recinit(unsigned int); +extern void initgetrec(void); +extern void makefields(int, int); +extern void growfldtab(int n); +extern int getrec(char **, int *, int); +extern void nextfile(void); +extern int readrec(char **buf, int *bufsize, FILE *inf); +extern char *getargv(int); +extern void setclvar(char *); +extern void fldbld(void); +extern void cleanfld(int, int); +extern void newfld(int); +extern int refldbld(const char *, const char *); +extern void recbld(void); +extern Cell *fieldadr(int); +extern void yyerror(const char *); +extern void fpecatch(int); +extern void bracecheck(void); +extern void bcheck2(int, int, int); +extern void SYNTAX(const char *, ...); +extern void FATAL(const char *, ...); +extern void WARNING(const char *, ...); +extern void error(void); +extern void eprint(void); +extern void bclass(int); +extern double errcheck(double, const char *); +extern int isclvar(const char *); +extern int is_number(const char *); + +extern int adjbuf(char **pb, int *sz, int min, int q, char **pbp, const char *what); +extern void run(Node *); +extern void freeTree(Node *, int eraseSelf); +extern Cell *execute(Node *); +extern Cell *program(Node **, int); +extern Cell *call(Node **, int); +extern Cell *copycell(Cell *); +extern Cell *arg(Node **, int); +extern Cell *jump(Node **, int); +extern Cell *awk_getline(Node **, int); +extern Cell *getnf(Node **, int); +extern Cell *array(Node **, int); +extern Cell *awkdelete(Node **, int); +extern Cell *intest(Node **, int); +extern Cell *matchop(Node **, int); +extern Cell *boolop(Node **, int); +extern Cell *relop(Node **, int); +extern void tfree(Cell *); +extern Cell *gettemp(void); +extern Cell *field(Node **, int); +extern Cell *indirect(Node **, int); +extern Cell *substr(Node **, int); +extern Cell *sindex(Node **, int); +extern int format(char **, int *, const char *, Node *); +extern Cell *awksprintf(Node **, int); +extern Cell *awkprintf(Node **, int); +extern Cell *arith(Node **, int); +extern double ipow(double, int); +extern Cell *incrdecr(Node **, int); +extern Cell *assign(Node **, int); +extern Cell *cat(Node **, int); +extern Cell *pastat(Node **, int); +extern Cell *dopa2(Node **, int); +extern Cell *split(Node **, int); +extern Cell *condexpr(Node **, int); +extern Cell *ifstat(Node **, int); +extern Cell *whilestat(Node **, int); +extern Cell *dostat(Node **, int); +extern Cell *forstat(Node **, int); +extern Cell *instat(Node **, int); +extern Cell *bltin(Node **, int); +extern Cell *printstat(Node **, int); +extern Cell *nullproc(Node **, int); +extern FILE *redirect(int, Node *); +extern FILE *openfile(int, const char *); +// extern const char *filename(FILE *); +extern Cell *closefile(Node **, int); +extern void closeall(void); +extern Cell *sub(Node **, int); +extern Cell *gsub(Node **, int); + +extern FILE *popen(const char *, const char *); +extern int pclose(FILE *); diff --git a/awk/Sources/awk/run.c b/awk/Sources/awk/run.c new file mode 100644 index 00000000..b8570b04 --- /dev/null +++ b/awk/Sources/awk/run.c @@ -0,0 +1,1999 @@ +/**************************************************************** +Copyright (C) Lucent Technologies 1997 +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name Lucent Technologies or any of +its entities not be used in advertising or publicity pertaining +to distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +****************************************************************/ + +#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include "awk.h" +#include "ytab.h" + +#include "ios_error.h" + +#define tempfree(x) if (istemp(x)) tfree(x); /* else */ + +/* +#undef tempfree + +void tempfree(Cell *p) { + if (p->ctype == OCELL && (p->csub < CUNK || p->csub > CFREE)) { + WARNING("bad csub %d in Cell %d %s", + p->csub, p->ctype, p->sval); + } + if (istemp(p)) + tfree(p); +} +*/ + +/* do we really need these? */ +/* #ifdef _NFILE */ +/* #ifndef FOPEN_MAX */ +/* #define FOPEN_MAX _NFILE */ +/* #endif */ +/* #endif */ +/* */ +/* #ifndef FOPEN_MAX */ +/* #define FOPEN_MAX 40 */ /* max number of open files */ +/* #endif */ +/* */ +/* #ifndef RAND_MAX */ +/* #define RAND_MAX 32767 */ /* all that ansi guarantees */ +/* #endif */ + +static __thread jmp_buf env; +extern __thread int pairstack[]; + +__thread Node *winner = NULL; /* root of parse tree */ +Cell *tmps; /* free temporary cells for execution */ + +static Cell truecell ={ OBOOL, BTRUE, 0, 0, 1.0, NUM }; +Cell *True = &truecell; +static Cell falsecell ={ OBOOL, BFALSE, 0, 0, 0.0, NUM }; +Cell *False = &falsecell; +static Cell breakcell ={ OJUMP, JBREAK, 0, 0, 0.0, NUM }; +Cell *jbreak = &breakcell; +static Cell contcell ={ OJUMP, JCONT, 0, 0, 0.0, NUM }; +Cell *jcont = &contcell; +static Cell nextcell ={ OJUMP, JNEXT, 0, 0, 0.0, NUM }; +Cell *jnext = &nextcell; +static Cell nextfilecell ={ OJUMP, JNEXTFILE, 0, 0, 0.0, NUM }; +Cell *jnextfile = &nextfilecell; +static Cell exitcell ={ OJUMP, JEXIT, 0, 0, 0.0, NUM }; +Cell *jexit = &exitcell; +static Cell retcell ={ OJUMP, JRET, 0, 0, 0.0, NUM }; +Cell *jret = &retcell; +static Cell tempcell ={ OCELL, CTEMP, 0, "", 0.0, NUM|STR|DONTFREE }; + +__thread Node *curnode = NULL; /* the node being executed, for debugging */ + +static __thread Awkfloat prev_srand, tmp_srand; + +/* buffer memory management */ +int adjbuf(char **pbuf, int *psiz, int minlen, int quantum, char **pbptr, + const char *whatrtn) +/* pbuf: address of pointer to buffer being managed + * psiz: address of buffer size variable + * minlen: minimum length of buffer needed + * quantum: buffer size quantum + * pbptr: address of movable pointer into buffer, or 0 if none + * whatrtn: name of the calling routine if failure should cause fatal error + * + * return 0 for realloc failure, !=0 for success + */ +{ + if (minlen > *psiz) { + char *tbuf; + int rminlen = quantum ? minlen % quantum : 0; + int boff = pbptr ? *pbptr - *pbuf : 0; + /* round up to next multiple of quantum */ + if (rminlen) + minlen += quantum - rminlen; + tbuf = (char *) realloc(*pbuf, minlen); + dprintf( (thread_stdout, "adjbuf %s: %d %d (pbuf=%p, tbuf=%p)\n", whatrtn, *psiz, minlen, *pbuf, tbuf) ); + if (tbuf == NULL) { + if (whatrtn) + FATAL("out of memory in %s", whatrtn); + return 0; + } + *pbuf = tbuf; + *psiz = minlen; + if (pbptr) + *pbptr = tbuf + boff; + } + return 1; +} + +void run(Node *a) /* execution of parse tree starts here */ +{ + extern void stdinit(void); + + stdinit(); + execute(a); + closeall(); + freeTree(a, 1); + // Reset main variables at exit: + curnode = NULL; + winner = NULL; + // These are defined in ytab.c + extern __thread Node *beginloc; + extern __thread Node *endloc; + beginloc = 0; + endloc = 0; +} + +void freeTree(Node *u, int eraseSelf) /* scan the entire tree, and frees the allocated memory */ +{ + Node *a; + Node *anext; + + if (u == NULL) return; + if ((int)u == 0x1) return; // safeguard + + // If it's a tree, freetr will do the job: + if ((u->nobj == CCL) || (u->nobj == NCCL) || (u->nobj == CHAR) || (u->nobj == DOT) || (u->nobj == FINAL) + || (u->nobj == ALL) || (u->nobj == EMPTYRE) || (u->nobj == STAR) || (u->nobj == PLUS) || (u->nobj == QUEST) + || (u->nobj == CAT) || (u->nobj == OR)) { + freetr(u); + return; + } + for (a = u; a; a = anext) { + if ((a->ntype == NSTAT) || (a->ntype == NEXPR)) { + for (int i = 0; i < a->nnarg; i++) { + freeTree(a->narg[i], 0); // never free narg, it was allocated as part of the node + a->narg[i] = NULL; + } + } + // Erase all values, all pointers. + anext = a->nnext; + if ((int)anext % 8 != 0) anext = NULL; // Enforce 8-bit alignment + a->ntype = 0; + a->nnext = NULL; + a->lineno = 0; + a->nobj = 0; + a->nnarg = 0; + a->narg[0] = NULL; + if (eraseSelf) { + free(a); + } + eraseSelf = 1; // but free node->next + a = NULL; + } +} + + +Cell *execute(Node *u) /* execute a node of the parse tree */ +{ + Cell *(*proc)(Node **, int); + Cell *x; + Node *a; + + if (u == NULL) + return(True); + for (a = u; ; a = a->nnext) { + curnode = a; + if (isvalue(a)) { + x = (Cell *) (a->narg[0]); + if (isfld(x) && !donefld) + fldbld(); + else if (isrec(x) && !donerec) + recbld(); + return(x); + } + if (notlegal(a->nobj)) /* probably a Cell* but too risky to print */ + FATAL("illegal statement"); + proc = proctab[a->nobj-FIRSTTOKEN]; + x = (*proc)(a->narg, a->nobj); + if (isfld(x) && !donefld) + fldbld(); + else if (isrec(x) && !donerec) + recbld(); + if (isexpr(a)) + return(x); + if (isjump(x)) + return(x); + if (a->nnext == NULL) + return(x); + tempfree(x); + } +} + + +Cell *program(Node **a, int n) /* execute an awk program */ +{ /* a[0] = BEGIN, a[1] = body, a[2] = END */ + Cell *x; + + if (setjmp(env) != 0) + goto ex; + if (a[0]) { /* BEGIN */ + x = execute(a[0]); + if (isexit(x)) + return(True); + if (isjump(x)) + FATAL("illegal break, continue, next or nextfile from BEGIN"); + tempfree(x); + } + if (a[1] || a[2]) + while (getrec(&record, &recsize, 1) > 0) { + x = execute(a[1]); + if (isexit(x)) + break; + tempfree(x); + } + ex: + if (setjmp(env) != 0) /* handles exit within END */ + goto ex1; + if (a[2]) { /* END */ + donefld = 1; /* avoid updating NF */ + x = execute(a[2]); + if (isbreak(x) || isnext(x) || iscont(x)) + FATAL("illegal break, continue, next or nextfile from END"); + tempfree(x); + } + ex1: + return(True); +} + +struct Frame { /* stack frame for awk function calls */ + int nargs; /* number of arguments in this call */ + Cell *fcncell; /* pointer to Cell for function */ + Cell **args; /* pointer to array of arguments after execute */ + Cell *retval; /* return value */ +}; + +#define NARGS 50 /* max args in a call */ + +struct Frame *frame = NULL; /* base of stack frames; dynamically allocated */ +int nframe = 0; /* number of frames allocated */ +struct Frame *fp = NULL; /* frame pointer. bottom level unused */ + +Cell *call(Node **a, int n) /* function call. very kludgy and fragile */ +{ + static __thread Cell newcopycell = { OCELL, CCOPY, 0, "", 0.0, NUM|STR|DONTFREE }; + int i, ncall, ndef; + int freed = 0; /* handles potential double freeing when fcn & param share a tempcell */ + Node *x; + Cell *args[NARGS], *oargs[NARGS]; /* BUG: fixed size arrays */ + Cell *y, *z, *fcn; + char *s; + + fcn = execute(a[0]); /* the function itself */ + s = fcn->nval; + if (!isfcn(fcn)) + FATAL("calling undefined function %s", s); + if (frame == NULL) { + fp = frame = (struct Frame *) calloc(nframe += 100, sizeof(struct Frame)); + if (frame == NULL) + FATAL("out of space for stack frames calling %s", s); + } + for (ncall = 0, x = a[1]; x != NULL; x = x->nnext) /* args in call */ + ncall++; + ndef = (int) fcn->fval; /* args in defn */ + dprintf( (thread_stdout, "calling %s, %d args (%d in defn), fp=%d\n", s, ncall, ndef, (int) (fp-frame)) ); + if (ncall > ndef) + WARNING("function %s called with %d args, uses only %d", + s, ncall, ndef); + if (ncall + ndef > NARGS) + FATAL("function %s has %d arguments, limit %d", s, ncall+ndef, NARGS); + for (i = 0, x = a[1]; x != NULL; i++, x = x->nnext) { /* get call args */ + dprintf( (thread_stdout, "evaluate args[%d], fp=%d:\n", i, (int) (fp-frame)) ); + y = execute(x); + oargs[i] = y; + dprintf( (thread_stdout, "args[%d]: %s %f <%s>, t=%o\n", + i, NN(y->nval), y->fval, isarr(y) ? "(array)" : NN(y->sval), y->tval) ); + if (isfcn(y)) + FATAL("can't use function %s as argument in %s", y->nval, s); + if (isarr(y)) + args[i] = y; /* arrays by ref */ + else + args[i] = copycell(y); + tempfree(y); + } + for ( ; i < ndef; i++) { /* add null args for ones not provided */ + args[i] = gettemp(); + *args[i] = newcopycell; + } + fp++; /* now ok to up frame */ + if (fp >= frame + nframe) { + int dfp = fp - frame; /* old index */ + frame = (struct Frame *) + realloc((char *) frame, (nframe += 100) * sizeof(struct Frame)); + if (frame == NULL) + FATAL("out of space for stack frames in %s", s); + fp = frame + dfp; + } + fp->fcncell = fcn; + fp->args = args; + fp->nargs = ndef; /* number defined with (excess are locals) */ + fp->retval = gettemp(); + + dprintf( (thread_stdout, "start exec of %s, fp=%d\n", s, (int) (fp-frame)) ); + y = execute((Node *)(fcn->sval)); /* execute body */ + dprintf( (thread_stdout, "finished exec of %s, fp=%d\n", s, (int) (fp-frame)) ); + + for (i = 0; i < ndef; i++) { + Cell *t = fp->args[i]; + if (isarr(t)) { + if (t->csub == CCOPY) { + if (i >= ncall) { + freesymtab(t); + t->csub = CTEMP; + tempfree(t); + } else { + oargs[i]->tval = t->tval; + oargs[i]->tval &= ~(STR|NUM|DONTFREE); + oargs[i]->sval = t->sval; + tempfree(t); + } + } + } else if (t != y) { /* kludge to prevent freeing twice */ + t->csub = CTEMP; + tempfree(t); + } else if (t == y && t->csub == CCOPY) { + t->csub = CTEMP; + tempfree(t); + freed = 1; + } + } + tempfree(fcn); + if (isexit(y) || isnext(y)) + return y; + if (freed == 0) { + tempfree(y); /* don't free twice! */ + } + z = fp->retval; /* return value */ + dprintf( (thread_stdout, "%s returns %g |%s| %o\n", s, getfval(z), getsval(z), z->tval) ); + fp--; + return(z); +} + +Cell *copycell(Cell *x) /* make a copy of a cell in a temp */ +{ + Cell *y; + + y = gettemp(); + y->csub = CCOPY; /* prevents freeing until call is over */ + y->nval = x->nval; /* BUG? */ + if (isstr(x)) + y->sval = tostring(x->sval); + y->fval = x->fval; + y->tval = x->tval & ~(CON|FLD|REC|DONTFREE); /* copy is not constant or field */ + /* is DONTFREE right? */ + return y; +} + +Cell *arg(Node **a, int n) /* nth argument of a function */ +{ + + n = ptoi(a[0]); /* argument number, counting from 0 */ + dprintf( (thread_stdout, "arg(%d), fp->nargs=%d\n", n, fp->nargs) ); + if (n+1 > fp->nargs) + FATAL("argument #%d of function %s was not supplied", + n+1, fp->fcncell->nval); + return fp->args[n]; +} + +Cell *jump(Node **a, int n) /* break, continue, next, nextfile, return */ +{ + Cell *y; + + switch (n) { + case EXIT: + if (a[0] != NULL) { + y = execute(a[0]); + errorflag = (int) getfval(y); + tempfree(y); + } + longjmp(env, 1); + case RETURN: + if (a[0] != NULL) { + y = execute(a[0]); + if ((y->tval & (STR|NUM)) == (STR|NUM)) { + setsval(fp->retval, getsval(y)); + fp->retval->fval = getfval(y); + fp->retval->tval |= NUM; + } + else if (y->tval & STR) + setsval(fp->retval, getsval(y)); + else if (y->tval & NUM) + setfval(fp->retval, getfval(y)); + else /* can't happen */ + FATAL("bad type variable %d", y->tval); + tempfree(y); + } + return(jret); + case NEXT: + return(jnext); + case NEXTFILE: + nextfile(); + return(jnextfile); + case BREAK: + return(jbreak); + case CONTINUE: + return(jcont); + default: /* can't happen */ + FATAL("illegal jump type %d", n); + } + return 0; /* not reached */ +} + +Cell *awk_getline(Node **a, int n) /* get next line from specific input */ +{ /* a[0] is variable, a[1] is operator, a[2] is filename */ + Cell *r, *x; + extern __thread Cell **fldtab; + FILE *fp; + char *buf; + int bufsize = recsize; + int mode; + + if ((buf = (char *) malloc(bufsize)) == NULL) + FATAL("out of memory in getline"); + + fflush(thread_stdout); /* in case someone is waiting for a prompt */ + r = gettemp(); + if (a[1] != NULL) { /* getline < file */ + x = execute(a[2]); /* filename */ + mode = ptoi(a[1]); + if (mode == '|') /* input pipe */ + mode = LE; /* arbitrary flag */ + fp = openfile(mode, getsval(x)); + tempfree(x); + if (fp == NULL) + n = -1; + else + n = readrec(&buf, &bufsize, fp); + if (n <= 0) { + ; + } else if (a[0] != NULL) { /* getline var sval)) { + fldtab[0]->fval = atof(fldtab[0]->sval); + fldtab[0]->tval |= NUM; + } + } + } else { /* bare getline; use current input */ + if (a[0] == NULL) /* getline */ + n = getrec(&record, &recsize, 1); + else { /* getline var */ + n = getrec(&buf, &bufsize, 0); + x = execute(a[0]); + setsval(x, buf); + tempfree(x); + } + } + setfval(r, (Awkfloat) n); + free(buf); + return r; +} + +Cell *getnf(Node **a, int n) /* get NF */ +{ + if (donefld == 0) + fldbld(); + return (Cell *) a[0]; +} + +Cell *array(Node **a, int n) /* a[0] is symtab, a[1] is list of subscripts */ +{ + Cell *x, *y, *z; + char *s; + Node *np; + char *buf; + int bufsz = recsize; + int nsub = strlen(*SUBSEP); + + if ((buf = (char *) malloc(bufsz)) == NULL) + FATAL("out of memory in array"); + + x = execute(a[0]); /* Cell* for symbol table */ + buf[0] = 0; + for (np = a[1]; np; np = np->nnext) { + y = execute(np); /* subscript */ + s = getsval(y); + if (!adjbuf(&buf, &bufsz, strlen(buf)+strlen(s)+nsub+1, recsize, 0, "array")) + FATAL("out of memory for %s[%s...]", x->nval, buf); + strcat(buf, s); + if (np->nnext) + strcat(buf, *SUBSEP); + tempfree(y); + } + if (!isarr(x)) { + dprintf( (thread_stdout, "making %s into an array\n", NN(x->nval)) ); + if (freeable(x)) + xfree(x->sval); + x->tval &= ~(STR|NUM|DONTFREE); + x->tval |= ARR; + x->sval = (char *) makesymtab(NSYMTAB); + } + z = setsymtab(buf, "", 0.0, STR|NUM, (Array *) x->sval); + z->ctype = OCELL; + z->csub = CVAR; + tempfree(x); + free(buf); + return(z); +} + +Cell *awkdelete(Node **a, int n) /* a[0] is symtab, a[1] is list of subscripts */ +{ + Cell *x, *y; + Node *np; + char *s; + int nsub = strlen(*SUBSEP); + + x = execute(a[0]); /* Cell* for symbol table */ + if (!isarr(x)) + return True; + if (a[1] == 0) { /* delete the elements, not the table */ + freesymtab(x); + x->tval &= ~STR; + x->tval |= ARR; + x->sval = (char *) makesymtab(NSYMTAB); + } else { + int bufsz = recsize; + char *buf; + if ((buf = (char *) malloc(bufsz)) == NULL) + FATAL("out of memory in adelete"); + buf[0] = 0; + for (np = a[1]; np; np = np->nnext) { + y = execute(np); /* subscript */ + s = getsval(y); + if (!adjbuf(&buf, &bufsz, strlen(buf)+strlen(s)+nsub+1, recsize, 0, "awkdelete")) + FATAL("out of memory deleting %s[%s...]", x->nval, buf); + strcat(buf, s); + if (np->nnext) + strcat(buf, *SUBSEP); + tempfree(y); + } + freeelem(x, buf); + free(buf); + } + tempfree(x); + return True; +} + +Cell *intest(Node **a, int n) /* a[0] is index (list), a[1] is symtab */ +{ + Cell *x, *ap, *k; + Node *p; + char *buf; + char *s; + int bufsz = recsize; + int nsub = strlen(*SUBSEP); + + ap = execute(a[1]); /* array name */ + if (!isarr(ap)) { + dprintf( (thread_stdout, "making %s into an array\n", ap->nval) ); + if (freeable(ap)) + xfree(ap->sval); + ap->tval &= ~(STR|NUM|DONTFREE); + ap->tval |= ARR; + ap->sval = (char *) makesymtab(NSYMTAB); + } + if ((buf = (char *) malloc(bufsz)) == NULL) { + FATAL("out of memory in intest"); + } + buf[0] = 0; + for (p = a[0]; p; p = p->nnext) { + x = execute(p); /* expr */ + s = getsval(x); + if (!adjbuf(&buf, &bufsz, strlen(buf)+strlen(s)+nsub+1, recsize, 0, "intest")) + FATAL("out of memory deleting %s[%s...]", x->nval, buf); + strcat(buf, s); + tempfree(x); + if (p->nnext) + strcat(buf, *SUBSEP); + } + k = lookup(buf, (Array *) ap->sval); + tempfree(ap); + free(buf); + if (k == NULL) + return(False); + else + return(True); +} + + +Cell *matchop(Node **a, int n) /* ~ and match() */ +{ + Cell *x, *y; + char *s, *t; + int i; + fa *pfa; + int (*mf)(fa *, const char *) = smatch, mode = 0; + + if (n == MATCHFCN) { + mf = pmatch; + mode = 1; + } + x = execute(a[1]); /* a[1] = target text */ + s = getsval(x); + if (a[0] == 0) /* a[1] == 0: already-compiled reg expr */ + i = (*mf)((fa *) a[2], s); + else { + y = execute(a[2]); /* a[2] = regular expr */ + t = getsval(y); + pfa = makedfa(t, mode); + i = (*mf)(pfa, s); + tempfree(y); + } + tempfree(x); + if (n == MATCHFCN) { + int start = patbeg - s + 1; + if (patlen < 0) + start = 0; + setfval(rstartloc, (Awkfloat) start); + setfval(rlengthloc, (Awkfloat) patlen); + x = gettemp(); + x->tval = NUM; + x->fval = start; + return x; + } else if ((n == MATCH && i == 1) || (n == NOTMATCH && i == 0)) + return(True); + else + return(False); +} + + +Cell *boolop(Node **a, int n) /* a[0] || a[1], a[0] && a[1], !a[0] */ +{ + Cell *x, *y; + int i; + + x = execute(a[0]); + i = istrue(x); + tempfree(x); + switch (n) { + case BOR: + if (i) return(True); + y = execute(a[1]); + i = istrue(y); + tempfree(y); + if (i) return(True); + else return(False); + case AND: + if ( !i ) return(False); + y = execute(a[1]); + i = istrue(y); + tempfree(y); + if (i) return(True); + else return(False); + case NOT: + if (i) return(False); + else return(True); + default: /* can't happen */ + FATAL("unknown boolean operator %d", n); + } + return 0; /*NOTREACHED*/ +} + +Cell *relop(Node **a, int n) /* a[0 < a[1], etc. */ +{ + int i; + Cell *x, *y; + Awkfloat j; + + x = execute(a[0]); + y = execute(a[1]); + if (x->tval&NUM && y->tval&NUM) { + j = x->fval - y->fval; + i = j<0? -1: (j>0? 1: 0); + } else { + i = strcmp(getsval(x), getsval(y)); + } + tempfree(x); + tempfree(y); + switch (n) { + case LT: if (i<0) return(True); + else return(False); + case LE: if (i<=0) return(True); + else return(False); + case NE: if (i!=0) return(True); + else return(False); + case EQ: if (i == 0) return(True); + else return(False); + case GE: if (i>=0) return(True); + else return(False); + case GT: if (i>0) return(True); + else return(False); + default: /* can't happen */ + FATAL("unknown relational operator %d", n); + } + return 0; /*NOTREACHED*/ +} + +void tfree(Cell *a) /* free a tempcell */ +{ + if (freeable(a)) { + dprintf( (thread_stdout, "freeing %s %s %o\n", NN(a->nval), NN(a->sval), a->tval) ); + xfree(a->sval); + } + if (a == tmps) + FATAL("tempcell list is curdled"); + a->cnext = tmps; + tmps = a; +} + +Cell *gettemp(void) /* get a tempcell */ +{ int i; + Cell *x; + + if (!tmps) { + tmps = (Cell *) calloc(100, sizeof(Cell)); + if (!tmps) + FATAL("out of space for temporaries"); + for(i = 1; i < 100; i++) + tmps[i-1].cnext = &tmps[i]; + tmps[i-1].cnext = 0; + } + x = tmps; + tmps = x->cnext; + *x = tempcell; + return(x); +} + +Cell *indirect(Node **a, int n) /* $( a[0] ) */ +{ + Awkfloat val; + Cell *x; + int m; + char *s; + + x = execute(a[0]); + val = getfval(x); /* freebsd: defend against super large field numbers */ + if ((Awkfloat)INT_MAX < val) + FATAL("trying to access out of range field %s", x->nval); + m = (int) val; + if (m == 0 && !is_number(s = getsval(x))) /* suspicion! */ + FATAL("illegal field $(%s), name \"%s\"", s, x->nval); + /* BUG: can x->nval ever be null??? */ + tempfree(x); + x = fieldadr(m); + x->ctype = OCELL; /* BUG? why are these needed? */ + x->csub = CFLD; + return(x); +} + +Cell *substr(Node **a, int nnn) /* substr(a[0], a[1], a[2]) */ +{ + int k, m, n; + char *s; + int temp; + Cell *x, *y, *z = 0; + + x = execute(a[0]); + y = execute(a[1]); + if (a[2] != 0) + z = execute(a[2]); + s = getsval(x); + k = strlen(s) + 1; + if (k <= 1) { + tempfree(x); + tempfree(y); + if (a[2] != 0) { + tempfree(z); + } + x = gettemp(); + setsval(x, ""); + return(x); + } + m = (int) getfval(y); + if (m <= 0) + m = 1; + else if (m > k) + m = k; + tempfree(y); + if (a[2] != 0) { + n = (int) getfval(z); + tempfree(z); + } else + n = k - 1; + if (n < 0) + n = 0; + else if (n > k - m) + n = k - m; + dprintf( (thread_stdout, "substr: m=%d, n=%d, s=%s\n", m, n, s) ); + y = gettemp(); + temp = s[n+m-1]; /* with thanks to John Linderman */ + s[n+m-1] = '\0'; + setsval(y, s + m - 1); + s[n+m-1] = temp; + tempfree(x); + return(y); +} + +Cell *sindex(Node **a, int nnn) /* index(a[0], a[1]) */ +{ + Cell *x, *y, *z; + char *s1, *s2, *p1, *p2, *q; + Awkfloat v = 0.0; + + x = execute(a[0]); + s1 = getsval(x); + y = execute(a[1]); + s2 = getsval(y); + + z = gettemp(); + for (p1 = s1; *p1 != '\0'; p1++) { + for (q=p1, p2=s2; *p2 != '\0' && *q == *p2; q++, p2++) + ; + if (*p2 == '\0') { + v = (Awkfloat) (p1 - s1 + 1); /* origin 1 */ + break; + } + } + tempfree(x); + tempfree(y); + setfval(z, v); + return(z); +} + +#define MAXNUMSIZE 50 + +int format(char **pbuf, int *pbufsize, const char *s, Node *a) /* printf-like conversions */ +{ + char *fmt; + char *p, *t; + const char *os; + Cell *x; + int flag = 0, n; + int fmtwd; /* format width */ + int fmtsz = recsize; + char *buf = *pbuf; + int bufsize = *pbufsize; + + os = s; + p = buf; + if ((fmt = (char *) malloc(fmtsz)) == NULL) + FATAL("out of memory in format()"); + while (*s) { + adjbuf(&buf, &bufsize, MAXNUMSIZE+1+p-buf, recsize, &p, "format1"); + if (*s != '%') { + *p++ = *s++; + continue; + } + if (*(s+1) == '%') { + *p++ = '%'; + s += 2; + continue; + } + /* have to be real careful in case this is a huge number, eg, %100000d */ + fmtwd = atoi(s+1); + if (fmtwd < 0) + fmtwd = -fmtwd; + adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format2"); + for (t = fmt; (*t++ = *s) != '\0'; s++) { + if (!adjbuf(&fmt, &fmtsz, MAXNUMSIZE+1+t-fmt, recsize, &t, "format3")) + FATAL("format item %.30s... ran format() out of memory", os); + if (isalpha((uschar)*s) && *s != 'l' && *s != 'h' && *s != 'L') + break; /* the ansi panoply */ + if (*s == '*') { + x = execute(a); + a = a->nnext; + sprintf(t-1, "%d", fmtwd=(int) getfval(x)); + if (fmtwd < 0) + fmtwd = -fmtwd; + adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format"); + t = fmt + strlen(fmt); + tempfree(x); + } + } + *t = '\0'; + if (fmtwd < 0) + fmtwd = -fmtwd; + adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format4"); + + switch (*s) { + case 'f': case 'e': case 'g': case 'E': case 'G': + flag = 'f'; + break; + case 'd': case 'i': + flag = 'd'; + if(*(s-1) == 'l') break; + *(t-1) = 'l'; + *t = 'd'; + *++t = '\0'; + break; + case 'o': case 'x': case 'X': case 'u': + flag = *(s-1) == 'l' ? 'd' : 'u'; + break; + case 's': + flag = 's'; + break; + case 'c': + flag = 'c'; + break; + default: + WARNING("weird printf conversion %s", fmt); + flag = '?'; + break; + } + if (a == NULL) + FATAL("not enough args in printf(%s)", os); + x = execute(a); + a = a->nnext; + n = MAXNUMSIZE; + if (fmtwd > n) + n = fmtwd; + adjbuf(&buf, &bufsize, 1+n+p-buf, recsize, &p, "format5"); + switch (flag) { + case '?': sprintf(p, "%s", fmt); /* unknown, so dump it too */ + t = getsval(x); + n = strlen(t); + if (fmtwd > n) + n = fmtwd; + adjbuf(&buf, &bufsize, 1+strlen(p)+n+p-buf, recsize, &p, "format6"); + p += strlen(p); + sprintf(p, "%s", t); + break; + case 'f': sprintf(p, fmt, getfval(x)); break; + case 'd': sprintf(p, fmt, (long) getfval(x)); break; + case 'u': sprintf(p, fmt, (unsigned int) getfval(x)); break; + case 's': + t = getsval(x); + n = strlen(t); + if (fmtwd > n) + n = fmtwd; + if (!adjbuf(&buf, &bufsize, 1+n+p-buf, recsize, &p, "format7")) + FATAL("huge string/format (%d chars) in printf %.30s... ran format() out of memory", n, t); + sprintf(p, fmt, t); + break; + case 'c': + if (isnum(x)) { + if (getfval(x)) + sprintf(p, fmt, (int) getfval(x)); + else { + *p++ = '\0'; /* explicit null byte */ + *p = '\0'; /* next output will start here */ + } + } else + sprintf(p, fmt, getsval(x)[0]); + break; + default: + FATAL("can't happen: bad conversion %c in format()", flag); + } + tempfree(x); + p += strlen(p); + s++; + } + *p = '\0'; + free(fmt); + for ( ; a; a = a->nnext) /* evaluate any remaining args */ + execute(a); + *pbuf = buf; + *pbufsize = bufsize; + return p - buf; +} + +// Moved function so it is static: +struct files { + FILE *fp; + const char *fname; + int mode; /* '|', 'a', 'w' => LE/LT, GT */ +} files[FOPEN_MAX] ={ + { NULL, "/dev/stdin", LT }, /* watch out: don't free this! */ + { NULL, "/dev/stdout", GT }, + { NULL, "/dev/stderr", GT } +}; + +void stdinit(void) /* in case stdin, etc., are not constants */ +{ + files[0].fp = thread_stdin; + files[1].fp = thread_stdout; + files[2].fp = thread_stderr; +} + +static const char *filename(FILE *fp) +{ + int i; + + for (i = 0; i < FOPEN_MAX; i++) + if (fp == files[i].fp) + return files[i].fname; + return "???"; +} + +Cell *awksprintf(Node **a, int n) /* sprintf(a[0]) */ +{ + Cell *x; + Node *y; + char *buf; + int bufsz=3*recsize; + + if ((buf = (char *) malloc(bufsz)) == NULL) + FATAL("out of memory in awksprintf"); + y = a[0]->nnext; + x = execute(a[0]); + if (format(&buf, &bufsz, getsval(x), y) == -1) + FATAL("sprintf string %.30s... too long. can't happen.", buf); + tempfree(x); + x = gettemp(); + x->sval = buf; + x->tval = STR; + return(x); +} + +Cell *awkprintf(Node **a, int n) /* printf */ +{ /* a[0] is list of args, starting with format string */ + /* a[1] is redirection operator, a[2] is redirection file */ + FILE *fp; + Cell *x; + Node *y; + char *buf; + int len; + int bufsz=3*recsize; + + if ((buf = (char *) malloc(bufsz)) == NULL) + FATAL("out of memory in awkprintf"); + y = a[0]->nnext; + x = execute(a[0]); + if ((len = format(&buf, &bufsz, getsval(x), y)) == -1) + FATAL("printf string %.30s... too long. can't happen.", buf); + tempfree(x); + if (a[1] == NULL) { + /* fputs(buf, stdout); */ + fwrite(buf, len, 1, thread_stdout); + if (ferror(thread_stdout)) + FATAL("write error on stdout"); + } else { + fp = redirect(ptoi(a[1]), a[2]); + /* fputs(buf, fp); */ + fwrite(buf, len, 1, fp); + fflush(fp); + if (ferror(fp)) + FATAL("write error on %s", filename(fp)); + } + free(buf); + return(True); +} + +Cell *arith(Node **a, int n) /* a[0] + a[1], etc. also -a[0] */ +{ + Awkfloat i, j = 0; + double v; + Cell *x, *y, *z; + + x = execute(a[0]); + i = getfval(x); + tempfree(x); + if (n != UMINUS && n != UPLUS) { + y = execute(a[1]); + j = getfval(y); + tempfree(y); + } + z = gettemp(); + switch (n) { + case ADD: + i += j; + break; + case MINUS: + i -= j; + break; + case MULT: + i *= j; + break; + case DIVIDE: + if (j == 0) + FATAL("division by zero"); + i /= j; + break; + case MOD: + if (j == 0) + FATAL("division by zero in mod"); + modf(i/j, &v); + i = i - j * v; + break; + case UMINUS: + i = -i; + break; + case UPLUS: + i = i; + break; + case POWER: + if (j >= 0 && modf(j, &v) == 0.0) /* pos integer exponent */ + i = ipow(i, (int) j); + else + i = errcheck(pow(i, j), "pow"); + break; + default: /* can't happen */ + FATAL("illegal arithmetic operator %d", n); + } + setfval(z, i); + return(z); +} + +double ipow(double x, int n) /* x**n. ought to be done by pow, but isn't always */ +{ + double v; + + if (n <= 0) + return 1; + v = ipow(x, n/2); + if (n % 2 == 0) + return v * v; + else + return x * v * v; +} + +Cell *incrdecr(Node **a, int n) /* a[0]++, etc. */ +{ + Cell *x, *z; + int k; + Awkfloat xf; + + x = execute(a[0]); + xf = getfval(x); + k = (n == PREINCR || n == POSTINCR) ? 1 : -1; + if (n == PREINCR || n == PREDECR) { + setfval(x, xf + k); + return(x); + } + z = gettemp(); + setfval(z, xf); + setfval(x, xf + k); + tempfree(x); + return(z); +} + +Cell *assign(Node **a, int n) /* a[0] = a[1], a[0] += a[1], etc. */ +{ /* this is subtle; don't muck with it. */ + Cell *x, *y; + Awkfloat xf, yf; + double v; + + y = execute(a[1]); + x = execute(a[0]); + if (n == ASSIGN) { /* ordinary assignment */ + if (x == y && !(x->tval & (FLD|REC))) /* self-assignment: */ + ; /* leave alone unless it's a field */ + else if ((y->tval & (STR|NUM)) == (STR|NUM)) { + setsval(x, getsval(y)); + x->fval = getfval(y); + x->tval |= NUM; + } + else if (isstr(y)) + setsval(x, getsval(y)); + else if (isnum(y)) + setfval(x, getfval(y)); + else + funnyvar(y, "read value of"); + tempfree(y); + return(x); + } + xf = getfval(x); + yf = getfval(y); + switch (n) { + case ADDEQ: + xf += yf; + break; + case SUBEQ: + xf -= yf; + break; + case MULTEQ: + xf *= yf; + break; + case DIVEQ: + if (yf == 0) + FATAL("division by zero in /="); + xf /= yf; + break; + case MODEQ: + if (yf == 0) + FATAL("division by zero in %%="); + modf(xf/yf, &v); + xf = xf - yf * v; + break; + case POWEQ: + if (yf >= 0 && modf(yf, &v) == 0.0) /* pos integer exponent */ + xf = ipow(xf, (int) yf); + else + xf = errcheck(pow(xf, yf), "pow"); + break; + default: + FATAL("illegal assignment operator %d", n); + break; + } + tempfree(y); + setfval(x, xf); + return(x); +} + +Cell *cat(Node **a, int q) /* a[0] cat a[1] */ +{ + Cell *x, *y, *z; + int n1, n2; + char *s; + + x = execute(a[0]); + y = execute(a[1]); + getsval(x); + getsval(y); + n1 = strlen(x->sval); + n2 = strlen(y->sval); + s = (char *) malloc(n1 + n2 + 1); + if (s == NULL) + FATAL("out of space concatenating %.15s... and %.15s...", + x->sval, y->sval); + strcpy(s, x->sval); + strcpy(s+n1, y->sval); + tempfree(y); + z = gettemp(); + z->sval = s; + z->tval = STR; + tempfree(x); + return(z); +} + +Cell *pastat(Node **a, int n) /* a[0] { a[1] } */ +{ + Cell *x; + + if (a[0] == 0) + x = execute(a[1]); + else { + x = execute(a[0]); + if (istrue(x)) { + tempfree(x); + x = execute(a[1]); + } + } + return x; +} + +Cell *dopa2(Node **a, int n) /* a[0], a[1] { a[2] } */ +{ + Cell *x; + int pair; + + pair = ptoi(a[3]); + if (pairstack[pair] == 0) { + x = execute(a[0]); + if (istrue(x)) + pairstack[pair] = 1; + tempfree(x); + } + if (pairstack[pair] == 1) { + x = execute(a[1]); + if (istrue(x)) + pairstack[pair] = 0; + tempfree(x); + x = execute(a[2]); + return(x); + } + return(False); +} + +Cell *split(Node **a, int nnn) /* split(a[0], a[1], a[2]); a[3] is type */ +{ + Cell *x = 0, *y, *ap; + char *s; + int sep; + char *t, temp, num[50], *fs = 0; + int n, tempstat, arg3type; + + y = execute(a[0]); /* source string */ + s = getsval(y); + arg3type = ptoi(a[3]); + if (a[2] == 0) /* fs string */ + fs = *FS; + else if (arg3type == STRING) { /* split(str,arr,"string") */ + x = execute(a[2]); + fs = getsval(x); + } else if (arg3type == REGEXPR) + fs = "(regexpr)"; /* split(str,arr,/regexpr/) */ + else + FATAL("illegal type of split"); + sep = *fs; + ap = execute(a[1]); /* array name */ + freesymtab(ap); + dprintf( (thread_stdout, "split: s=|%s|, a=%s, sep=|%s|\n", s, NN(ap->nval), fs) ); + ap->tval &= ~STR; + ap->tval |= ARR; + ap->sval = (char *) makesymtab(NSYMTAB); + + n = 0; + if (*s != '\0' && (strlen(fs) > 1 || arg3type == REGEXPR)) { /* reg expr */ + fa *pfa; + if (arg3type == REGEXPR) { /* it's ready already */ + pfa = (fa *) a[2]; + } else { + pfa = makedfa(fs, 1); + } + if (nematch(pfa,s)) { + tempstat = pfa->initstat; + pfa->initstat = 2; + do { + n++; + sprintf(num, "%d", n); + temp = *patbeg; + *patbeg = '\0'; + if (is_number(s)) + setsymtab(num, s, atof(s), STR|NUM, (Array *) ap->sval); + else + setsymtab(num, s, 0.0, STR, (Array *) ap->sval); + *patbeg = temp; + s = patbeg + patlen; + if (*(patbeg+patlen-1) == 0 || *s == 0) { + n++; + sprintf(num, "%d", n); + setsymtab(num, "", 0.0, STR, (Array *) ap->sval); + pfa->initstat = tempstat; + goto spdone; + } + } while (nematch(pfa,s)); + pfa->initstat = tempstat; /* bwk: has to be here to reset */ + /* cf gsub and refldbld */ + } + n++; + sprintf(num, "%d", n); + if (is_number(s)) + setsymtab(num, s, atof(s), STR|NUM, (Array *) ap->sval); + else + setsymtab(num, s, 0.0, STR, (Array *) ap->sval); + spdone: + pfa = NULL; + } else if (sep == ' ') { + for (n = 0; ; ) { + while (*s == ' ' || *s == '\t' || *s == '\n') + s++; + if (*s == 0) + break; + n++; + t = s; + do + s++; + while (*s!=' ' && *s!='\t' && *s!='\n' && *s!='\0'); + temp = *s; + *s = '\0'; + sprintf(num, "%d", n); + if (is_number(t)) + setsymtab(num, t, atof(t), STR|NUM, (Array *) ap->sval); + else + setsymtab(num, t, 0.0, STR, (Array *) ap->sval); + *s = temp; + if (*s != 0) + s++; + } + } else if (sep == 0) { /* new: split(s, a, "") => 1 char/elem */ + for (n = 0; *s != 0; s++) { + char buf[2]; + n++; + sprintf(num, "%d", n); + buf[0] = *s; + buf[1] = 0; + if (isdigit((uschar)buf[0])) + setsymtab(num, buf, atof(buf), STR|NUM, (Array *) ap->sval); + else + setsymtab(num, buf, 0.0, STR, (Array *) ap->sval); + } + } else if (*s != 0) { + for (;;) { + n++; + t = s; + while (*s != sep && *s != '\n' && *s != '\0') + s++; + temp = *s; + *s = '\0'; + sprintf(num, "%d", n); + if (is_number(t)) + setsymtab(num, t, atof(t), STR|NUM, (Array *) ap->sval); + else + setsymtab(num, t, 0.0, STR, (Array *) ap->sval); + *s = temp; + if (*s++ == 0) + break; + } + } + tempfree(ap); + tempfree(y); + if (a[2] != 0 && arg3type == STRING) { + tempfree(x); + } + x = gettemp(); + x->tval = NUM; + x->fval = n; + return(x); +} + +Cell *condexpr(Node **a, int n) /* a[0] ? a[1] : a[2] */ +{ + Cell *x; + + x = execute(a[0]); + if (istrue(x)) { + tempfree(x); + x = execute(a[1]); + } else { + tempfree(x); + x = execute(a[2]); + } + return(x); +} + +Cell *ifstat(Node **a, int n) /* if (a[0]) a[1]; else a[2] */ +{ + Cell *x; + + x = execute(a[0]); + if (istrue(x)) { + tempfree(x); + x = execute(a[1]); + } else if (a[2] != 0) { + tempfree(x); + x = execute(a[2]); + } + return(x); +} + +Cell *whilestat(Node **a, int n) /* while (a[0]) a[1] */ +{ + Cell *x; + + for (;;) { + x = execute(a[0]); + if (!istrue(x)) + return(x); + tempfree(x); + x = execute(a[1]); + if (isbreak(x)) { + x = True; + return(x); + } + if (isnext(x) || isexit(x) || isret(x)) + return(x); + tempfree(x); + } +} + +Cell *dostat(Node **a, int n) /* do a[0]; while(a[1]) */ +{ + Cell *x; + + for (;;) { + x = execute(a[0]); + if (isbreak(x)) + return True; + if (isnext(x) || isexit(x) || isret(x)) + return(x); + tempfree(x); + x = execute(a[1]); + if (!istrue(x)) + return(x); + tempfree(x); + } +} + +Cell *forstat(Node **a, int n) /* for (a[0]; a[1]; a[2]) a[3] */ +{ + Cell *x; + + x = execute(a[0]); + tempfree(x); + for (;;) { + if (a[1]!=0) { + x = execute(a[1]); + if (!istrue(x)) return(x); + else tempfree(x); + } + x = execute(a[3]); + if (isbreak(x)) /* turn off break */ + return True; + if (isnext(x) || isexit(x) || isret(x)) + return(x); + tempfree(x); + x = execute(a[2]); + tempfree(x); + } +} + +Cell *instat(Node **a, int n) /* for (a[0] in a[1]) a[2] */ +{ + Cell *x, *vp, *arrayp, *cp, *ncp; + Array *tp; + int i; + + vp = execute(a[0]); + arrayp = execute(a[1]); + if (!isarr(arrayp)) { + return True; + } + tp = (Array *) arrayp->sval; + tempfree(arrayp); + for (i = 0; i < tp->size; i++) { /* this routine knows too much */ + for (cp = tp->tab[i]; cp != NULL; cp = ncp) { + setsval(vp, cp->nval); + ncp = cp->cnext; + x = execute(a[2]); + if (isbreak(x)) { + tempfree(vp); + return True; + } + if (isnext(x) || isexit(x) || isret(x)) { + tempfree(vp); + return(x); + } + tempfree(x); + } + } + return True; +} + +Cell *bltin(Node **a, int n) /* builtin functions. a[0] is type, a[1] is arg list */ +{ + Cell *x, *y; + Awkfloat u; + int t; + char *p, *buf; + Node *nextarg; + FILE *fp; + void flush_all(void); + + t = ptoi(a[0]); + x = execute(a[1]); + nextarg = a[1]->nnext; + switch (t) { + case FLENGTH: + if (isarr(x)) + u = ((Array *) x->sval)->nelem; /* GROT. should be function*/ + else + u = strlen(getsval(x)); + break; + case FLOG: + u = errcheck(log(getfval(x)), "log"); break; + case FINT: + modf(getfval(x), &u); break; + case FEXP: + u = errcheck(exp(getfval(x)), "exp"); break; + case FSQRT: + u = errcheck(sqrt(getfval(x)), "sqrt"); break; + case FSIN: + u = sin(getfval(x)); break; + case FCOS: + u = cos(getfval(x)); break; + case FATAN: + if (nextarg == 0) { + WARNING("atan2 requires two arguments; returning 1.0"); + u = 1.0; + } else { + y = execute(a[1]->nnext); + u = atan2(getfval(x), getfval(y)); + tempfree(y); + nextarg = nextarg->nnext; + } + break; + case FSYSTEM: + fflush(thread_stdout); /* in case something is buffered already */ + u = (Awkfloat) ios_system(getsval(x)) / 256; /* 256 is unix-dep */ + break; + case FRAND: + /* in principle, rand() returns something in 0..RAND_MAX */ + u = (Awkfloat) (random() % RAND_MAX) / RAND_MAX; + break; + case FSRAND: + if (isrec(x)) /* no argument provided */ + u = time((time_t *)0); + else + u = getfval(x); + srandom((unsigned int) u); + tmp_srand = u; + u = prev_srand; /* return the previous value */ + prev_srand = tmp_srand; /* remember for next time */ + break; + case FTOUPPER: + case FTOLOWER: + buf = tostring(getsval(x)); + if (t == FTOUPPER) { + for (p = buf; *p; p++) + if (islower((uschar) *p)) + *p = toupper((uschar)*p); + } else { + for (p = buf; *p; p++) + if (isupper((uschar) *p)) + *p = tolower((uschar)*p); + } + tempfree(x); + x = gettemp(); + setsval(x, buf); + free(buf); + return x; + case FFLUSH: + if (isrec(x) || strlen(getsval(x)) == 0) { + flush_all(); /* fflush() or fflush("") -> all */ + u = 0; + } else if ((fp = openfile(FFLUSH, getsval(x))) == NULL) + u = EOF; + else + u = fflush(fp); + break; + default: /* can't happen */ + FATAL("illegal function type %d", t); + break; + } + tempfree(x); + x = gettemp(); + setfval(x, u); + if (nextarg != 0) { + WARNING("warning: function has too many arguments"); + for ( ; nextarg; nextarg = nextarg->nnext) + execute(nextarg); + } + return(x); +} + +Cell *printstat(Node **a, int n) /* print a[0] */ +{ + Node *x; + Cell *y; + FILE *fp; + + if (a[1] == 0) /* a[1] is redirection operator, a[2] is file */ + fp = thread_stdout; + else + fp = redirect(ptoi(a[1]), a[2]); + for (x = a[0]; x != NULL; x = x->nnext) { + y = execute(x); + fputs(getpssval(y), fp); + tempfree(y); + if (x->nnext == NULL) + fputs(*ORS, fp); + else + fputs(*OFS, fp); + } + if (a[1] != 0) + fflush(fp); + if (ferror(fp)) + FATAL("write error on %s", filename(fp)); + return(True); +} + +Cell *nullproc(Node **a, int n) +{ + n = n; + a = a; + return 0; +} + + +FILE *redirect(int a, Node *b) /* set up all i/o redirections */ +{ + FILE *fp; + Cell *x; + char *fname; + + x = execute(b); + fname = getsval(x); + fp = openfile(a, fname); + if (fp == NULL) + FATAL("can't open file %s", fname); + tempfree(x); + return fp; +} + + +FILE *openfile(int a, const char *us) +{ + const char *s = us; + int i, m; + FILE *fp = 0; + + if (*s == '\0') + FATAL("null file name in print or getline"); + for (i=0; i < FOPEN_MAX; i++) + if (files[i].fname && strcmp(s, files[i].fname) == 0) { + if (a == files[i].mode || (a==APPEND && files[i].mode==GT)) + return files[i].fp; + if (a == FFLUSH) + return files[i].fp; + } + if (a == FFLUSH) /* didn't find it, so don't create it! */ + return NULL; + + for (i=0; i < FOPEN_MAX; i++) + if (files[i].fp == 0) + break; + if (i >= FOPEN_MAX) + FATAL("%s makes too many open files", s); + fflush(thread_stdout); /* force a semblance of order */ + m = a; + if (a == GT) { + fp = fopen(s, "w"); + } else if (a == APPEND) { + fp = fopen(s, "a"); + m = GT; /* so can mix > and >> */ + } else if (a == '|') { /* output pipe */ + fp = popen(s, "w"); + } else if (a == LE) { /* input pipe */ + fp = popen(s, "r"); + } else if (a == LT) { /* getline sval, files[i].fname) == 0) { + if (ferror(files[i].fp)) + WARNING( "i/o error occurred on %s", files[i].fname ); + if (files[i].mode == '|' || files[i].mode == LE) + stat = pclose(files[i].fp); + else { + stat = fclose(files[i].fp); + files[i].fp = 0; + } + if (stat == EOF) + WARNING( "i/o error occurred closing %s", files[i].fname ); + if (i > 2) /* don't do /dev/std... */ + xfree(files[i].fname); + files[i].fname = NULL; /* watch out for ref thru this */ + files[i].fp = NULL; + } + } + tempfree(x); + x = gettemp(); + setfval(x, (Awkfloat) stat); + return(x); +} + +void closeall(void) +{ + int i, stat; + + for (i = 0; i < FOPEN_MAX; i++) { + if (files[i].fp) { + // Do not close stdin or the others + if (files[i].fp == thread_stdin) continue; + if (files[i].fp == thread_stdout) continue; + if (files[i].fp == thread_stderr) continue; + if (ferror(files[i].fp)) + WARNING( "i/o error occurred on %s", files[i].fname ); + if (files[i].mode == '|' || files[i].mode == LE) + stat = pclose(files[i].fp); + else { + stat = fclose(files[i].fp); + files[i].fp = 0; + } + if (stat == EOF) + WARNING( "i/o error occurred while closing %s", files[i].fname ); + } + } +} + +void flush_all(void) +{ + int i; + + for (i = 0; i < FOPEN_MAX; i++) + if (files[i].fp) + fflush(files[i].fp); +} + +void backsub(char **pb_ptr, char **sptr_ptr); + +Cell *sub(Node **a, int nnn) /* substitute command */ +{ + char *sptr, *pb, *q; + Cell *x, *y, *result; + char *t, *buf; + fa *pfa; + int bufsz = recsize; + + if ((buf = (char *) malloc(bufsz)) == NULL) + FATAL("out of memory in sub"); + x = execute(a[3]); /* target string */ + t = getsval(x); + if (a[0] == 0) /* 0 => a[1] is already-compiled regexpr */ + pfa = (fa *) a[1]; /* regular expression */ + else { + y = execute(a[1]); + pfa = makedfa(getsval(y), 1); + tempfree(y); + } + y = execute(a[2]); /* replacement string */ + result = False; + if (pmatch(pfa, t)) { + sptr = t; + adjbuf(&buf, &bufsz, 1+patbeg-sptr, recsize, 0, "sub"); + pb = buf; + while (sptr < patbeg) + *pb++ = *sptr++; + sptr = getsval(y); + while (*sptr != 0) { + adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "sub"); + if (*sptr == '\\') { + backsub(&pb, &sptr); + } else if (*sptr == '&') { + sptr++; + adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "sub"); + for (q = patbeg; q < patbeg+patlen; ) + *pb++ = *q++; + } else + *pb++ = *sptr++; + } + *pb = '\0'; + if (pb > buf + bufsz) + FATAL("sub result1 %.30s too big; can't happen", buf); + sptr = patbeg + patlen; + if ((patlen == 0 && *patbeg) || (patlen && *(sptr-1))) { + adjbuf(&buf, &bufsz, 1+strlen(sptr)+pb-buf, 0, &pb, "sub"); + while ((*pb++ = *sptr++) != 0) + ; + } + if (pb > buf + bufsz) + FATAL("sub result2 %.30s too big; can't happen", buf); + setsval(x, buf); /* BUG: should be able to avoid copy */ + result = True;; + } + tempfree(x); + tempfree(y); + free(buf); + return result; +} + +Cell *gsub(Node **a, int nnn) /* global substitute */ +{ + Cell *x, *y; + char *rptr, *sptr, *t, *pb, *q; + char *buf; + fa *pfa; + int mflag, tempstat, num; + int bufsz = recsize; + + if ((buf = (char *) malloc(bufsz)) == NULL) + FATAL("out of memory in gsub"); + mflag = 0; /* if mflag == 0, can replace empty string */ + num = 0; + x = execute(a[3]); /* target string */ + t = getsval(x); + if (a[0] == 0) /* 0 => a[1] is already-compiled regexpr */ + pfa = (fa *) a[1]; /* regular expression */ + else { + y = execute(a[1]); + pfa = makedfa(getsval(y), 1); + tempfree(y); + } + y = execute(a[2]); /* replacement string */ + if (pmatch(pfa, t)) { + tempstat = pfa->initstat; + pfa->initstat = 2; + pb = buf; + rptr = getsval(y); + do { + if (patlen == 0 && *patbeg != 0) { /* matched empty string */ + if (mflag == 0) { /* can replace empty */ + num++; + sptr = rptr; + while (*sptr != 0) { + adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "gsub"); + if (*sptr == '\\') { + backsub(&pb, &sptr); + } else if (*sptr == '&') { + sptr++; + adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "gsub"); + for (q = patbeg; q < patbeg+patlen; ) + *pb++ = *q++; + } else + *pb++ = *sptr++; + } + } + if (*t == 0) /* at end */ + goto done; + adjbuf(&buf, &bufsz, 2+pb-buf, recsize, &pb, "gsub"); + *pb++ = *t++; + if (pb > buf + bufsz) /* BUG: not sure of this test */ + FATAL("gsub result0 %.30s too big; can't happen", buf); + mflag = 0; + } + else { /* matched nonempty string */ + num++; + sptr = t; + adjbuf(&buf, &bufsz, 1+(patbeg-sptr)+pb-buf, recsize, &pb, "gsub"); + while (sptr < patbeg) + *pb++ = *sptr++; + sptr = rptr; + while (*sptr != 0) { + adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "gsub"); + if (*sptr == '\\') { + backsub(&pb, &sptr); + } else if (*sptr == '&') { + sptr++; + adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "gsub"); + for (q = patbeg; q < patbeg+patlen; ) + *pb++ = *q++; + } else + *pb++ = *sptr++; + } + t = patbeg + patlen; + if (patlen == 0 || *t == 0 || *(t-1) == 0) + goto done; + if (pb > buf + bufsz) + FATAL("gsub result1 %.30s too big; can't happen", buf); + mflag = 1; + } + } while (pmatch(pfa,t)); + sptr = t; + adjbuf(&buf, &bufsz, 1+strlen(sptr)+pb-buf, 0, &pb, "gsub"); + while ((*pb++ = *sptr++) != 0) + ; + done: if (pb > buf + bufsz) + FATAL("gsub result2 %.30s too big; can't happen", buf); + *pb = '\0'; + setsval(x, buf); /* BUG: should be able to avoid copy + free */ + pfa->initstat = tempstat; + } + tempfree(x); + tempfree(y); + x = gettemp(); + x->tval = NUM; + x->fval = num; + free(buf); + return(x); +} + +void backsub(char **pb_ptr, char **sptr_ptr) /* handle \\& variations */ +{ /* sptr[0] == '\\' */ + char *pb = *pb_ptr, *sptr = *sptr_ptr; + + if (sptr[1] == '\\') { + if (sptr[2] == '\\' && sptr[3] == '&') { /* \\\& -> \& */ + *pb++ = '\\'; + *pb++ = '&'; + sptr += 4; + } else if (sptr[2] == '&') { /* \\& -> \ + matched */ + *pb++ = '\\'; + sptr += 2; + } else { /* \\x -> \\x */ + *pb++ = *sptr++; + *pb++ = *sptr++; + } + } else if (sptr[1] == '&') { /* literal & */ + sptr++; + *pb++ = *sptr++; + } else /* literal \ */ + *pb++ = *sptr++; + + *pb_ptr = pb; + *sptr_ptr = sptr; +} diff --git a/awk/Sources/awk/tran.c b/awk/Sources/awk/tran.c new file mode 100644 index 00000000..af9b9b46 --- /dev/null +++ b/awk/Sources/awk/tran.c @@ -0,0 +1,467 @@ +/**************************************************************** +Copyright (C) Lucent Technologies 1997 +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name Lucent Technologies or any of +its entities not be used in advertising or publicity pertaining +to distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +****************************************************************/ + +#define DEBUG +#include +#include +#include +#include +#include +#include "awk.h" +#include "ytab.h" +#include "ios_error.h" + +#define FULLTAB 2 /* rehash when table gets this x full */ +#define GROWTAB 4 /* grow table by this factor */ + +__thread Array *symtab; /* main symbol table */ + +__thread char **FS; /* initial field sep */ +__thread char **RS; /* initial record sep */ +__thread char **OFS; /* output field sep */ +__thread char **ORS; /* output record sep */ +__thread char **OFMT; /* output format for numbers */ +static __thread char **CONVFMT; /* format for conversions in getsval */ +__thread Awkfloat *NF; /* number of fields in current record */ +__thread Awkfloat *NR; /* number of current record */ +__thread Awkfloat *FNR; /* number of current record in current file */ +__thread char **FILENAME; /* current filename argument */ +__thread Awkfloat *ARGC; /* number of arguments from command line */ +__thread char **SUBSEP; /* subscript separator for a[i,j,k]; default \034 */ +__thread Awkfloat *RSTART; /* start of re matched with ~; origin 1 (!) */ +__thread Awkfloat *RLENGTH; /* length of same */ + +static __thread Cell *fsloc; /* FS */ +__thread Cell *nrloc; /* NR */ +__thread Cell *nfloc; /* NF */ +__thread Cell *fnrloc; /* FNR */ +__thread Array *ARGVtab; /* symbol table containing ARGV[...] */ +static __thread Array *ENVtab; /* symbol table containing ENVIRON[...] */ +__thread Cell *rstartloc; /* RSTART */ +__thread Cell *rlengthloc; /* RLENGTH */ +static __thread Cell *symtabloc; /* SYMTAB */ + +static __thread Cell *nullloc; /* a guaranteed empty cell */ +__thread Node *nullnode; /* zero&null, converted into a node for comparisons */ +__thread Cell *literal0; + +extern __thread Cell **fldtab; + +void syminit(void) /* initialize symbol table with builtin vars */ +{ + literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab); + /* this is used for if(x)... tests: */ + nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab); + nullnode = celltonode(nullloc, CCON); + + fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab); + FS = &fsloc->sval; + RS = &setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab)->sval; + OFS = &setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab)->sval; + ORS = &setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab)->sval; + OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval; + CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval; + FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval; + nfloc = setsymtab("NF", "", 0.0, NUM, symtab); + NF = &nfloc->fval; + nrloc = setsymtab("NR", "", 0.0, NUM, symtab); + NR = &nrloc->fval; + fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab); + FNR = &fnrloc->fval; + SUBSEP = &setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab)->sval; + rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab); + RSTART = &rstartloc->fval; + rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab); + RLENGTH = &rlengthloc->fval; + symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab); + symtabloc->sval = (char *) symtab; +} + +void arginit(int ac, char **av) /* set up ARGV and ARGC */ +{ + Cell *cp; + int i; + char temp[50]; + + ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval; + cp = setsymtab("ARGV", "", 0.0, ARR, symtab); + ARGVtab = makesymtab(NSYMTAB); /* could be (int) ARGC as well */ + cp->sval = (char *) ARGVtab; + for (i = 0; i < ac; i++) { + sprintf(temp, "%d", i); + if (is_number(*av)) + setsymtab(temp, *av, atof(*av), STR|NUM, ARGVtab); + else + setsymtab(temp, *av, 0.0, STR, ARGVtab); + av++; + } +} + +void envinit(char **envp) /* set up ENVIRON variable */ +{ + Cell *cp; + char *p; + + cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab); + ENVtab = makesymtab(NSYMTAB); + cp->sval = (char *) ENVtab; + for ( ; *envp; envp++) { + if ((p = strchr(*envp, '=')) == NULL) + continue; + if( p == *envp ) /* no left hand side name in env string */ + continue; + *p++ = 0; /* split into two strings at = */ + if (is_number(p)) + setsymtab(*envp, p, atof(p), STR|NUM, ENVtab); + else + setsymtab(*envp, p, 0.0, STR, ENVtab); + p[-1] = '='; /* restore in case env is passed down to a shell */ + } +} + +Array *makesymtab(int n) /* make a new symbol table */ +{ + Array *ap; + Cell **tp; + + ap = (Array *) calloc(1, sizeof(Array)); + tp = (Cell **) calloc(n, sizeof(Cell *)); + if (ap == NULL || tp == NULL) + FATAL("out of space in makesymtab"); + + ap->nelem = 0; + ap->size = n; + ap->tab = tp; + return(ap); +} + +void freesymtab(Cell *ap) /* free a symbol table */ +{ + Cell *cp, *temp; + Array *tp; + int i; + + if (!isarr(ap)) + return; + tp = (Array *) ap->sval; + if (tp == NULL) + return; + for (i = 0; i < tp->size; i++) { + for (cp = tp->tab[i]; cp != NULL; cp = temp) { + xfree(cp->nval); + if (freeable(cp)) + xfree(cp->sval); + temp = cp->cnext; /* avoids freeing then using */ + free(cp); + tp->nelem--; + } + tp->tab[i] = 0; + } + if (tp->nelem != 0) + WARNING("can't happen: inconsistent element count freeing %s", ap->nval); + free(tp->tab); + free(tp); +} + +void freeelem(Cell *ap, const char *s) /* free elem s from ap (i.e., ap["s"] */ +{ + Array *tp; + Cell *p, *prev = NULL; + int h; + + tp = (Array *) ap->sval; + h = hash(s, tp->size); + for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext) + if (strcmp(s, p->nval) == 0) { + if (prev == NULL) /* 1st one */ + tp->tab[h] = p->cnext; + else /* middle somewhere */ + prev->cnext = p->cnext; + if (freeable(p)) + xfree(p->sval); + free(p->nval); + free(p); + tp->nelem--; + return; + } +} + +Cell *setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp) +{ + int h; + Cell *p; + + if (n != NULL && (p = lookup(n, tp)) != NULL) { + dprintf( (thread_stdout, "setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n", + p, NN(p->nval), NN(p->sval), p->fval, p->tval) ); + return(p); + } + p = (Cell *) malloc(sizeof(Cell)); + if (p == NULL) + FATAL("out of space for symbol table at %s", n); + p->nval = tostring(n); + p->sval = s ? tostring(s) : tostring(""); + p->fval = f; + p->tval = t; + p->csub = CUNK; + p->ctype = OCELL; + tp->nelem++; + if (tp->nelem > FULLTAB * tp->size) + rehash(tp); + h = hash(n, tp->size); + p->cnext = tp->tab[h]; + tp->tab[h] = p; + dprintf( (thread_stdout, "setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n", + p, p->nval, p->sval, p->fval, p->tval) ); + return(p); +} + +int hash(const char *s, int n) /* form hash value for string s */ +{ + unsigned hashval; + + for (hashval = 0; *s != '\0'; s++) + hashval = (*s + 31 * hashval); + return hashval % n; +} + +void rehash(Array *tp) /* rehash items in small table into big one */ +{ + int i, nh, nsz; + Cell *cp, *op, **np; + + nsz = GROWTAB * tp->size; + np = (Cell **) calloc(nsz, sizeof(Cell *)); + if (np == NULL) /* can't do it, but can keep running. */ + return; /* someone else will run out later. */ + for (i = 0; i < tp->size; i++) { + for (cp = tp->tab[i]; cp; cp = op) { + op = cp->cnext; + nh = hash(cp->nval, nsz); + cp->cnext = np[nh]; + np[nh] = cp; + } + } + free(tp->tab); + tp->tab = np; + tp->size = nsz; +} + +Cell *lookup(const char *s, Array *tp) /* look for s in tp */ +{ + Cell *p; + int h; + + h = hash(s, tp->size); + for (p = tp->tab[h]; p != NULL; p = p->cnext) + if (strcmp(s, p->nval) == 0) + return(p); /* found it */ + return(NULL); /* not found */ +} + +Awkfloat setfval(Cell *vp, Awkfloat f) /* set float val of a Cell */ +{ + int fldno; + + if ((vp->tval & (NUM | STR)) == 0) + funnyvar(vp, "assign to"); + if (isfld(vp)) { + donerec = 0; /* mark $0 invalid */ + fldno = atoi(vp->nval); + if (fldno > *NF) + newfld(fldno); + dprintf( (thread_stdout, "setting field %d to %g\n", fldno, f) ); + } else if (isrec(vp)) { + donefld = 0; /* mark $1... invalid */ + donerec = 1; + } + if (freeable(vp)) + xfree(vp->sval); /* free any previous string */ + vp->tval &= ~STR; /* mark string invalid */ + vp->tval |= NUM; /* mark number ok */ + if (f == -0) /* who would have thought this possible? */ + f = 0; + dprintf( (thread_stdout, "setfval %p: %s = %g, t=%o\n", vp, NN(vp->nval), f, vp->tval) ); + return vp->fval = f; +} + +void funnyvar(Cell *vp, const char *rw) +{ + if (isarr(vp)) + FATAL("can't %s %s; it's an array name.", rw, vp->nval); + if (vp->tval & FCN) + FATAL("can't %s %s; it's a function.", rw, vp->nval); + WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o", + vp, vp->nval, vp->sval, vp->fval, vp->tval); +} + +char *setsval(Cell *vp, const char *s) /* set string val of a Cell */ +{ + char *t; + int fldno; + + dprintf( (thread_stdout, "starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n", + vp, NN(vp->nval), s, vp->tval, donerec, donefld) ); + if ((vp->tval & (NUM | STR)) == 0) + funnyvar(vp, "assign to"); + if (isfld(vp)) { + donerec = 0; /* mark $0 invalid */ + fldno = atoi(vp->nval); + if (fldno > *NF) + newfld(fldno); + dprintf( (thread_stdout, "setting field %d to %s (%p)\n", fldno, s, s) ); + } else if (isrec(vp)) { + donefld = 0; /* mark $1... invalid */ + donerec = 1; + } + t = tostring(s); /* in case it's self-assign */ + if (freeable(vp)) + xfree(vp->sval); + vp->tval &= ~NUM; + vp->tval |= STR; + vp->tval &= ~DONTFREE; + dprintf( (thread_stdout, "setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n", + vp, NN(vp->nval), t,t, vp->tval, donerec, donefld) ); + return(vp->sval = t); +} + +Awkfloat getfval(Cell *vp) /* get float val of a Cell */ +{ + if ((vp->tval & (NUM | STR)) == 0) + funnyvar(vp, "read value of"); + if (isfld(vp) && donefld == 0) + fldbld(); + else if (isrec(vp) && donerec == 0) + recbld(); + if (!isnum(vp)) { /* not a number */ + vp->fval = atof(vp->sval); /* best guess */ + if (is_number(vp->sval) && !(vp->tval&CON)) + vp->tval |= NUM; /* make NUM only sparingly */ + } + dprintf( (thread_stdout, "getfval %p: %s = %g, t=%o\n", vp, NN(vp->nval), vp->fval, vp->tval) ); + return(vp->fval); +} + +static char *get_str_val(Cell *vp, char **fmt) /* get string val of a Cell */ +{ + char s[100]; /* BUG: unchecked */ + double dtemp; + + if ((vp->tval & (NUM | STR)) == 0) + funnyvar(vp, "read value of"); + if (isfld(vp) && donefld == 0) + fldbld(); + else if (isrec(vp) && donerec == 0) + recbld(); + if (isstr(vp) == 0) { + if (freeable(vp)) + xfree(vp->sval); + if (modf(vp->fval, &dtemp) == 0) /* it's integral */ + sprintf(s, "%.30g", vp->fval); + else + sprintf(s, *fmt, vp->fval); + vp->sval = tostring(s); +#if 0 + /* + Cannot reuse the converted form unless you confirm + that the current value of CONVFMT is the same as that + which was used to convert the previous string. See + conformance test awk.ex 233 + */ + vp->tval &= ~DONTFREE; + vp->tval |= STR; +#endif + } + dprintf( (thread_stdout, "getsval %p: %s = \"%s (%p)\", t=%o\n", vp, NN(vp->nval), vp->sval, vp->sval, vp->tval) ); + return(vp->sval); +} + +char *getsval(Cell *vp) /* get string val of a Cell */ +{ + return get_str_val(vp, CONVFMT); +} + +char *getpssval(Cell *vp) /* get string val of a Cell for print */ +{ + return get_str_val(vp, OFMT); +} + + +char *tostring(const char *s) /* make a copy of string s */ +{ + char *p; + + p = (char *) malloc(strlen(s)+1); + if (p == NULL) + FATAL("out of space in tostring on %s", s); + strcpy(p, s); + return(p); +} + +char *qstring(const char *is, int delim) /* collect string up to next delim */ +{ + const char *os = is; + int c, n; + uschar *s = (uschar *) is; + uschar *buf, *bp; + + if ((buf = (uschar *) malloc(strlen(is)+3)) == NULL) + FATAL( "out of space in qstring(%s)", s); + for (bp = buf; (c = *s) != delim; s++) { + if (c == '\n') + SYNTAX( "newline in string %.20s...", os ); + else if (c != '\\') + *bp++ = c; + else { /* \something */ + c = *++s; + if (c == 0) { /* \ at end */ + *bp++ = '\\'; + break; /* for loop */ + } + switch (c) { + case '\\': *bp++ = '\\'; break; + case 'n': *bp++ = '\n'; break; + case 't': *bp++ = '\t'; break; + case 'b': *bp++ = '\b'; break; + case 'f': *bp++ = '\f'; break; + case 'r': *bp++ = '\r'; break; + default: + if (!isdigit(c)) { + *bp++ = c; + break; + } + n = c - '0'; + if (isdigit(s[1])) { + n = 8 * n + *++s - '0'; + if (isdigit(s[1])) + n = 8 * n + *++s - '0'; + } + *bp++ = n; + break; + } + } + } + *bp++ = 0; + return (char *) buf; +} diff --git a/awk/Sources/awk/ytab.c b/awk/Sources/awk/ytab.c new file mode 100644 index 00000000..a25b610e --- /dev/null +++ b/awk/Sources/awk/ytab.c @@ -0,0 +1,3753 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ +#include "ios_error.h" + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.3" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + FIRSTTOKEN = 258, + PROGRAM = 259, + PASTAT = 260, + PASTAT2 = 261, + XBEGIN = 262, + XEND = 263, + NL = 264, + ARRAY = 265, + MATCH = 266, + NOTMATCH = 267, + MATCHOP = 268, + FINAL = 269, + DOT = 270, + ALL = 271, + CCL = 272, + NCCL = 273, + CHAR = 274, + OR = 275, + STAR = 276, + QUEST = 277, + PLUS = 278, + EMPTYRE = 279, + IGNORE_PRIOR_ATOM = 280, + AND = 281, + BOR = 282, + APPEND = 283, + EQ = 284, + GE = 285, + GT = 286, + LE = 287, + LT = 288, + NE = 289, + IN = 290, + ARG = 291, + BLTIN = 292, + BREAK = 293, + CLOSE = 294, + CONTINUE = 295, + DELETE = 296, + DO = 297, + EXIT = 298, + FOR = 299, + FUNC = 300, + SUB = 301, + GSUB = 302, + IF = 303, + INDEX = 304, + LSUBSTR = 305, + MATCHFCN = 306, + NEXT = 307, + NEXTFILE = 308, + ADD = 309, + MINUS = 310, + MULT = 311, + DIVIDE = 312, + MOD = 313, + ASSIGN = 314, + ASGNOP = 315, + ADDEQ = 316, + SUBEQ = 317, + MULTEQ = 318, + DIVEQ = 319, + MODEQ = 320, + POWEQ = 321, + PRINT = 322, + PRINTF = 323, + SPRINTF = 324, + ELSE = 325, + INTEST = 326, + CONDEXPR = 327, + POSTINCR = 328, + PREINCR = 329, + POSTDECR = 330, + PREDECR = 331, + VAR = 332, + IVAR = 333, + VARNF = 334, + CALL = 335, + NUMBER = 336, + STRING = 337, + REGEXPR = 338, + GETLINE = 339, + SUBSTR = 340, + SPLIT = 341, + RETURN = 342, + WHILE = 343, + CAT = 344, + UPLUS = 345, + UMINUS = 346, + NOT = 347, + POWER = 348, + INCR = 349, + DECR = 350, + INDIRECT = 351, + LASTTOKEN = 352 + }; +#endif +/* Tokens. */ +#define FIRSTTOKEN 258 +#define PROGRAM 259 +#define PASTAT 260 +#define PASTAT2 261 +#define XBEGIN 262 +#define XEND 263 +#define NL 264 +#define ARRAY 265 +#define MATCH 266 +#define NOTMATCH 267 +#define MATCHOP 268 +#define FINAL 269 +#define DOT 270 +#define ALL 271 +#define CCL 272 +#define NCCL 273 +#define CHAR 274 +#define OR 275 +#define STAR 276 +#define QUEST 277 +#define PLUS 278 +#define EMPTYRE 279 +#define IGNORE_PRIOR_ATOM 280 +#define AND 281 +#define BOR 282 +#define APPEND 283 +#define EQ 284 +#define GE 285 +#define GT 286 +#define LE 287 +#define LT 288 +#define NE 289 +#define IN 290 +#define ARG 291 +#define BLTIN 292 +#define BREAK 293 +#define CLOSE 294 +#define CONTINUE 295 +#define DELETE 296 +#define DO 297 +#define EXIT 298 +#define FOR 299 +#define FUNC 300 +#define SUB 301 +#define GSUB 302 +#define IF 303 +#define INDEX 304 +#define LSUBSTR 305 +#define MATCHFCN 306 +#define NEXT 307 +#define NEXTFILE 308 +#define ADD 309 +#define MINUS 310 +#define MULT 311 +#define DIVIDE 312 +#define MOD 313 +#define ASSIGN 314 +#define ASGNOP 315 +#define ADDEQ 316 +#define SUBEQ 317 +#define MULTEQ 318 +#define DIVEQ 319 +#define MODEQ 320 +#define POWEQ 321 +#define PRINT 322 +#define PRINTF 323 +#define SPRINTF 324 +#define ELSE 325 +#define INTEST 326 +#define CONDEXPR 327 +#define POSTINCR 328 +#define PREINCR 329 +#define POSTDECR 330 +#define PREDECR 331 +#define VAR 332 +#define IVAR 333 +#define VARNF 334 +#define CALL 335 +#define NUMBER 336 +#define STRING 337 +#define REGEXPR 338 +#define GETLINE 339 +#define SUBSTR 340 +#define SPLIT 341 +#define RETURN 342 +#define WHILE 343 +#define CAT 344 +#define UPLUS 345 +#define UMINUS 346 +#define NOT 347 +#define POWER 348 +#define INCR 349 +#define DECR 350 +#define INDIRECT 351 +#define LASTTOKEN 352 + + + + +/* Copy the first part of user declarations. */ +#line 25 "awkgram.y" + +#include +#include +#include "awk.h" + +void checkdup(Node *list, Cell *item); +int yywrap(void) { return(1); } + +__thread Node *beginloc = 0; +__thread Node *endloc = 0; +__thread int infunc = 0; /* = 1 if in arglist or body of func */ +__thread int inloop = 0; /* = 1 if in while, for, do */ +__thread char *curfname = 0; /* current function name */ +__thread Node *arglist = 0; /* list of args for current function */ + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +#line 41 "awkgram.y" +{ + Node *p; + Cell *cp; + int i; + char *s; +} +/* Line 193 of yacc.c. */ +#line 313 "y.tab.c" + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +/* Copy the second part of user declarations. */ + + +/* Line 216 of yacc.c. */ +#line 326 "y.tab.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int i) +#else +static int +YYID (i) + int i; +#endif +{ + return i; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss; + YYSTYPE yyvs; + }; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 8 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 4787 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 114 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 49 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 186 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 369 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 352 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 105, 2, 2, + 12, 16, 104, 102, 9, 103, 2, 15, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 95, 14, + 2, 2, 2, 94, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 18, 2, 19, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 11, 13, 17, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 10, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 96, + 97, 98, 99, 100, 101, 106, 107, 108, 109, 110, + 111, 112, 113 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint16 yyprhs[] = +{ + 0, 0, 3, 5, 7, 9, 12, 14, 17, 19, + 22, 24, 27, 29, 32, 33, 46, 47, 58, 59, + 68, 70, 72, 77, 79, 82, 84, 87, 88, 90, + 91, 93, 94, 96, 98, 102, 104, 106, 111, 115, + 122, 126, 131, 136, 137, 147, 149, 153, 155, 159, + 163, 169, 173, 177, 181, 185, 189, 195, 198, 200, + 202, 206, 212, 216, 220, 224, 228, 232, 236, 240, + 244, 248, 252, 256, 262, 267, 271, 274, 276, 278, + 282, 286, 288, 292, 293, 295, 299, 301, 303, 305, + 307, 310, 313, 315, 318, 320, 323, 324, 329, 331, + 334, 339, 344, 349, 352, 358, 361, 363, 365, 367, + 370, 373, 376, 377, 378, 388, 392, 395, 397, 402, + 405, 409, 412, 415, 419, 422, 425, 426, 430, 433, + 435, 438, 440, 442, 447, 451, 455, 459, 463, 467, + 471, 475, 478, 481, 484, 488, 493, 495, 499, 504, + 507, 510, 513, 516, 519, 524, 528, 531, 533, 540, + 547, 551, 558, 565, 567, 576, 585, 592, 597, 599, + 606, 613, 622, 631, 640, 647, 649, 651, 656, 658, + 661, 662, 664, 668, 670, 672, 674 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int16 yyrhs[] = +{ + 115, 0, -1, 132, -1, 1, -1, 36, -1, 116, + 10, -1, 37, -1, 117, 10, -1, 9, -1, 118, + 10, -1, 52, -1, 119, 10, -1, 80, -1, 120, + 10, -1, -1, 54, 12, 131, 14, 129, 139, 14, + 129, 131, 149, 122, 152, -1, -1, 54, 12, 131, + 14, 14, 129, 131, 149, 123, 152, -1, -1, 54, + 12, 161, 45, 161, 149, 124, 152, -1, 87, -1, + 90, -1, 58, 12, 139, 149, -1, 11, -1, 127, + 10, -1, 10, -1, 128, 10, -1, -1, 128, -1, + -1, 144, -1, -1, 150, -1, 130, -1, 130, 136, + 130, -1, 139, -1, 133, -1, 133, 127, 156, 17, + -1, 133, 9, 133, -1, 133, 9, 133, 127, 156, + 17, -1, 127, 156, 17, -1, 7, 127, 156, 17, + -1, 8, 127, 156, 17, -1, -1, 55, 125, 12, + 160, 149, 135, 127, 156, 17, -1, 134, -1, 136, + 130, 134, -1, 139, -1, 137, 118, 139, -1, 159, + 70, 138, -1, 138, 94, 138, 95, 138, -1, 138, + 117, 138, -1, 138, 116, 138, -1, 138, 23, 147, + -1, 138, 23, 138, -1, 138, 45, 161, -1, 12, + 140, 16, 45, 161, -1, 138, 158, -1, 146, -1, + 158, -1, 159, 70, 139, -1, 139, 94, 139, 95, + 139, -1, 139, 117, 139, -1, 139, 116, 139, -1, + 139, 39, 139, -1, 139, 40, 139, -1, 139, 41, + 139, -1, 139, 42, 139, -1, 139, 43, 139, -1, + 139, 44, 139, -1, 139, 23, 147, -1, 139, 23, + 139, -1, 139, 45, 161, -1, 12, 140, 16, 45, + 161, -1, 139, 13, 96, 159, -1, 139, 13, 96, + -1, 139, 158, -1, 146, -1, 158, -1, 139, 118, + 139, -1, 140, 118, 139, -1, 138, -1, 141, 118, + 138, -1, -1, 141, -1, 12, 140, 16, -1, 77, + -1, 78, -1, 10, -1, 14, -1, 144, 10, -1, + 144, 14, -1, 17, -1, 145, 10, -1, 147, -1, + 108, 146, -1, -1, 15, 148, 93, 15, -1, 16, + -1, 149, 10, -1, 143, 142, 13, 158, -1, 143, + 142, 38, 158, -1, 143, 142, 41, 158, -1, 143, + 142, -1, 51, 161, 18, 137, 19, -1, 51, 161, + -1, 139, -1, 1, -1, 128, -1, 14, 129, -1, + 48, 151, -1, 50, 151, -1, -1, -1, 119, 153, + 152, 154, 100, 12, 139, 16, 151, -1, 53, 139, + 151, -1, 53, 151, -1, 121, -1, 126, 152, 120, + 152, -1, 126, 152, -1, 127, 156, 145, -1, 62, + 151, -1, 63, 151, -1, 99, 139, 151, -1, 99, + 151, -1, 150, 151, -1, -1, 162, 155, 152, -1, + 14, 129, -1, 152, -1, 156, 152, -1, 56, -1, + 57, -1, 158, 15, 70, 158, -1, 158, 102, 158, + -1, 158, 103, 158, -1, 158, 104, 158, -1, 158, + 15, 158, -1, 146, 15, 158, -1, 158, 105, 158, + -1, 158, 109, 158, -1, 103, 158, -1, 102, 158, + -1, 108, 158, -1, 47, 12, 16, -1, 47, 12, + 137, 16, -1, 47, -1, 90, 12, 16, -1, 90, + 12, 137, 16, -1, 49, 158, -1, 111, 159, -1, + 110, 159, -1, 159, 111, -1, 159, 110, -1, 96, + 159, 43, 158, -1, 96, 43, 158, -1, 96, 159, + -1, 96, -1, 59, 12, 139, 118, 139, 16, -1, + 59, 12, 139, 118, 147, 16, -1, 12, 139, 16, + -1, 61, 12, 139, 118, 147, 16, -1, 61, 12, + 139, 118, 139, 16, -1, 91, -1, 98, 12, 139, + 118, 161, 118, 139, 16, -1, 98, 12, 139, 118, + 161, 118, 147, 16, -1, 98, 12, 139, 118, 161, + 16, -1, 79, 12, 137, 16, -1, 92, -1, 157, + 12, 147, 118, 139, 16, -1, 157, 12, 139, 118, + 139, 16, -1, 157, 12, 147, 118, 139, 118, 159, + 16, -1, 157, 12, 139, 118, 139, 118, 159, 16, + -1, 97, 12, 139, 118, 139, 118, 139, 16, -1, + 97, 12, 139, 118, 139, 16, -1, 159, -1, 161, + -1, 161, 18, 137, 19, -1, 88, -1, 112, 158, + -1, -1, 87, -1, 160, 118, 87, -1, 87, -1, + 46, -1, 89, -1, 100, 12, 139, 149, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 98, 98, 100, 104, 104, 108, 108, 112, 112, + 116, 116, 120, 120, 124, 124, 126, 126, 128, 128, + 133, 134, 138, 142, 142, 146, 146, 150, 151, 155, + 156, 161, 162, 166, 167, 171, 175, 176, 177, 178, + 179, 180, 182, 184, 184, 189, 190, 194, 195, 199, + 200, 202, 204, 206, 207, 212, 213, 214, 215, 216, + 220, 221, 223, 225, 227, 228, 229, 230, 231, 232, + 233, 234, 239, 240, 241, 244, 247, 248, 249, 253, + 254, 258, 259, 263, 264, 265, 269, 269, 273, 273, + 273, 273, 277, 277, 281, 283, 287, 287, 291, 291, + 295, 298, 301, 304, 305, 306, 307, 308, 312, 313, + 317, 319, 321, 321, 321, 323, 324, 325, 326, 327, + 328, 329, 332, 335, 336, 337, 338, 338, 339, 343, + 344, 348, 348, 352, 353, 354, 355, 356, 357, 358, + 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, + 369, 370, 371, 372, 373, 374, 375, 376, 377, 379, + 382, 383, 385, 390, 391, 393, 395, 397, 398, 399, + 401, 406, 408, 413, 415, 417, 421, 422, 423, 424, + 428, 429, 430, 436, 437, 438, 443 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "FIRSTTOKEN", "PROGRAM", "PASTAT", + "PASTAT2", "XBEGIN", "XEND", "','", "NL", "'{'", "'('", "'|'", "';'", + "'/'", "')'", "'}'", "'['", "']'", "ARRAY", "MATCH", "NOTMATCH", + "MATCHOP", "FINAL", "DOT", "ALL", "CCL", "NCCL", "CHAR", "OR", "STAR", + "QUEST", "PLUS", "EMPTYRE", "IGNORE_PRIOR_ATOM", "AND", "BOR", "APPEND", + "EQ", "GE", "GT", "LE", "LT", "NE", "IN", "ARG", "BLTIN", "BREAK", + "CLOSE", "CONTINUE", "DELETE", "DO", "EXIT", "FOR", "FUNC", "SUB", + "GSUB", "IF", "INDEX", "LSUBSTR", "MATCHFCN", "NEXT", "NEXTFILE", "ADD", + "MINUS", "MULT", "DIVIDE", "MOD", "ASSIGN", "ASGNOP", "ADDEQ", "SUBEQ", + "MULTEQ", "DIVEQ", "MODEQ", "POWEQ", "PRINT", "PRINTF", "SPRINTF", + "ELSE", "INTEST", "CONDEXPR", "POSTINCR", "PREINCR", "POSTDECR", + "PREDECR", "VAR", "IVAR", "VARNF", "CALL", "NUMBER", "STRING", "REGEXPR", + "'?'", "':'", "GETLINE", "SUBSTR", "SPLIT", "RETURN", "WHILE", "CAT", + "'+'", "'-'", "'*'", "'%'", "UPLUS", "UMINUS", "NOT", "POWER", "INCR", + "DECR", "INDIRECT", "LASTTOKEN", "$accept", "program", "and", "bor", + "comma", "do", "else", "for", "@1", "@2", "@3", "funcname", "if", + "lbrace", "nl", "opt_nl", "opt_pst", "opt_simple_stmt", "pas", "pa_pat", + "pa_stat", "@4", "pa_stats", "patlist", "ppattern", "pattern", "plist", + "pplist", "prarg", "print", "pst", "rbrace", "re", "reg_expr", "@5", + "rparen", "simple_stmt", "st", "stmt", "@6", "@7", "@8", "stmtlist", + "subop", "term", "var", "varlist", "varname", "while", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 44, + 264, 123, 40, 124, 59, 47, 41, 125, 91, 93, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, + 335, 336, 337, 338, 63, 58, 339, 340, 341, 342, + 343, 344, 43, 45, 42, 37, 345, 346, 347, 348, + 349, 350, 351, 352 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 114, 115, 115, 116, 116, 117, 117, 118, 118, + 119, 119, 120, 120, 122, 121, 123, 121, 124, 121, + 125, 125, 126, 127, 127, 128, 128, 129, 129, 130, + 130, 131, 131, 132, 132, 133, 134, 134, 134, 134, + 134, 134, 134, 135, 134, 136, 136, 137, 137, 138, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 140, + 140, 141, 141, 142, 142, 142, 143, 143, 144, 144, + 144, 144, 145, 145, 146, 146, 148, 147, 149, 149, + 150, 150, 150, 150, 150, 150, 150, 150, 151, 151, + 152, 152, 153, 154, 152, 152, 152, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 155, 152, 152, 156, + 156, 157, 157, 158, 158, 158, 158, 158, 158, 158, + 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, + 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, + 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, + 158, 158, 158, 158, 158, 158, 159, 159, 159, 159, + 160, 160, 160, 161, 161, 161, 162 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 1, 1, 1, 2, 1, 2, 1, 2, + 1, 2, 1, 2, 0, 12, 0, 10, 0, 8, + 1, 1, 4, 1, 2, 1, 2, 0, 1, 0, + 1, 0, 1, 1, 3, 1, 1, 4, 3, 6, + 3, 4, 4, 0, 9, 1, 3, 1, 3, 3, + 5, 3, 3, 3, 3, 3, 5, 2, 1, 1, + 3, 5, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 5, 4, 3, 2, 1, 1, 3, + 3, 1, 3, 0, 1, 3, 1, 1, 1, 1, + 2, 2, 1, 2, 1, 2, 0, 4, 1, 2, + 4, 4, 4, 2, 5, 2, 1, 1, 1, 2, + 2, 2, 0, 0, 9, 3, 2, 1, 4, 2, + 3, 2, 2, 3, 2, 2, 0, 3, 2, 1, + 2, 1, 1, 4, 3, 3, 3, 3, 3, 3, + 3, 2, 2, 2, 3, 4, 1, 3, 4, 2, + 2, 2, 2, 2, 4, 3, 2, 1, 6, 6, + 3, 6, 6, 1, 8, 8, 6, 4, 1, 6, + 6, 8, 8, 8, 6, 1, 1, 4, 1, 2, + 0, 1, 3, 1, 1, 1, 4 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 0, 3, 88, 89, 0, 33, 2, 30, 1, 0, + 0, 23, 0, 96, 184, 146, 0, 0, 131, 132, + 0, 0, 0, 183, 178, 185, 0, 163, 168, 157, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, + 45, 29, 35, 77, 94, 0, 78, 175, 176, 90, + 91, 0, 0, 0, 0, 0, 0, 0, 0, 149, + 175, 20, 21, 0, 0, 0, 0, 0, 0, 156, + 0, 0, 142, 141, 95, 143, 151, 150, 179, 107, + 24, 27, 0, 0, 0, 10, 0, 0, 0, 0, + 0, 86, 87, 0, 0, 112, 117, 0, 0, 106, + 83, 0, 129, 0, 126, 0, 0, 34, 0, 0, + 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 76, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 153, 152, 0, 0, 0, 8, 160, 0, + 0, 0, 0, 144, 0, 47, 0, 180, 0, 0, + 0, 147, 0, 155, 0, 0, 0, 25, 28, 128, + 27, 108, 110, 111, 105, 0, 116, 0, 0, 121, + 122, 0, 124, 0, 11, 0, 119, 0, 0, 81, + 84, 103, 58, 59, 175, 125, 40, 130, 0, 38, + 0, 46, 75, 71, 70, 64, 65, 66, 67, 68, + 69, 72, 0, 5, 63, 7, 62, 138, 0, 94, + 0, 137, 134, 135, 136, 139, 140, 60, 0, 41, + 42, 9, 79, 0, 80, 97, 145, 0, 181, 0, + 0, 0, 167, 148, 154, 0, 0, 26, 109, 0, + 115, 0, 32, 176, 0, 123, 0, 113, 12, 0, + 92, 120, 0, 0, 0, 0, 0, 0, 57, 0, + 0, 0, 0, 0, 127, 0, 37, 74, 0, 0, + 0, 133, 177, 73, 48, 98, 0, 43, 0, 94, + 0, 94, 0, 0, 0, 27, 0, 22, 186, 0, + 13, 118, 93, 85, 0, 54, 53, 55, 0, 52, + 51, 82, 100, 101, 102, 49, 0, 61, 0, 0, + 182, 99, 0, 158, 159, 162, 161, 174, 0, 166, + 0, 104, 27, 0, 0, 0, 0, 0, 0, 39, + 170, 0, 169, 0, 0, 0, 0, 94, 0, 0, + 18, 0, 56, 0, 50, 0, 0, 0, 173, 164, + 165, 0, 27, 0, 0, 172, 171, 44, 16, 0, + 19, 0, 0, 0, 114, 17, 14, 0, 15 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 4, 120, 121, 227, 95, 249, 96, 367, 362, + 353, 63, 97, 98, 161, 159, 5, 241, 6, 39, + 40, 312, 41, 144, 179, 99, 54, 180, 181, 100, + 7, 251, 43, 44, 55, 277, 101, 162, 102, 175, + 289, 188, 103, 45, 46, 47, 229, 48, 104 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -327 +static const yytype_int16 yypact[] = +{ + 744, -327, -327, -327, 48, 1663, -327, 30, -327, 51, + 51, -327, 4501, -327, -327, 45, 4559, -55, -327, -327, + 56, 59, 65, -327, -327, -327, 94, -327, -327, 164, + 108, 111, 4559, 4559, 4559, 185, 185, 4559, 847, 79, + -327, 77, 3663, 116, -327, 113, -7, -32, 118, -327, + -327, 847, 847, 1767, 106, -30, 4267, 4501, 116, -7, + -87, -327, -327, 122, 4501, 4501, 4501, 4325, 4559, 99, + 4501, 4501, 37, 37, -327, 37, -327, -327, -327, -327, + -327, 138, 91, 91, -40, -327, 2332, 142, 147, 91, + 91, -327, -327, 2332, 158, 163, -327, 1469, 847, 3663, + 4617, 91, -327, 916, -327, 4501, 847, 1663, 95, 4501, + -327, -327, 4501, 4501, 4501, 4501, 4501, 4501, -40, 4501, + 2390, 2448, -7, 4559, 4501, 4383, 4559, 4559, 4559, 4559, + 4559, 4501, -327, -327, 4501, 985, 1054, -327, -327, 2506, + 130, 2506, 172, -327, 110, 3663, 2781, 112, 2171, 2171, + 128, -327, 149, -7, 4559, 2171, 2171, -327, 190, -327, + 138, 190, -327, -327, 177, 2274, -327, 1537, 4501, -327, + -327, 2274, -327, 4501, -327, 1469, 123, 1123, 4501, 4079, + 195, 15, 116, -7, -29, -327, -327, -327, 1469, 51, + 1192, -327, 185, 3939, -327, 3939, 3939, 3939, 3939, 3939, + 3939, -327, 2873, -327, 3847, -327, 3755, 37, 2171, 195, + 4559, 37, -1, -1, 37, 37, 37, 3663, 46, -327, + -327, -327, 3663, -40, 3663, -327, -327, 2506, -327, 155, + 2506, 2506, -327, -327, -7, 2506, 54, -327, -327, 4501, + -327, 210, -327, 16, 2974, -327, 2974, -327, -327, 1262, + -327, 215, 169, 4675, -40, 4675, 2564, 2622, -7, 2680, + 4559, 4559, 4559, 4675, -327, 847, -327, -327, 4501, 2506, + 2506, -7, -327, -327, 3663, -327, 12, 216, 3075, 213, + 3176, 217, 1871, 196, 66, 146, -40, 216, 216, 135, + -327, -327, -327, 187, 4501, 4443, -327, -327, 4009, 4209, + 4149, 4079, -7, -7, -7, 4079, 1331, 3663, 1975, 2079, + -327, -327, 51, -327, -327, -327, -327, -327, 2506, -327, + 2506, -327, 138, 4501, 220, 226, -40, 211, 4675, -327, + -327, 5, -327, 5, 847, 3277, 3378, 238, 1605, 3470, + 216, 4501, -327, 187, 4079, 239, 241, 1400, -327, -327, + -327, 220, 138, 1469, 3571, -327, -327, -327, 216, 1605, + -327, 91, 1469, 220, -327, -327, 216, 1469, -327 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -327, -327, -148, 152, 13, -327, -327, -327, -327, -327, + -327, -327, -327, 20, -74, -150, 218, -326, -327, 156, + 153, -327, -327, -65, -183, 550, -173, -327, -327, -327, + -327, -327, 181, -64, -327, -235, -140, -47, 395, -327, + -327, -327, -48, -327, 378, -16, -327, 132, -327 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -32 +static const yytype_int16 yytable[] = +{ + 60, 150, 152, 135, 136, 252, 14, 158, 125, 287, + 238, 288, 351, 69, 125, 221, 60, 60, 60, 76, + 77, 60, 221, 132, 133, 38, 60, 242, 260, 51, + 52, 256, 61, 363, 134, 62, 163, 60, 131, 166, + 49, 263, 169, 170, 50, 194, 172, 23, 8, 25, + 177, 14, 60, 261, 185, 137, 262, 56, 190, 106, + 209, 286, 11, 142, 221, 272, 139, 141, 64, 218, + 295, 65, 298, 299, 300, 137, 301, 66, 132, 133, + 305, 132, 133, 60, 184, 321, 158, 2, 105, 340, + 11, 3, 23, 24, 25, 126, 127, 128, 129, 310, + 14, 157, 130, 128, 129, 160, 67, 60, 130, 60, + 60, 60, 60, 60, 60, 137, 358, 37, 240, 137, + 70, 327, 140, 71, 245, 124, 226, 38, 366, 60, + 60, 123, 60, 60, 147, 323, 134, 137, 60, 60, + 60, 23, 154, 25, 232, 344, 130, 256, 157, 60, + 256, 256, 256, 256, 167, 60, 157, 256, 137, 168, + 322, 230, 231, 60, 137, 233, 279, 281, 235, 236, + 173, 275, 338, 174, 284, 223, 267, 60, 137, 60, + 60, 60, 60, 60, 60, 293, 60, 225, 60, 296, + 60, 192, 60, 259, 60, 239, 256, 58, 242, 228, + 237, 60, 359, 248, 137, 137, 60, 68, 60, 265, + 14, 158, 319, 58, 58, 74, 164, 306, 58, 242, + 137, 269, 270, 58, 285, 292, 311, 343, 60, 314, + 60, 14, 326, 316, 58, 325, 275, 184, 341, 184, + 184, 184, 276, 184, 60, 60, 60, 184, 158, 58, + 201, 23, 24, 25, 350, 355, 337, 356, 60, 107, + 191, 189, 60, 0, 60, 141, 60, 0, 0, 0, + 0, 0, 23, 24, 25, 0, 37, 0, 158, 60, + 58, 182, 60, 60, 60, 60, 347, 0, 0, 60, + 0, 60, 60, 60, 0, 318, 320, 37, 0, 243, + 0, 0, 0, 0, 58, 0, 58, 58, 58, 58, + 58, 58, 184, 0, 364, 345, 0, 346, 0, 60, + 60, 331, 333, 60, 0, 0, 58, 58, 60, 58, + 58, 257, 334, 0, 0, 58, 58, 58, 60, 0, + 141, 0, 0, 0, 0, 0, 58, 0, 0, 0, + 0, 0, 58, 0, 0, 273, 0, 0, 0, 0, + 58, 0, 0, 0, 0, 0, 0, 0, 283, 0, + 0, 0, 0, 0, 58, 0, 58, 58, 58, 58, + 58, 58, 0, 58, 0, 58, 297, 58, 0, 58, + 0, 58, 0, 0, 59, 0, 0, 0, 58, 0, + 0, 0, 0, 58, 0, 58, 0, 0, 0, 0, + 72, 73, 75, 0, 0, 78, 0, 0, 324, 0, + 122, 0, 0, 0, 0, 58, 0, 58, 0, 0, + 0, 122, 0, 0, 182, 0, 182, 182, 182, 0, + 182, 58, 58, 58, 182, 0, 153, 257, 0, 0, + 257, 257, 257, 257, 0, 58, 0, 257, 342, 58, + 0, 58, 0, 58, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 58, 122, 183, 58, + 58, 58, 58, 0, 0, 0, 58, 0, 58, 58, + 58, 0, 176, 0, 0, 0, 257, 0, 187, 0, + 0, 207, 0, 211, 212, 213, 214, 215, 216, 182, + 0, 0, 0, 0, 0, 0, 58, 58, 0, 0, + 58, 0, 0, 122, 122, 58, 122, 122, 0, 0, + 187, 187, 234, 122, 122, 58, 0, 0, 0, 0, + 0, 0, 0, 122, 0, 0, 0, 0, 0, 122, + 0, 0, 0, 0, 0, 42, 0, 258, 0, 0, + 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, + 247, 122, 187, 122, 122, 122, 122, 122, 122, 0, + 122, 0, 122, 264, 122, 187, 122, 0, 271, 0, + 0, 0, 0, 0, 0, 122, 0, 0, 0, 0, + 122, 0, 122, 0, 0, 0, 145, 146, 0, 0, + 0, 0, 0, 0, 148, 149, 145, 145, 0, 0, + 155, 156, 122, 0, 122, 0, 0, 0, 0, 0, + 0, 183, 0, 183, 183, 183, 165, 183, 302, 303, + 304, 183, 0, 171, 291, 0, 0, 0, 0, 0, + 0, 0, 122, 0, 0, 42, 122, 42, 122, 193, + 122, 0, 195, 196, 197, 198, 199, 200, 0, 202, + 204, 206, 0, 258, 208, 0, 258, 258, 258, 258, + 0, 217, 0, 258, 145, 122, 122, 122, 0, 222, + 0, 224, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 187, 0, 0, 0, 0, 183, 0, 0, 0, + 0, 0, 0, 122, 122, 0, 0, 122, 244, 0, + 0, 0, 258, 246, 0, 0, 0, 0, 53, 0, + 0, 0, 122, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 187, 0, -29, 1, 0, 0, 360, 0, + 0, -29, -29, 0, 2, -29, -29, 365, 3, -29, + 0, 0, 368, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 274, 0, 0, + 278, 280, 0, 0, 0, 282, 0, 0, 0, 145, + -29, -29, 0, -29, 0, 0, 0, 0, 0, -29, + -29, -29, 0, -29, 0, -29, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 307, 308, + 309, 0, 0, -29, 0, 0, 0, 0, 0, 0, + 0, -29, -29, -29, -29, -29, -29, 0, 0, 0, + -29, -29, -29, 0, 53, 0, -29, -29, 79, 0, + 0, 0, -29, 0, -29, -29, -29, 80, 11, 12, + 0, 81, 13, 0, 0, 0, 0, 0, 335, 0, + 336, 0, 0, 339, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 354, 0, 14, 15, 82, 16, 83, 84, 85, + 86, 87, 0, 18, 19, 88, 20, 0, 21, 89, + 90, 0, 0, 0, 0, 0, 0, 79, 0, 0, + 0, 0, 0, 0, 91, 92, 22, 11, 12, 0, + 81, 13, 0, 186, 23, 24, 25, 26, 27, 28, + 0, 0, 0, 29, 30, 31, 93, 94, 0, 32, + 33, 0, 0, 0, 0, 34, 0, 35, 36, 37, + 0, 0, 14, 15, 82, 16, 83, 84, 85, 86, + 87, 0, 18, 19, 88, 20, 0, 21, 89, 90, + 0, 0, 0, 0, 0, 0, 79, 0, 0, 0, + 0, 0, 0, 91, 92, 22, 11, 12, 0, 81, + 13, 0, 219, 23, 24, 25, 26, 27, 28, 0, + 0, 0, 29, 30, 31, 93, 94, 0, 32, 33, + 0, 0, 0, 0, 34, 0, 35, 36, 37, 0, + 0, 14, 15, 82, 16, 83, 84, 85, 86, 87, + 0, 18, 19, 88, 20, 0, 21, 89, 90, 0, + 0, 0, 0, 0, 0, 79, 0, 0, 0, 0, + 0, 0, 91, 92, 22, 11, 12, 0, 81, 13, + 0, 220, 23, 24, 25, 26, 27, 28, 0, 0, + 0, 29, 30, 31, 93, 94, 0, 32, 33, 0, + 0, 0, 0, 34, 0, 35, 36, 37, 0, 0, + 14, 15, 82, 16, 83, 84, 85, 86, 87, 0, + 18, 19, 88, 20, 0, 21, 89, 90, 0, 0, + 0, 0, 0, 0, 79, 0, 0, 0, 0, 0, + 0, 91, 92, 22, 11, 12, 0, 81, 13, 0, + 250, 23, 24, 25, 26, 27, 28, 0, 0, 0, + 29, 30, 31, 93, 94, 0, 32, 33, 0, 0, + 0, 0, 34, 0, 35, 36, 37, 0, 0, 14, + 15, 82, 16, 83, 84, 85, 86, 87, 0, 18, + 19, 88, 20, 0, 21, 89, 90, 0, 0, 0, + 0, 0, 0, 79, 0, 0, 0, 0, 0, 0, + 91, 92, 22, 11, 12, 0, 81, 13, 0, 266, + 23, 24, 25, 26, 27, 28, 0, 0, 0, 29, + 30, 31, 93, 94, 0, 32, 33, 0, 0, 0, + 0, 34, 0, 35, 36, 37, 0, 0, 14, 15, + 82, 16, 83, 84, 85, 86, 87, 0, 18, 19, + 88, 20, 0, 21, 89, 90, 0, 0, 0, 0, + 0, 0, 0, 79, 0, 0, 0, 0, 0, 91, + 92, 22, 290, 11, 12, 0, 81, 13, 0, 23, + 24, 25, 26, 27, 28, 0, 0, 0, 29, 30, + 31, 93, 94, 0, 32, 33, 0, 0, 0, 0, + 34, 0, 35, 36, 37, 0, 0, 0, 14, 15, + 82, 16, 83, 84, 85, 86, 87, 0, 18, 19, + 88, 20, 0, 21, 89, 90, 0, 0, 0, 0, + 0, 0, 79, 0, 0, 0, 0, 0, 0, 91, + 92, 22, 11, 12, 0, 81, 13, 0, 329, 23, + 24, 25, 26, 27, 28, 0, 0, 0, 29, 30, + 31, 93, 94, 0, 32, 33, 0, 0, 0, 0, + 34, 0, 35, 36, 37, 0, 0, 14, 15, 82, + 16, 83, 84, 85, 86, 87, 0, 18, 19, 88, + 20, 0, 21, 89, 90, 0, 0, 0, 0, 0, + 0, 79, 0, 0, 0, 0, 0, 0, 91, 92, + 22, 11, 12, 0, 81, 13, 0, 357, 23, 24, + 25, 26, 27, 28, 0, 0, 0, 29, 30, 31, + 93, 94, 0, 32, 33, 0, 0, 0, 0, 34, + 0, 35, 36, 37, 0, 0, 14, 15, 82, 16, + 83, 84, 85, 86, 87, 0, 18, 19, 88, 20, + 0, 21, 89, 90, 0, 0, 0, 0, 0, 0, + 79, 0, 0, 0, 0, 0, 0, 91, 92, 22, + 11, 12, 0, 81, 13, 0, 0, 23, 24, 25, + 26, 27, 28, 0, 0, 0, 29, 30, 31, 93, + 94, 0, 32, 33, 0, 0, 0, 0, 34, 0, + 35, 36, 37, 0, 0, 14, 15, 82, 16, 83, + 84, 85, 86, 87, 0, 18, 19, 88, 20, 0, + 21, 89, 90, 0, 0, 0, 0, 0, 79, 0, + 0, 0, 0, 0, 0, 0, 91, 92, 22, 12, + 0, -31, 13, 0, 0, 0, 23, 24, 25, 26, + 27, 28, 0, 0, 0, 29, 30, 31, 93, 94, + 0, 32, 33, 0, 0, 0, 0, 34, 0, 35, + 36, 37, 0, 14, 15, 0, 16, 0, 84, 0, + 0, 0, 0, 18, 19, 0, 20, 0, 21, 0, + 0, 0, 0, 0, 0, 0, 79, 0, 0, 0, + 0, 0, 0, 0, 91, 92, 22, 12, 0, 0, + 13, -31, 0, 0, 23, 24, 25, 26, 27, 28, + 0, 0, 0, 29, 30, 31, 0, 0, 0, 32, + 33, 0, 0, 0, 0, 34, 0, 35, 36, 37, + 0, 14, 15, 0, 16, 0, 84, 0, 0, 0, + 0, 18, 19, 0, 20, 0, 21, 0, 0, 0, + 9, 10, 0, 0, 11, 12, 0, 0, 13, 0, + 0, 0, 91, 92, 22, 0, 0, 0, 0, 0, + 0, 0, 23, 24, 25, 26, 27, 28, 0, 0, + 0, 29, 30, 31, 0, 0, 0, 32, 33, 14, + 15, 0, 16, 34, 0, 35, 36, 37, 17, 18, + 19, 0, 20, 0, 21, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 22, 0, 0, 0, 0, 0, 0, 0, + 23, 24, 25, 26, 27, 28, 0, 0, 0, 29, + 30, 31, 0, 0, 0, 32, 33, 0, 0, 0, + 0, 34, 0, 35, 36, 37, 137, 0, 0, 57, + 108, 0, 13, 138, 0, 0, 0, 0, 0, 0, + 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 110, 111, 0, 112, 113, 114, 115, + 116, 117, 118, 14, 15, 0, 16, 0, 0, 0, + 0, 0, 0, 18, 19, 0, 20, 0, 21, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, + 0, 0, 0, 0, 23, 24, 25, 26, 27, 28, + 0, 119, 0, 29, 30, 31, 0, 0, 0, 32, + 33, 0, 0, 0, 0, 34, 0, 35, 36, 37, + 137, 0, 0, 57, 108, 0, 13, 317, 0, 0, + 0, 0, 0, 0, 109, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 110, 111, 0, + 112, 113, 114, 115, 116, 117, 118, 14, 15, 0, + 16, 0, 0, 0, 0, 0, 0, 18, 19, 0, + 20, 0, 21, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 22, 0, 0, 0, 0, 0, 0, 0, 23, 24, + 25, 26, 27, 28, 0, 119, 0, 29, 30, 31, + 0, 0, 0, 32, 33, 0, 0, 0, 0, 34, + 0, 35, 36, 37, 137, 0, 0, 57, 108, 0, + 13, 330, 0, 0, 0, 0, 0, 0, 109, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 110, 111, 0, 112, 113, 114, 115, 116, 117, + 118, 14, 15, 0, 16, 0, 0, 0, 0, 0, + 0, 18, 19, 0, 20, 0, 21, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, + 0, 0, 23, 24, 25, 26, 27, 28, 0, 119, + 0, 29, 30, 31, 0, 0, 0, 32, 33, 0, + 0, 0, 0, 34, 0, 35, 36, 37, 137, 0, + 0, 57, 108, 0, 13, 332, 0, 0, 0, 0, + 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 110, 111, 0, 112, 113, + 114, 115, 116, 117, 118, 14, 15, 0, 16, 0, + 0, 0, 0, 0, 0, 18, 19, 0, 20, 0, + 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, + 0, 0, 0, 0, 0, 0, 23, 24, 25, 26, + 27, 28, 0, 119, 0, 29, 30, 31, 0, 0, + 137, 32, 33, 57, 108, 0, 13, 34, 0, 35, + 36, 37, 0, 0, 109, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 110, 111, 0, + 112, 113, 114, 115, 116, 117, 118, 14, 15, 0, + 16, 0, 0, 0, 0, 0, 0, 18, 19, 0, + 20, 0, 21, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 22, 0, 0, 0, 0, 0, 0, 0, 23, 24, + 25, 26, 27, 28, 0, 119, 0, 29, 30, 31, + 0, 0, 0, 32, 33, 0, 0, 0, 0, 34, + 0, 35, 36, 37, 157, 0, 57, 108, 160, 13, + 0, 0, 0, 0, 0, 0, 0, 109, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 110, 111, 0, 112, 113, 114, 115, 116, 117, 118, + 14, 15, 0, 16, 0, 0, 0, 0, 0, 0, + 18, 19, 0, 20, 0, 21, 0, 0, 0, 0, + 0, 0, 157, 0, 12, 0, 160, 13, 0, 0, + 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, + 0, 23, 24, 25, 26, 27, 28, 0, 119, 0, + 29, 30, 31, 0, 0, 0, 32, 33, 14, 15, + 0, 16, 34, 0, 35, 36, 37, 0, 18, 19, + 0, 20, 0, 21, 0, 0, 0, 0, 0, 0, + 203, 0, 12, 0, 0, 13, 0, 0, 0, 0, + 0, 22, 0, 0, 0, 0, 0, 0, 0, 23, + 24, 25, 26, 27, 28, 0, 0, 0, 29, 30, + 31, 0, 0, 0, 32, 33, 14, 15, 0, 16, + 34, 0, 35, 36, 37, 0, 18, 19, 0, 20, + 0, 21, 0, 0, 0, 0, 0, 0, 205, 0, + 12, 0, 0, 13, 0, 0, 0, 0, 0, 22, + 0, 0, 0, 0, 0, 0, 0, 23, 24, 25, + 26, 27, 28, 0, 0, 0, 29, 30, 31, 0, + 0, 0, 32, 33, 14, 15, 0, 16, 34, 0, + 35, 36, 37, 0, 18, 19, 0, 20, 0, 21, + 0, 0, 0, 0, 0, 0, 221, 0, 12, 0, + 0, 13, 0, 0, 0, 0, 0, 22, 0, 0, + 0, 0, 0, 0, 0, 23, 24, 25, 26, 27, + 28, 0, 0, 0, 29, 30, 31, 0, 0, 0, + 32, 33, 14, 15, 0, 16, 34, 0, 35, 36, + 37, 0, 18, 19, 0, 20, 0, 21, 0, 0, + 0, 0, 0, 0, 203, 0, 294, 0, 0, 13, + 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, + 0, 0, 0, 23, 24, 25, 26, 27, 28, 0, + 0, 0, 29, 30, 31, 0, 0, 0, 32, 33, + 14, 15, 0, 16, 34, 0, 35, 36, 37, 0, + 18, 19, 0, 20, 0, 21, 0, 0, 0, 0, + 0, 0, 205, 0, 294, 0, 0, 13, 0, 0, + 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, + 0, 23, 24, 25, 26, 27, 28, 0, 0, 0, + 29, 30, 31, 0, 0, 0, 32, 33, 14, 15, + 0, 16, 34, 0, 35, 36, 37, 0, 18, 19, + 0, 20, 0, 21, 0, 0, 0, 0, 0, 0, + 221, 0, 294, 0, 0, 13, 0, 0, 0, 0, + 0, 22, 0, 0, 0, 0, 0, 0, 0, 23, + 24, 25, 26, 27, 28, 0, 0, 0, 29, 30, + 31, 0, 0, 0, 32, 33, 14, 15, 0, 16, + 34, 0, 35, 36, 37, 0, 18, 19, 0, 20, + 0, 21, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, + 0, 0, 0, 0, 0, 0, 0, 23, 24, 25, + 26, 27, 28, 0, 0, 0, 29, 30, 31, 0, + 0, 0, 32, 33, 0, 0, 0, 0, 34, 0, + 35, 36, 37, 57, 108, 0, 13, 138, 0, 0, + 0, 0, 0, 0, 109, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 110, 111, 0, + 112, 113, 114, 115, 116, 117, 118, 14, 15, 0, + 16, 0, 0, 0, 0, 0, 0, 18, 19, 0, + 20, 0, 21, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 22, 0, 0, 0, 0, 0, 0, 0, 23, 24, + 25, 26, 27, 28, 0, 119, 0, 29, 30, 31, + 0, 0, 0, 32, 33, 57, 108, 0, 13, 34, + 0, 35, 36, 37, 0, 0, 109, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, + 111, 0, 112, 113, 114, 115, 116, 117, 118, 14, + 15, 0, 16, 0, 0, 0, 0, 0, 0, 18, + 19, 0, 20, 0, 21, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 22, 0, 0, 0, 0, 0, 0, 0, + 23, 24, 25, 26, 27, 28, 0, 119, 268, 29, + 30, 31, 0, 0, 0, 32, 33, 0, 0, 0, + 0, 34, 0, 35, 36, 37, 57, 108, 0, 13, + 275, 0, 0, 0, 0, 0, 0, 109, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 110, 111, 0, 112, 113, 114, 115, 116, 117, 118, + 14, 15, 0, 16, 0, 0, 0, 0, 0, 0, + 18, 19, 0, 20, 0, 21, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, + 0, 23, 24, 25, 26, 27, 28, 0, 119, 0, + 29, 30, 31, 0, 0, 0, 32, 33, 0, 0, + 0, 0, 34, 0, 35, 36, 37, 57, 108, 0, + 13, 313, 0, 0, 0, 0, 0, 0, 109, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 110, 111, 0, 112, 113, 114, 115, 116, 117, + 118, 14, 15, 0, 16, 0, 0, 0, 0, 0, + 0, 18, 19, 0, 20, 0, 21, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, + 0, 0, 23, 24, 25, 26, 27, 28, 0, 119, + 0, 29, 30, 31, 0, 0, 0, 32, 33, 0, + 0, 0, 0, 34, 0, 35, 36, 37, 57, 108, + 0, 13, 315, 0, 0, 0, 0, 0, 0, 109, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 110, 111, 0, 112, 113, 114, 115, 116, + 117, 118, 14, 15, 0, 16, 0, 0, 0, 0, + 0, 0, 18, 19, 0, 20, 0, 21, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, + 0, 0, 0, 23, 24, 25, 26, 27, 28, 0, + 119, 0, 29, 30, 31, 0, 0, 0, 32, 33, + 0, 0, 0, 0, 34, 0, 35, 36, 37, 57, + 108, 0, 13, 348, 0, 0, 0, 0, 0, 0, + 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 110, 111, 0, 112, 113, 114, 115, + 116, 117, 118, 14, 15, 0, 16, 0, 0, 0, + 0, 0, 0, 18, 19, 0, 20, 0, 21, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, + 0, 0, 0, 0, 23, 24, 25, 26, 27, 28, + 0, 119, 0, 29, 30, 31, 0, 0, 0, 32, + 33, 0, 0, 0, 0, 34, 0, 35, 36, 37, + 57, 108, 0, 13, 349, 0, 0, 0, 0, 0, + 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 110, 111, 0, 112, 113, 114, + 115, 116, 117, 118, 14, 15, 0, 16, 0, 0, + 0, 0, 0, 0, 18, 19, 0, 20, 0, 21, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, + 0, 0, 0, 0, 0, 23, 24, 25, 26, 27, + 28, 0, 119, 0, 29, 30, 31, 0, 0, 0, + 32, 33, 57, 108, 352, 13, 34, 0, 35, 36, + 37, 0, 0, 109, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 110, 111, 0, 112, + 113, 114, 115, 116, 117, 118, 14, 15, 0, 16, + 0, 0, 0, 0, 0, 0, 18, 19, 0, 20, + 0, 21, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, + 0, 0, 0, 0, 0, 0, 0, 23, 24, 25, + 26, 27, 28, 0, 119, 0, 29, 30, 31, 0, + 0, 0, 32, 33, 0, 0, 0, 0, 34, 0, + 35, 36, 37, 57, 108, 0, 13, 361, 0, 0, + 0, 0, 0, 0, 109, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 110, 111, 0, + 112, 113, 114, 115, 116, 117, 118, 14, 15, 0, + 16, 0, 0, 0, 0, 0, 0, 18, 19, 0, + 20, 0, 21, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 22, 0, 0, 0, 0, 0, 0, 0, 23, 24, + 25, 26, 27, 28, 0, 119, 0, 29, 30, 31, + 0, 0, 0, 32, 33, 57, 108, 0, 13, 34, + 0, 35, 36, 37, 0, 0, 109, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, + 111, 0, 112, 113, 114, 115, 116, 117, 118, 14, + 15, 0, 16, 0, 0, 0, 0, 0, 0, 18, + 19, 0, 20, 0, 21, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 22, 0, 0, 0, 0, 0, 0, 0, + 23, 24, 25, 26, 27, 28, 0, 119, 0, 29, + 30, 31, 0, 0, 0, 32, 33, 57, 108, 0, + 13, 34, 0, 35, 36, 37, 0, 0, 109, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 110, 0, 0, 112, 113, 114, 115, 116, 117, + 118, 14, 15, 0, 16, 0, 0, 0, 0, 0, + 0, 18, 19, 0, 20, 0, 21, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, + 0, 0, 23, 24, 25, 26, 27, 28, 0, 0, + 0, 29, 30, 31, 0, 0, 0, 32, 33, 57, + 108, 0, 13, 34, 0, 35, 36, 37, 0, 0, + 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 112, 113, 114, 115, + 116, 117, 118, 14, 15, 0, 16, 0, 0, 0, + 0, 0, 0, 18, 19, 0, 20, 0, 21, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, + 0, 0, 0, 0, 23, 24, 25, 26, 27, 28, + 0, 0, 0, 29, 30, 31, 0, 0, 0, 32, + 33, 57, -32, 0, 13, 34, 0, 35, 36, 37, + 0, 0, -32, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, -32, -32, + -32, -32, -32, -32, -32, 14, 15, 0, 16, 0, + 0, 0, 0, 0, 0, 18, 19, 0, 20, 0, + 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, + 0, 57, 0, 0, 13, 0, 23, 24, 25, 26, + 27, 28, 253, 0, 0, 0, 30, 31, 0, 0, + 0, 32, 33, 0, 0, 110, 111, 34, 0, 35, + 36, 37, 0, 0, 254, 14, 15, 0, 16, 0, + 0, 0, 0, 0, 0, 18, 19, 0, 20, 0, + 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, + 0, 57, 0, 0, 13, 0, 23, 24, 25, 26, + 27, 28, 253, 255, 328, 29, 30, 31, 0, 0, + 0, 32, 33, 0, 0, 110, 111, 34, 0, 35, + 36, 37, 0, 0, 254, 14, 15, 0, 16, 0, + 0, 0, 0, 0, 0, 18, 19, 0, 20, 0, + 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, + 0, 57, 0, 0, 13, 0, 23, 24, 25, 26, + 27, 28, 253, 255, 0, 29, 30, 31, 0, 0, + 0, 32, 33, 0, 0, 110, 0, 34, 0, 35, + 36, 37, 0, 0, 254, 14, 15, 0, 16, 0, + 0, 0, 0, 0, 0, 18, 19, 0, 20, 0, + 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 57, 0, 0, 13, 0, 0, 0, 22, 0, + 0, 0, 253, 0, 0, 0, 23, 24, 25, 26, + 27, 28, 0, 0, 0, 29, 30, 31, 0, 0, + 0, 32, 33, 0, 254, 14, 15, 34, 16, 35, + 36, 37, 0, 0, 0, 18, 19, 0, 20, 0, + 21, 0, 0, 0, 0, 0, 0, 0, 0, 12, + 0, 0, 13, 143, 0, 0, 0, 0, 22, 0, + 0, 0, 0, 0, 0, 0, 23, 24, 25, 26, + 27, 28, 0, 0, 0, 29, 30, 31, 0, 0, + 0, 32, 33, 14, 15, 0, 16, 34, 0, 35, + 36, 37, 0, 18, 19, 0, 20, 0, 21, 0, + 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, + 13, 151, 0, 0, 0, 0, 22, 0, 0, 0, + 0, 0, 0, 0, 23, 24, 25, 26, 27, 28, + 0, 0, 0, 29, 30, 31, 0, 0, 0, 32, + 33, 14, 15, 0, 16, 34, 0, 35, 36, 37, + 0, 18, 19, 0, 20, 0, 21, 0, 0, 0, + 0, 0, 0, 0, 0, 57, 0, 0, 13, 0, + 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, + 0, 0, 23, 24, 25, 26, 27, 28, 0, 0, + 0, 29, 30, 31, 0, 0, 0, 32, 33, 14, + 15, 0, 16, 34, 0, 35, 36, 37, 0, 18, + 19, 0, 20, 0, 21, 0, 0, 0, 0, 0, + 0, 0, 0, 210, 0, 57, 0, 0, 13, 0, + 0, 0, 22, 0, 0, 0, -32, 0, 0, 0, + 23, 24, 25, 26, 27, 28, 0, 0, 0, 29, + 30, 31, 0, 0, 0, 32, 33, 0, -32, 14, + 15, 34, 16, 35, 36, 37, 0, 0, 0, 18, + 19, 0, 20, 0, 21, 0, 0, 0, 0, 0, + 0, 0, 0, 12, 0, 0, 13, 0, 0, 0, + 0, 0, 22, 0, 0, 0, 0, 0, 0, 0, + 23, 24, 25, 26, 27, 28, 0, 0, 0, 0, + 30, 31, 0, 0, 0, 32, 33, 14, 15, 0, + 16, 34, 0, 35, 36, 37, 0, 18, 19, 0, + 20, 0, 21, 0, 0, 0, 0, 0, 0, 0, + 0, 57, 0, 0, 13, 0, 0, 0, 0, 0, + 22, 0, 0, 0, 0, 0, 0, 0, 23, 24, + 25, 26, 27, 28, 0, 0, 0, 29, 30, 31, + 0, 0, 0, 32, 33, 14, 15, 0, 16, 34, + 0, 35, 36, 37, 0, 18, 19, 0, 20, 0, + 21, 0, 0, 0, 0, 0, 0, 0, 0, 178, + 0, 0, 13, 0, 0, 0, 0, 0, 22, 0, + 0, 0, 0, 0, 0, 0, 23, 24, 25, 26, + 27, 28, 0, 0, 0, 29, 30, 31, 0, 0, + 0, 32, 33, 14, 15, 0, 16, 34, 0, 35, + 36, 37, 0, 18, 19, 0, 20, 0, 21, 0, + 0, 0, 0, 0, 0, 0, 0, 294, 0, 0, + 13, 0, 0, 0, 0, 0, 22, 0, 0, 0, + 0, 0, 0, 0, 23, 24, 25, 26, 27, 28, + 0, 0, 0, 29, 30, 31, 0, 0, 0, 32, + 33, 14, 15, 0, 16, 34, 0, 35, 36, 37, + 0, 18, 19, 0, 20, 0, 21, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, + 0, 0, 23, 24, 25, 26, 27, 28, 0, 0, + 0, 29, 30, 31, 0, 0, 0, 32, 33, 0, + 0, 0, 0, 34, 0, 35, 36, 37 +}; + +static const yytype_int16 yycheck[] = +{ + 16, 66, 67, 51, 52, 178, 46, 81, 15, 244, + 160, 246, 338, 29, 15, 10, 32, 33, 34, 35, + 36, 37, 10, 110, 111, 5, 42, 167, 13, 9, + 10, 179, 87, 359, 18, 90, 83, 53, 70, 86, + 10, 70, 89, 90, 14, 109, 93, 87, 0, 89, + 98, 46, 68, 38, 101, 9, 41, 12, 106, 39, + 124, 45, 11, 93, 10, 19, 53, 54, 12, 134, + 253, 12, 255, 256, 257, 9, 259, 12, 110, 111, + 263, 110, 111, 99, 100, 19, 160, 10, 9, 324, + 11, 14, 87, 88, 89, 102, 103, 104, 105, 87, + 46, 10, 109, 104, 105, 14, 12, 123, 109, 125, + 126, 127, 128, 129, 130, 9, 351, 112, 165, 9, + 12, 294, 16, 12, 171, 12, 16, 107, 363, 145, + 146, 15, 148, 149, 12, 285, 18, 9, 154, 155, + 156, 87, 43, 89, 16, 328, 109, 295, 10, 165, + 298, 299, 300, 301, 12, 171, 10, 305, 9, 12, + 14, 148, 149, 179, 9, 16, 230, 231, 155, 156, + 12, 16, 322, 10, 239, 45, 192, 193, 9, 195, + 196, 197, 198, 199, 200, 16, 202, 15, 204, 253, + 206, 96, 208, 180, 210, 18, 344, 16, 338, 87, + 10, 217, 352, 80, 9, 9, 222, 43, 224, 189, + 46, 285, 16, 32, 33, 34, 84, 265, 37, 359, + 9, 208, 209, 42, 14, 10, 10, 16, 244, 16, + 246, 46, 45, 16, 53, 100, 16, 253, 12, 255, + 256, 257, 229, 259, 260, 261, 262, 263, 322, 68, + 118, 87, 88, 89, 16, 16, 320, 16, 274, 41, + 107, 105, 278, -1, 280, 252, 282, -1, -1, -1, + -1, -1, 87, 88, 89, -1, 112, -1, 352, 295, + 99, 100, 298, 299, 300, 301, 334, -1, -1, 305, + -1, 307, 308, 309, -1, 282, 283, 112, -1, 167, + -1, -1, -1, -1, 123, -1, 125, 126, 127, 128, + 129, 130, 328, -1, 361, 331, -1, 333, -1, 335, + 336, 308, 309, 339, -1, -1, 145, 146, 344, 148, + 149, 179, 312, -1, -1, 154, 155, 156, 354, -1, + 327, -1, -1, -1, -1, -1, 165, -1, -1, -1, + -1, -1, 171, -1, -1, 223, -1, -1, -1, -1, + 179, -1, -1, -1, -1, -1, -1, -1, 236, -1, + -1, -1, -1, -1, 193, -1, 195, 196, 197, 198, + 199, 200, -1, 202, -1, 204, 254, 206, -1, 208, + -1, 210, -1, -1, 16, -1, -1, -1, 217, -1, + -1, -1, -1, 222, -1, 224, -1, -1, -1, -1, + 32, 33, 34, -1, -1, 37, -1, -1, 286, -1, + 42, -1, -1, -1, -1, 244, -1, 246, -1, -1, + -1, 53, -1, -1, 253, -1, 255, 256, 257, -1, + 259, 260, 261, 262, 263, -1, 68, 295, -1, -1, + 298, 299, 300, 301, -1, 274, -1, 305, 326, 278, + -1, 280, -1, 282, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 295, 99, 100, 298, + 299, 300, 301, -1, -1, -1, 305, -1, 307, 308, + 309, -1, 97, -1, -1, -1, 344, -1, 103, -1, + -1, 123, -1, 125, 126, 127, 128, 129, 130, 328, + -1, -1, -1, -1, -1, -1, 335, 336, -1, -1, + 339, -1, -1, 145, 146, 344, 148, 149, -1, -1, + 135, 136, 154, 155, 156, 354, -1, -1, -1, -1, + -1, -1, -1, 165, -1, -1, -1, -1, -1, 171, + -1, -1, -1, -1, -1, 5, -1, 179, -1, -1, + -1, -1, 12, -1, -1, -1, -1, -1, -1, -1, + 175, 193, 177, 195, 196, 197, 198, 199, 200, -1, + 202, -1, 204, 188, 206, 190, 208, -1, 210, -1, + -1, -1, -1, -1, -1, 217, -1, -1, -1, -1, + 222, -1, 224, -1, -1, -1, 56, 57, -1, -1, + -1, -1, -1, -1, 64, 65, 66, 67, -1, -1, + 70, 71, 244, -1, 246, -1, -1, -1, -1, -1, + -1, 253, -1, 255, 256, 257, 86, 259, 260, 261, + 262, 263, -1, 93, 249, -1, -1, -1, -1, -1, + -1, -1, 274, -1, -1, 105, 278, 107, 280, 109, + 282, -1, 112, 113, 114, 115, 116, 117, -1, 119, + 120, 121, -1, 295, 124, -1, 298, 299, 300, 301, + -1, 131, -1, 305, 134, 307, 308, 309, -1, 139, + -1, 141, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 306, -1, -1, -1, -1, 328, -1, -1, -1, + -1, -1, -1, 335, 336, -1, -1, 339, 168, -1, + -1, -1, 344, 173, -1, -1, -1, -1, 178, -1, + -1, -1, 354, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 347, -1, 0, 1, -1, -1, 353, -1, + -1, 7, 8, -1, 10, 11, 12, 362, 14, 15, + -1, -1, 367, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 227, -1, -1, + 230, 231, -1, -1, -1, 235, -1, -1, -1, 239, + 46, 47, -1, 49, -1, -1, -1, -1, -1, 55, + 56, 57, -1, 59, -1, 61, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 268, 269, + 270, -1, -1, 79, -1, -1, -1, -1, -1, -1, + -1, 87, 88, 89, 90, 91, 92, -1, -1, -1, + 96, 97, 98, -1, 294, -1, 102, 103, 1, -1, + -1, -1, 108, -1, 110, 111, 112, 10, 11, 12, + -1, 14, 15, -1, -1, -1, -1, -1, 318, -1, + 320, -1, -1, 323, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 341, -1, 46, 47, 48, 49, 50, 51, 52, + 53, 54, -1, 56, 57, 58, 59, -1, 61, 62, + 63, -1, -1, -1, -1, -1, -1, 1, -1, -1, + -1, -1, -1, -1, 77, 78, 79, 11, 12, -1, + 14, 15, -1, 17, 87, 88, 89, 90, 91, 92, + -1, -1, -1, 96, 97, 98, 99, 100, -1, 102, + 103, -1, -1, -1, -1, 108, -1, 110, 111, 112, + -1, -1, 46, 47, 48, 49, 50, 51, 52, 53, + 54, -1, 56, 57, 58, 59, -1, 61, 62, 63, + -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + -1, -1, -1, 77, 78, 79, 11, 12, -1, 14, + 15, -1, 17, 87, 88, 89, 90, 91, 92, -1, + -1, -1, 96, 97, 98, 99, 100, -1, 102, 103, + -1, -1, -1, -1, 108, -1, 110, 111, 112, -1, + -1, 46, 47, 48, 49, 50, 51, 52, 53, 54, + -1, 56, 57, 58, 59, -1, 61, 62, 63, -1, + -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, + -1, -1, 77, 78, 79, 11, 12, -1, 14, 15, + -1, 17, 87, 88, 89, 90, 91, 92, -1, -1, + -1, 96, 97, 98, 99, 100, -1, 102, 103, -1, + -1, -1, -1, 108, -1, 110, 111, 112, -1, -1, + 46, 47, 48, 49, 50, 51, 52, 53, 54, -1, + 56, 57, 58, 59, -1, 61, 62, 63, -1, -1, + -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, + -1, 77, 78, 79, 11, 12, -1, 14, 15, -1, + 17, 87, 88, 89, 90, 91, 92, -1, -1, -1, + 96, 97, 98, 99, 100, -1, 102, 103, -1, -1, + -1, -1, 108, -1, 110, 111, 112, -1, -1, 46, + 47, 48, 49, 50, 51, 52, 53, 54, -1, 56, + 57, 58, 59, -1, 61, 62, 63, -1, -1, -1, + -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, + 77, 78, 79, 11, 12, -1, 14, 15, -1, 17, + 87, 88, 89, 90, 91, 92, -1, -1, -1, 96, + 97, 98, 99, 100, -1, 102, 103, -1, -1, -1, + -1, 108, -1, 110, 111, 112, -1, -1, 46, 47, + 48, 49, 50, 51, 52, 53, 54, -1, 56, 57, + 58, 59, -1, 61, 62, 63, -1, -1, -1, -1, + -1, -1, -1, 1, -1, -1, -1, -1, -1, 77, + 78, 79, 10, 11, 12, -1, 14, 15, -1, 87, + 88, 89, 90, 91, 92, -1, -1, -1, 96, 97, + 98, 99, 100, -1, 102, 103, -1, -1, -1, -1, + 108, -1, 110, 111, 112, -1, -1, -1, 46, 47, + 48, 49, 50, 51, 52, 53, 54, -1, 56, 57, + 58, 59, -1, 61, 62, 63, -1, -1, -1, -1, + -1, -1, 1, -1, -1, -1, -1, -1, -1, 77, + 78, 79, 11, 12, -1, 14, 15, -1, 17, 87, + 88, 89, 90, 91, 92, -1, -1, -1, 96, 97, + 98, 99, 100, -1, 102, 103, -1, -1, -1, -1, + 108, -1, 110, 111, 112, -1, -1, 46, 47, 48, + 49, 50, 51, 52, 53, 54, -1, 56, 57, 58, + 59, -1, 61, 62, 63, -1, -1, -1, -1, -1, + -1, 1, -1, -1, -1, -1, -1, -1, 77, 78, + 79, 11, 12, -1, 14, 15, -1, 17, 87, 88, + 89, 90, 91, 92, -1, -1, -1, 96, 97, 98, + 99, 100, -1, 102, 103, -1, -1, -1, -1, 108, + -1, 110, 111, 112, -1, -1, 46, 47, 48, 49, + 50, 51, 52, 53, 54, -1, 56, 57, 58, 59, + -1, 61, 62, 63, -1, -1, -1, -1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, 77, 78, 79, + 11, 12, -1, 14, 15, -1, -1, 87, 88, 89, + 90, 91, 92, -1, -1, -1, 96, 97, 98, 99, + 100, -1, 102, 103, -1, -1, -1, -1, 108, -1, + 110, 111, 112, -1, -1, 46, 47, 48, 49, 50, + 51, 52, 53, 54, -1, 56, 57, 58, 59, -1, + 61, 62, 63, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, 77, 78, 79, 12, + -1, 14, 15, -1, -1, -1, 87, 88, 89, 90, + 91, 92, -1, -1, -1, 96, 97, 98, 99, 100, + -1, 102, 103, -1, -1, -1, -1, 108, -1, 110, + 111, 112, -1, 46, 47, -1, 49, -1, 51, -1, + -1, -1, -1, 56, 57, -1, 59, -1, 61, -1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + -1, -1, -1, -1, 77, 78, 79, 12, -1, -1, + 15, 16, -1, -1, 87, 88, 89, 90, 91, 92, + -1, -1, -1, 96, 97, 98, -1, -1, -1, 102, + 103, -1, -1, -1, -1, 108, -1, 110, 111, 112, + -1, 46, 47, -1, 49, -1, 51, -1, -1, -1, + -1, 56, 57, -1, 59, -1, 61, -1, -1, -1, + 7, 8, -1, -1, 11, 12, -1, -1, 15, -1, + -1, -1, 77, 78, 79, -1, -1, -1, -1, -1, + -1, -1, 87, 88, 89, 90, 91, 92, -1, -1, + -1, 96, 97, 98, -1, -1, -1, 102, 103, 46, + 47, -1, 49, 108, -1, 110, 111, 112, 55, 56, + 57, -1, 59, -1, 61, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 79, -1, -1, -1, -1, -1, -1, -1, + 87, 88, 89, 90, 91, 92, -1, -1, -1, 96, + 97, 98, -1, -1, -1, 102, 103, -1, -1, -1, + -1, 108, -1, 110, 111, 112, 9, -1, -1, 12, + 13, -1, 15, 16, -1, -1, -1, -1, -1, -1, + 23, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 36, 37, -1, 39, 40, 41, 42, + 43, 44, 45, 46, 47, -1, 49, -1, -1, -1, + -1, -1, -1, 56, 57, -1, 59, -1, 61, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 79, -1, -1, -1, + -1, -1, -1, -1, 87, 88, 89, 90, 91, 92, + -1, 94, -1, 96, 97, 98, -1, -1, -1, 102, + 103, -1, -1, -1, -1, 108, -1, 110, 111, 112, + 9, -1, -1, 12, 13, -1, 15, 16, -1, -1, + -1, -1, -1, -1, 23, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 36, 37, -1, + 39, 40, 41, 42, 43, 44, 45, 46, 47, -1, + 49, -1, -1, -1, -1, -1, -1, 56, 57, -1, + 59, -1, 61, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 79, -1, -1, -1, -1, -1, -1, -1, 87, 88, + 89, 90, 91, 92, -1, 94, -1, 96, 97, 98, + -1, -1, -1, 102, 103, -1, -1, -1, -1, 108, + -1, 110, 111, 112, 9, -1, -1, 12, 13, -1, + 15, 16, -1, -1, -1, -1, -1, -1, 23, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 36, 37, -1, 39, 40, 41, 42, 43, 44, + 45, 46, 47, -1, 49, -1, -1, -1, -1, -1, + -1, 56, 57, -1, 59, -1, 61, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 79, -1, -1, -1, -1, -1, + -1, -1, 87, 88, 89, 90, 91, 92, -1, 94, + -1, 96, 97, 98, -1, -1, -1, 102, 103, -1, + -1, -1, -1, 108, -1, 110, 111, 112, 9, -1, + -1, 12, 13, -1, 15, 16, -1, -1, -1, -1, + -1, -1, 23, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 36, 37, -1, 39, 40, + 41, 42, 43, 44, 45, 46, 47, -1, 49, -1, + -1, -1, -1, -1, -1, 56, 57, -1, 59, -1, + 61, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 79, -1, + -1, -1, -1, -1, -1, -1, 87, 88, 89, 90, + 91, 92, -1, 94, -1, 96, 97, 98, -1, -1, + 9, 102, 103, 12, 13, -1, 15, 108, -1, 110, + 111, 112, -1, -1, 23, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 36, 37, -1, + 39, 40, 41, 42, 43, 44, 45, 46, 47, -1, + 49, -1, -1, -1, -1, -1, -1, 56, 57, -1, + 59, -1, 61, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 79, -1, -1, -1, -1, -1, -1, -1, 87, 88, + 89, 90, 91, 92, -1, 94, -1, 96, 97, 98, + -1, -1, -1, 102, 103, -1, -1, -1, -1, 108, + -1, 110, 111, 112, 10, -1, 12, 13, 14, 15, + -1, -1, -1, -1, -1, -1, -1, 23, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 36, 37, -1, 39, 40, 41, 42, 43, 44, 45, + 46, 47, -1, 49, -1, -1, -1, -1, -1, -1, + 56, 57, -1, 59, -1, 61, -1, -1, -1, -1, + -1, -1, 10, -1, 12, -1, 14, 15, -1, -1, + -1, -1, -1, 79, -1, -1, -1, -1, -1, -1, + -1, 87, 88, 89, 90, 91, 92, -1, 94, -1, + 96, 97, 98, -1, -1, -1, 102, 103, 46, 47, + -1, 49, 108, -1, 110, 111, 112, -1, 56, 57, + -1, 59, -1, 61, -1, -1, -1, -1, -1, -1, + 10, -1, 12, -1, -1, 15, -1, -1, -1, -1, + -1, 79, -1, -1, -1, -1, -1, -1, -1, 87, + 88, 89, 90, 91, 92, -1, -1, -1, 96, 97, + 98, -1, -1, -1, 102, 103, 46, 47, -1, 49, + 108, -1, 110, 111, 112, -1, 56, 57, -1, 59, + -1, 61, -1, -1, -1, -1, -1, -1, 10, -1, + 12, -1, -1, 15, -1, -1, -1, -1, -1, 79, + -1, -1, -1, -1, -1, -1, -1, 87, 88, 89, + 90, 91, 92, -1, -1, -1, 96, 97, 98, -1, + -1, -1, 102, 103, 46, 47, -1, 49, 108, -1, + 110, 111, 112, -1, 56, 57, -1, 59, -1, 61, + -1, -1, -1, -1, -1, -1, 10, -1, 12, -1, + -1, 15, -1, -1, -1, -1, -1, 79, -1, -1, + -1, -1, -1, -1, -1, 87, 88, 89, 90, 91, + 92, -1, -1, -1, 96, 97, 98, -1, -1, -1, + 102, 103, 46, 47, -1, 49, 108, -1, 110, 111, + 112, -1, 56, 57, -1, 59, -1, 61, -1, -1, + -1, -1, -1, -1, 10, -1, 12, -1, -1, 15, + -1, -1, -1, -1, -1, 79, -1, -1, -1, -1, + -1, -1, -1, 87, 88, 89, 90, 91, 92, -1, + -1, -1, 96, 97, 98, -1, -1, -1, 102, 103, + 46, 47, -1, 49, 108, -1, 110, 111, 112, -1, + 56, 57, -1, 59, -1, 61, -1, -1, -1, -1, + -1, -1, 10, -1, 12, -1, -1, 15, -1, -1, + -1, -1, -1, 79, -1, -1, -1, -1, -1, -1, + -1, 87, 88, 89, 90, 91, 92, -1, -1, -1, + 96, 97, 98, -1, -1, -1, 102, 103, 46, 47, + -1, 49, 108, -1, 110, 111, 112, -1, 56, 57, + -1, 59, -1, 61, -1, -1, -1, -1, -1, -1, + 10, -1, 12, -1, -1, 15, -1, -1, -1, -1, + -1, 79, -1, -1, -1, -1, -1, -1, -1, 87, + 88, 89, 90, 91, 92, -1, -1, -1, 96, 97, + 98, -1, -1, -1, 102, 103, 46, 47, -1, 49, + 108, -1, 110, 111, 112, -1, 56, 57, -1, 59, + -1, 61, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 79, + -1, -1, -1, -1, -1, -1, -1, 87, 88, 89, + 90, 91, 92, -1, -1, -1, 96, 97, 98, -1, + -1, -1, 102, 103, -1, -1, -1, -1, 108, -1, + 110, 111, 112, 12, 13, -1, 15, 16, -1, -1, + -1, -1, -1, -1, 23, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 36, 37, -1, + 39, 40, 41, 42, 43, 44, 45, 46, 47, -1, + 49, -1, -1, -1, -1, -1, -1, 56, 57, -1, + 59, -1, 61, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 79, -1, -1, -1, -1, -1, -1, -1, 87, 88, + 89, 90, 91, 92, -1, 94, -1, 96, 97, 98, + -1, -1, -1, 102, 103, 12, 13, -1, 15, 108, + -1, 110, 111, 112, -1, -1, 23, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 36, + 37, -1, 39, 40, 41, 42, 43, 44, 45, 46, + 47, -1, 49, -1, -1, -1, -1, -1, -1, 56, + 57, -1, 59, -1, 61, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 79, -1, -1, -1, -1, -1, -1, -1, + 87, 88, 89, 90, 91, 92, -1, 94, 95, 96, + 97, 98, -1, -1, -1, 102, 103, -1, -1, -1, + -1, 108, -1, 110, 111, 112, 12, 13, -1, 15, + 16, -1, -1, -1, -1, -1, -1, 23, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 36, 37, -1, 39, 40, 41, 42, 43, 44, 45, + 46, 47, -1, 49, -1, -1, -1, -1, -1, -1, + 56, 57, -1, 59, -1, 61, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 79, -1, -1, -1, -1, -1, -1, + -1, 87, 88, 89, 90, 91, 92, -1, 94, -1, + 96, 97, 98, -1, -1, -1, 102, 103, -1, -1, + -1, -1, 108, -1, 110, 111, 112, 12, 13, -1, + 15, 16, -1, -1, -1, -1, -1, -1, 23, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 36, 37, -1, 39, 40, 41, 42, 43, 44, + 45, 46, 47, -1, 49, -1, -1, -1, -1, -1, + -1, 56, 57, -1, 59, -1, 61, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 79, -1, -1, -1, -1, -1, + -1, -1, 87, 88, 89, 90, 91, 92, -1, 94, + -1, 96, 97, 98, -1, -1, -1, 102, 103, -1, + -1, -1, -1, 108, -1, 110, 111, 112, 12, 13, + -1, 15, 16, -1, -1, -1, -1, -1, -1, 23, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 36, 37, -1, 39, 40, 41, 42, 43, + 44, 45, 46, 47, -1, 49, -1, -1, -1, -1, + -1, -1, 56, 57, -1, 59, -1, 61, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 79, -1, -1, -1, -1, + -1, -1, -1, 87, 88, 89, 90, 91, 92, -1, + 94, -1, 96, 97, 98, -1, -1, -1, 102, 103, + -1, -1, -1, -1, 108, -1, 110, 111, 112, 12, + 13, -1, 15, 16, -1, -1, -1, -1, -1, -1, + 23, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 36, 37, -1, 39, 40, 41, 42, + 43, 44, 45, 46, 47, -1, 49, -1, -1, -1, + -1, -1, -1, 56, 57, -1, 59, -1, 61, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 79, -1, -1, -1, + -1, -1, -1, -1, 87, 88, 89, 90, 91, 92, + -1, 94, -1, 96, 97, 98, -1, -1, -1, 102, + 103, -1, -1, -1, -1, 108, -1, 110, 111, 112, + 12, 13, -1, 15, 16, -1, -1, -1, -1, -1, + -1, 23, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 36, 37, -1, 39, 40, 41, + 42, 43, 44, 45, 46, 47, -1, 49, -1, -1, + -1, -1, -1, -1, 56, 57, -1, 59, -1, 61, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 79, -1, -1, + -1, -1, -1, -1, -1, 87, 88, 89, 90, 91, + 92, -1, 94, -1, 96, 97, 98, -1, -1, -1, + 102, 103, 12, 13, 14, 15, 108, -1, 110, 111, + 112, -1, -1, 23, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 36, 37, -1, 39, + 40, 41, 42, 43, 44, 45, 46, 47, -1, 49, + -1, -1, -1, -1, -1, -1, 56, 57, -1, 59, + -1, 61, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 79, + -1, -1, -1, -1, -1, -1, -1, 87, 88, 89, + 90, 91, 92, -1, 94, -1, 96, 97, 98, -1, + -1, -1, 102, 103, -1, -1, -1, -1, 108, -1, + 110, 111, 112, 12, 13, -1, 15, 16, -1, -1, + -1, -1, -1, -1, 23, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 36, 37, -1, + 39, 40, 41, 42, 43, 44, 45, 46, 47, -1, + 49, -1, -1, -1, -1, -1, -1, 56, 57, -1, + 59, -1, 61, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 79, -1, -1, -1, -1, -1, -1, -1, 87, 88, + 89, 90, 91, 92, -1, 94, -1, 96, 97, 98, + -1, -1, -1, 102, 103, 12, 13, -1, 15, 108, + -1, 110, 111, 112, -1, -1, 23, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 36, + 37, -1, 39, 40, 41, 42, 43, 44, 45, 46, + 47, -1, 49, -1, -1, -1, -1, -1, -1, 56, + 57, -1, 59, -1, 61, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 79, -1, -1, -1, -1, -1, -1, -1, + 87, 88, 89, 90, 91, 92, -1, 94, -1, 96, + 97, 98, -1, -1, -1, 102, 103, 12, 13, -1, + 15, 108, -1, 110, 111, 112, -1, -1, 23, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 36, -1, -1, 39, 40, 41, 42, 43, 44, + 45, 46, 47, -1, 49, -1, -1, -1, -1, -1, + -1, 56, 57, -1, 59, -1, 61, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 79, -1, -1, -1, -1, -1, + -1, -1, 87, 88, 89, 90, 91, 92, -1, -1, + -1, 96, 97, 98, -1, -1, -1, 102, 103, 12, + 13, -1, 15, 108, -1, 110, 111, 112, -1, -1, + 23, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 39, 40, 41, 42, + 43, 44, 45, 46, 47, -1, 49, -1, -1, -1, + -1, -1, -1, 56, 57, -1, 59, -1, 61, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 79, -1, -1, -1, + -1, -1, -1, -1, 87, 88, 89, 90, 91, 92, + -1, -1, -1, 96, 97, 98, -1, -1, -1, 102, + 103, 12, 13, -1, 15, 108, -1, 110, 111, 112, + -1, -1, 23, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 39, 40, + 41, 42, 43, 44, 45, 46, 47, -1, 49, -1, + -1, -1, -1, -1, -1, 56, 57, -1, 59, -1, + 61, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 79, -1, + -1, 12, -1, -1, 15, -1, 87, 88, 89, 90, + 91, 92, 23, -1, -1, -1, 97, 98, -1, -1, + -1, 102, 103, -1, -1, 36, 37, 108, -1, 110, + 111, 112, -1, -1, 45, 46, 47, -1, 49, -1, + -1, -1, -1, -1, -1, 56, 57, -1, 59, -1, + 61, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 79, -1, + -1, 12, -1, -1, 15, -1, 87, 88, 89, 90, + 91, 92, 23, 94, 95, 96, 97, 98, -1, -1, + -1, 102, 103, -1, -1, 36, 37, 108, -1, 110, + 111, 112, -1, -1, 45, 46, 47, -1, 49, -1, + -1, -1, -1, -1, -1, 56, 57, -1, 59, -1, + 61, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 79, -1, + -1, 12, -1, -1, 15, -1, 87, 88, 89, 90, + 91, 92, 23, 94, -1, 96, 97, 98, -1, -1, + -1, 102, 103, -1, -1, 36, -1, 108, -1, 110, + 111, 112, -1, -1, 45, 46, 47, -1, 49, -1, + -1, -1, -1, -1, -1, 56, 57, -1, 59, -1, + 61, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 12, -1, -1, 15, -1, -1, -1, 79, -1, + -1, -1, 23, -1, -1, -1, 87, 88, 89, 90, + 91, 92, -1, -1, -1, 96, 97, 98, -1, -1, + -1, 102, 103, -1, 45, 46, 47, 108, 49, 110, + 111, 112, -1, -1, -1, 56, 57, -1, 59, -1, + 61, -1, -1, -1, -1, -1, -1, -1, -1, 12, + -1, -1, 15, 16, -1, -1, -1, -1, 79, -1, + -1, -1, -1, -1, -1, -1, 87, 88, 89, 90, + 91, 92, -1, -1, -1, 96, 97, 98, -1, -1, + -1, 102, 103, 46, 47, -1, 49, 108, -1, 110, + 111, 112, -1, 56, 57, -1, 59, -1, 61, -1, + -1, -1, -1, -1, -1, -1, -1, 12, -1, -1, + 15, 16, -1, -1, -1, -1, 79, -1, -1, -1, + -1, -1, -1, -1, 87, 88, 89, 90, 91, 92, + -1, -1, -1, 96, 97, 98, -1, -1, -1, 102, + 103, 46, 47, -1, 49, 108, -1, 110, 111, 112, + -1, 56, 57, -1, 59, -1, 61, -1, -1, -1, + -1, -1, -1, -1, -1, 12, -1, -1, 15, -1, + -1, -1, -1, -1, 79, -1, -1, -1, -1, -1, + -1, -1, 87, 88, 89, 90, 91, 92, -1, -1, + -1, 96, 97, 98, -1, -1, -1, 102, 103, 46, + 47, -1, 49, 108, -1, 110, 111, 112, -1, 56, + 57, -1, 59, -1, 61, -1, -1, -1, -1, -1, + -1, -1, -1, 70, -1, 12, -1, -1, 15, -1, + -1, -1, 79, -1, -1, -1, 23, -1, -1, -1, + 87, 88, 89, 90, 91, 92, -1, -1, -1, 96, + 97, 98, -1, -1, -1, 102, 103, -1, 45, 46, + 47, 108, 49, 110, 111, 112, -1, -1, -1, 56, + 57, -1, 59, -1, 61, -1, -1, -1, -1, -1, + -1, -1, -1, 12, -1, -1, 15, -1, -1, -1, + -1, -1, 79, -1, -1, -1, -1, -1, -1, -1, + 87, 88, 89, 90, 91, 92, -1, -1, -1, -1, + 97, 98, -1, -1, -1, 102, 103, 46, 47, -1, + 49, 108, -1, 110, 111, 112, -1, 56, 57, -1, + 59, -1, 61, -1, -1, -1, -1, -1, -1, -1, + -1, 12, -1, -1, 15, -1, -1, -1, -1, -1, + 79, -1, -1, -1, -1, -1, -1, -1, 87, 88, + 89, 90, 91, 92, -1, -1, -1, 96, 97, 98, + -1, -1, -1, 102, 103, 46, 47, -1, 49, 108, + -1, 110, 111, 112, -1, 56, 57, -1, 59, -1, + 61, -1, -1, -1, -1, -1, -1, -1, -1, 12, + -1, -1, 15, -1, -1, -1, -1, -1, 79, -1, + -1, -1, -1, -1, -1, -1, 87, 88, 89, 90, + 91, 92, -1, -1, -1, 96, 97, 98, -1, -1, + -1, 102, 103, 46, 47, -1, 49, 108, -1, 110, + 111, 112, -1, 56, 57, -1, 59, -1, 61, -1, + -1, -1, -1, -1, -1, -1, -1, 12, -1, -1, + 15, -1, -1, -1, -1, -1, 79, -1, -1, -1, + -1, -1, -1, -1, 87, 88, 89, 90, 91, 92, + -1, -1, -1, 96, 97, 98, -1, -1, -1, 102, + 103, 46, 47, -1, 49, 108, -1, 110, 111, 112, + -1, 56, 57, -1, 59, -1, 61, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 79, -1, -1, -1, -1, -1, + -1, -1, 87, 88, 89, 90, 91, 92, -1, -1, + -1, 96, 97, 98, -1, -1, -1, 102, 103, -1, + -1, -1, -1, 108, -1, 110, 111, 112 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 1, 10, 14, 115, 130, 132, 144, 0, 7, + 8, 11, 12, 15, 46, 47, 49, 55, 56, 57, + 59, 61, 79, 87, 88, 89, 90, 91, 92, 96, + 97, 98, 102, 103, 108, 110, 111, 112, 127, 133, + 134, 136, 139, 146, 147, 157, 158, 159, 161, 10, + 14, 127, 127, 139, 140, 148, 12, 12, 146, 158, + 159, 87, 90, 125, 12, 12, 12, 12, 43, 159, + 12, 12, 158, 158, 146, 158, 159, 159, 158, 1, + 10, 14, 48, 50, 51, 52, 53, 54, 58, 62, + 63, 77, 78, 99, 100, 119, 121, 126, 127, 139, + 143, 150, 152, 156, 162, 9, 127, 130, 13, 23, + 36, 37, 39, 40, 41, 42, 43, 44, 45, 94, + 116, 117, 158, 15, 12, 15, 102, 103, 104, 105, + 109, 70, 110, 111, 18, 156, 156, 9, 16, 118, + 16, 118, 93, 16, 137, 139, 139, 12, 139, 139, + 137, 16, 137, 158, 43, 139, 139, 10, 128, 129, + 14, 128, 151, 151, 161, 139, 151, 12, 12, 151, + 151, 139, 151, 12, 10, 153, 152, 156, 12, 138, + 141, 142, 146, 158, 159, 151, 17, 152, 155, 133, + 156, 134, 96, 139, 147, 139, 139, 139, 139, 139, + 139, 161, 139, 10, 139, 10, 139, 158, 139, 147, + 70, 158, 158, 158, 158, 158, 158, 139, 137, 17, + 17, 10, 139, 45, 139, 15, 16, 118, 87, 160, + 118, 118, 16, 16, 158, 118, 118, 10, 129, 18, + 151, 131, 150, 161, 139, 151, 139, 152, 80, 120, + 17, 145, 140, 23, 45, 94, 116, 117, 158, 118, + 13, 38, 41, 70, 152, 127, 17, 159, 95, 118, + 118, 158, 19, 161, 139, 16, 118, 149, 139, 147, + 139, 147, 139, 161, 137, 14, 45, 149, 149, 154, + 10, 152, 10, 16, 12, 138, 147, 161, 138, 138, + 138, 138, 158, 158, 158, 138, 156, 139, 139, 139, + 87, 10, 135, 16, 16, 16, 16, 16, 118, 16, + 118, 19, 14, 129, 161, 100, 45, 140, 95, 17, + 16, 118, 16, 118, 127, 139, 139, 147, 129, 139, + 149, 12, 161, 16, 138, 159, 159, 156, 16, 16, + 16, 131, 14, 124, 139, 16, 16, 17, 149, 129, + 152, 16, 123, 131, 151, 152, 149, 122, 152 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (thread_stderr, "%s ", Title); \ + yy_symbol_print (thread_stderr, \ + Type, Value); \ + YYFPRINTF (thread_stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +#else +static void +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; +#endif +{ + YYFPRINTF (thread_stderr, "Stack now"); + for (; bottom <= top; ++bottom) + YYFPRINTF (thread_stderr, " %d", *bottom); + YYFPRINTF (thread_stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (thread_stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + fprintf (thread_stderr, " $%d = ", yyi + 1); + yy_symbol_print (thread_stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + fprintf (thread_stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) +{ + int yyn = yypact[yystate]; + + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } +} +#endif /* YYERROR_VERBOSE */ + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + +/* The look-ahead symbol. */ +static int yychar; + +/* The semantic value of the look-ahead symbol. */ +__thread YYSTYPE yylval; + +/* Number of syntax errors so far. */ +static int yynerrs; + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + int yystate; + int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss = yyssa; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp; + + + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((thread_stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((thread_stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((thread_stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + look-ahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to look-ahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a look-ahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((thread_stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((thread_stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 98 "awkgram.y" + { if (errorflag==0) + winner = (Node *)stat3(PROGRAM, beginloc, (yyvsp[(1) - (1)].p), endloc); } + break; + + case 3: +#line 100 "awkgram.y" + { yyclearin; bracecheck(); SYNTAX("bailing out"); } + break; + + case 14: +#line 124 "awkgram.y" + {inloop++;} + break; + + case 15: +#line 125 "awkgram.y" + { --inloop; (yyval.p) = stat4(FOR, (yyvsp[(3) - (12)].p), notnull((yyvsp[(6) - (12)].p)), (yyvsp[(9) - (12)].p), (yyvsp[(12) - (12)].p)); } + break; + + case 16: +#line 126 "awkgram.y" + {inloop++;} + break; + + case 17: +#line 127 "awkgram.y" + { --inloop; (yyval.p) = stat4(FOR, (yyvsp[(3) - (10)].p), NIL, (yyvsp[(7) - (10)].p), (yyvsp[(10) - (10)].p)); } + break; + + case 18: +#line 128 "awkgram.y" + {inloop++;} + break; + + case 19: +#line 129 "awkgram.y" + { --inloop; (yyval.p) = stat3(IN, (yyvsp[(3) - (8)].p), makearr((yyvsp[(5) - (8)].p)), (yyvsp[(8) - (8)].p)); } + break; + + case 20: +#line 133 "awkgram.y" + { setfname((yyvsp[(1) - (1)].cp)); } + break; + + case 21: +#line 134 "awkgram.y" + { setfname((yyvsp[(1) - (1)].cp)); } + break; + + case 22: +#line 138 "awkgram.y" + { (yyval.p) = notnull((yyvsp[(3) - (4)].p)); } + break; + + case 27: +#line 150 "awkgram.y" + { (yyval.i) = 0; } + break; + + case 29: +#line 155 "awkgram.y" + { (yyval.i) = 0; } + break; + + case 31: +#line 161 "awkgram.y" + { (yyval.p) = 0; } + break; + + case 33: +#line 166 "awkgram.y" + { (yyval.p) = 0; } + break; + + case 34: +#line 167 "awkgram.y" + { (yyval.p) = (yyvsp[(2) - (3)].p); } + break; + + case 35: +#line 171 "awkgram.y" + { (yyval.p) = notnull((yyvsp[(1) - (1)].p)); } + break; + + case 36: +#line 175 "awkgram.y" + { (yyval.p) = stat2(PASTAT, (yyvsp[(1) - (1)].p), stat2(PRINT, rectonode(), NIL)); } + break; + + case 37: +#line 176 "awkgram.y" + { (yyval.p) = stat2(PASTAT, (yyvsp[(1) - (4)].p), (yyvsp[(3) - (4)].p)); } + break; + + case 38: +#line 177 "awkgram.y" + { (yyval.p) = pa2stat((yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p), stat2(PRINT, rectonode(), NIL)); } + break; + + case 39: +#line 178 "awkgram.y" + { (yyval.p) = pa2stat((yyvsp[(1) - (6)].p), (yyvsp[(3) - (6)].p), (yyvsp[(5) - (6)].p)); } + break; + + case 40: +#line 179 "awkgram.y" + { (yyval.p) = stat2(PASTAT, NIL, (yyvsp[(2) - (3)].p)); } + break; + + case 41: +#line 181 "awkgram.y" + { beginloc = linkum(beginloc, (yyvsp[(3) - (4)].p)); (yyval.p) = 0; } + break; + + case 42: +#line 183 "awkgram.y" + { endloc = linkum(endloc, (yyvsp[(3) - (4)].p)); (yyval.p) = 0; } + break; + + case 43: +#line 184 "awkgram.y" + {infunc++;} + break; + + case 44: +#line 185 "awkgram.y" + { infunc--; curfname=0; defn((Cell *)(yyvsp[(2) - (9)].p), (yyvsp[(4) - (9)].p), (yyvsp[(8) - (9)].p)); (yyval.p) = 0; } + break; + + case 46: +#line 190 "awkgram.y" + { (yyval.p) = linkum((yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 48: +#line 195 "awkgram.y" + { (yyval.p) = linkum((yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 49: +#line 199 "awkgram.y" + { (yyval.p) = op2((yyvsp[(2) - (3)].i), (yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 50: +#line 201 "awkgram.y" + { (yyval.p) = op3(CONDEXPR, notnull((yyvsp[(1) - (5)].p)), (yyvsp[(3) - (5)].p), (yyvsp[(5) - (5)].p)); } + break; + + case 51: +#line 203 "awkgram.y" + { (yyval.p) = op2(BOR, notnull((yyvsp[(1) - (3)].p)), notnull((yyvsp[(3) - (3)].p))); } + break; + + case 52: +#line 205 "awkgram.y" + { (yyval.p) = op2(AND, notnull((yyvsp[(1) - (3)].p)), notnull((yyvsp[(3) - (3)].p))); } + break; + + case 53: +#line 206 "awkgram.y" + { (yyval.p) = op3((yyvsp[(2) - (3)].i), NIL, (yyvsp[(1) - (3)].p), (Node*)makedfa((yyvsp[(3) - (3)].s), 0)); } + break; + + case 54: +#line 208 "awkgram.y" + { if (constnode((yyvsp[(3) - (3)].p))) + (yyval.p) = op3((yyvsp[(2) - (3)].i), NIL, (yyvsp[(1) - (3)].p), (Node*)makedfa(strnode((yyvsp[(3) - (3)].p)), 0)); + else + (yyval.p) = op3((yyvsp[(2) - (3)].i), (Node *)1, (yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 55: +#line 212 "awkgram.y" + { (yyval.p) = op2(INTEST, (yyvsp[(1) - (3)].p), makearr((yyvsp[(3) - (3)].p))); } + break; + + case 56: +#line 213 "awkgram.y" + { (yyval.p) = op2(INTEST, (yyvsp[(2) - (5)].p), makearr((yyvsp[(5) - (5)].p))); } + break; + + case 57: +#line 214 "awkgram.y" + { (yyval.p) = op2(CAT, (yyvsp[(1) - (2)].p), (yyvsp[(2) - (2)].p)); } + break; + + case 60: +#line 220 "awkgram.y" + { (yyval.p) = op2((yyvsp[(2) - (3)].i), (yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 61: +#line 222 "awkgram.y" + { (yyval.p) = op3(CONDEXPR, notnull((yyvsp[(1) - (5)].p)), (yyvsp[(3) - (5)].p), (yyvsp[(5) - (5)].p)); } + break; + + case 62: +#line 224 "awkgram.y" + { (yyval.p) = op2(BOR, notnull((yyvsp[(1) - (3)].p)), notnull((yyvsp[(3) - (3)].p))); } + break; + + case 63: +#line 226 "awkgram.y" + { (yyval.p) = op2(AND, notnull((yyvsp[(1) - (3)].p)), notnull((yyvsp[(3) - (3)].p))); } + break; + + case 64: +#line 227 "awkgram.y" + { (yyval.p) = op2((yyvsp[(2) - (3)].i), (yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 65: +#line 228 "awkgram.y" + { (yyval.p) = op2((yyvsp[(2) - (3)].i), (yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 66: +#line 229 "awkgram.y" + { (yyval.p) = op2((yyvsp[(2) - (3)].i), (yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 67: +#line 230 "awkgram.y" + { (yyval.p) = op2((yyvsp[(2) - (3)].i), (yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 68: +#line 231 "awkgram.y" + { (yyval.p) = op2((yyvsp[(2) - (3)].i), (yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 69: +#line 232 "awkgram.y" + { (yyval.p) = op2((yyvsp[(2) - (3)].i), (yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 70: +#line 233 "awkgram.y" + { (yyval.p) = op3((yyvsp[(2) - (3)].i), NIL, (yyvsp[(1) - (3)].p), (Node*)makedfa((yyvsp[(3) - (3)].s), 0)); } + break; + + case 71: +#line 235 "awkgram.y" + { if (constnode((yyvsp[(3) - (3)].p))) + (yyval.p) = op3((yyvsp[(2) - (3)].i), NIL, (yyvsp[(1) - (3)].p), (Node*)makedfa(strnode((yyvsp[(3) - (3)].p)), 0)); + else + (yyval.p) = op3((yyvsp[(2) - (3)].i), (Node *)1, (yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 72: +#line 239 "awkgram.y" + { (yyval.p) = op2(INTEST, (yyvsp[(1) - (3)].p), makearr((yyvsp[(3) - (3)].p))); } + break; + + case 73: +#line 240 "awkgram.y" + { (yyval.p) = op2(INTEST, (yyvsp[(2) - (5)].p), makearr((yyvsp[(5) - (5)].p))); } + break; + + case 74: +#line 241 "awkgram.y" + { + if (safe) SYNTAX("cmd | getline is unsafe"); + else (yyval.p) = op3(GETLINE, (yyvsp[(4) - (4)].p), itonp((yyvsp[(2) - (4)].i)), (yyvsp[(1) - (4)].p)); } + break; + + case 75: +#line 244 "awkgram.y" + { + if (safe) SYNTAX("cmd | getline is unsafe"); + else (yyval.p) = op3(GETLINE, (Node*)0, itonp((yyvsp[(2) - (3)].i)), (yyvsp[(1) - (3)].p)); } + break; + + case 76: +#line 247 "awkgram.y" + { (yyval.p) = op2(CAT, (yyvsp[(1) - (2)].p), (yyvsp[(2) - (2)].p)); } + break; + + case 79: +#line 253 "awkgram.y" + { (yyval.p) = linkum((yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 80: +#line 254 "awkgram.y" + { (yyval.p) = linkum((yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 82: +#line 259 "awkgram.y" + { (yyval.p) = linkum((yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 83: +#line 263 "awkgram.y" + { (yyval.p) = rectonode(); } + break; + + case 85: +#line 265 "awkgram.y" + { (yyval.p) = (yyvsp[(2) - (3)].p); } + break; + + case 94: +#line 282 "awkgram.y" + { (yyval.p) = op3(MATCH, NIL, rectonode(), (Node*)makedfa((yyvsp[(1) - (1)].s), 0)); } + break; + + case 95: +#line 283 "awkgram.y" + { (yyval.p) = op1(NOT, notnull((yyvsp[(2) - (2)].p))); } + break; + + case 96: +#line 287 "awkgram.y" + {startreg();} + break; + + case 97: +#line 287 "awkgram.y" + { (yyval.s) = (yyvsp[(3) - (4)].s); } + break; + + case 100: +#line 295 "awkgram.y" + { + if (safe) SYNTAX("print | is unsafe"); + else (yyval.p) = stat3((yyvsp[(1) - (4)].i), (yyvsp[(2) - (4)].p), itonp((yyvsp[(3) - (4)].i)), (yyvsp[(4) - (4)].p)); } + break; + + case 101: +#line 298 "awkgram.y" + { + if (safe) SYNTAX("print >> is unsafe"); + else (yyval.p) = stat3((yyvsp[(1) - (4)].i), (yyvsp[(2) - (4)].p), itonp((yyvsp[(3) - (4)].i)), (yyvsp[(4) - (4)].p)); } + break; + + case 102: +#line 301 "awkgram.y" + { + if (safe) SYNTAX("print > is unsafe"); + else (yyval.p) = stat3((yyvsp[(1) - (4)].i), (yyvsp[(2) - (4)].p), itonp((yyvsp[(3) - (4)].i)), (yyvsp[(4) - (4)].p)); } + break; + + case 103: +#line 304 "awkgram.y" + { (yyval.p) = stat3((yyvsp[(1) - (2)].i), (yyvsp[(2) - (2)].p), NIL, NIL); } + break; + + case 104: +#line 305 "awkgram.y" + { (yyval.p) = stat2(DELETE, makearr((yyvsp[(2) - (5)].p)), (yyvsp[(4) - (5)].p)); } + break; + + case 105: +#line 306 "awkgram.y" + { (yyval.p) = stat2(DELETE, makearr((yyvsp[(2) - (2)].p)), 0); } + break; + + case 106: +#line 307 "awkgram.y" + { (yyval.p) = exptostat((yyvsp[(1) - (1)].p)); } + break; + + case 107: +#line 308 "awkgram.y" + { yyclearin; SYNTAX("illegal statement"); } + break; + + case 110: +#line 317 "awkgram.y" + { if (!inloop) SYNTAX("break illegal outside of loops"); + (yyval.p) = stat1(BREAK, NIL); } + break; + + case 111: +#line 319 "awkgram.y" + { if (!inloop) SYNTAX("continue illegal outside of loops"); + (yyval.p) = stat1(CONTINUE, NIL); } + break; + + case 112: +#line 321 "awkgram.y" + {inloop++;} + break; + + case 113: +#line 321 "awkgram.y" + {--inloop;} + break; + + case 114: +#line 322 "awkgram.y" + { (yyval.p) = stat2(DO, (yyvsp[(3) - (9)].p), notnull((yyvsp[(7) - (9)].p))); } + break; + + case 115: +#line 323 "awkgram.y" + { (yyval.p) = stat1(EXIT, (yyvsp[(2) - (3)].p)); } + break; + + case 116: +#line 324 "awkgram.y" + { (yyval.p) = stat1(EXIT, NIL); } + break; + + case 118: +#line 326 "awkgram.y" + { (yyval.p) = stat3(IF, (yyvsp[(1) - (4)].p), (yyvsp[(2) - (4)].p), (yyvsp[(4) - (4)].p)); } + break; + + case 119: +#line 327 "awkgram.y" + { (yyval.p) = stat3(IF, (yyvsp[(1) - (2)].p), (yyvsp[(2) - (2)].p), NIL); } + break; + + case 120: +#line 328 "awkgram.y" + { (yyval.p) = (yyvsp[(2) - (3)].p); } + break; + + case 121: +#line 329 "awkgram.y" + { if (infunc) + SYNTAX("next is illegal inside a function"); + (yyval.p) = stat1(NEXT, NIL); } + break; + + case 122: +#line 332 "awkgram.y" + { if (infunc) + SYNTAX("nextfile is illegal inside a function"); + (yyval.p) = stat1(NEXTFILE, NIL); } + break; + + case 123: +#line 335 "awkgram.y" + { (yyval.p) = stat1(RETURN, (yyvsp[(2) - (3)].p)); } + break; + + case 124: +#line 336 "awkgram.y" + { (yyval.p) = stat1(RETURN, NIL); } + break; + + case 126: +#line 338 "awkgram.y" + {inloop++;} + break; + + case 127: +#line 338 "awkgram.y" + { --inloop; (yyval.p) = stat2(WHILE, (yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 128: +#line 339 "awkgram.y" + { (yyval.p) = 0; } + break; + + case 130: +#line 344 "awkgram.y" + { (yyval.p) = linkum((yyvsp[(1) - (2)].p), (yyvsp[(2) - (2)].p)); } + break; + + case 133: +#line 352 "awkgram.y" + { (yyval.p) = op2(DIVEQ, (yyvsp[(1) - (4)].p), (yyvsp[(4) - (4)].p)); } + break; + + case 134: +#line 353 "awkgram.y" + { (yyval.p) = op2(ADD, (yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 135: +#line 354 "awkgram.y" + { (yyval.p) = op2(MINUS, (yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 136: +#line 355 "awkgram.y" + { (yyval.p) = op2(MULT, (yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 137: +#line 356 "awkgram.y" + { (yyval.p) = op2(DIVIDE, (yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 138: +#line 357 "awkgram.y" + { (yyval.p) = op2(DIVIDE, (yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 139: +#line 358 "awkgram.y" + { (yyval.p) = op2(MOD, (yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 140: +#line 359 "awkgram.y" + { (yyval.p) = op2(POWER, (yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].p)); } + break; + + case 141: +#line 360 "awkgram.y" + { (yyval.p) = op1(UMINUS, (yyvsp[(2) - (2)].p)); } + break; + + case 142: +#line 361 "awkgram.y" + { (yyval.p) = op1(UPLUS, (yyvsp[(2) - (2)].p)); } + break; + + case 143: +#line 362 "awkgram.y" + { (yyval.p) = op1(NOT, notnull((yyvsp[(2) - (2)].p))); } + break; + + case 144: +#line 363 "awkgram.y" + { (yyval.p) = op2(BLTIN, itonp((yyvsp[(1) - (3)].i)), rectonode()); } + break; + + case 145: +#line 364 "awkgram.y" + { (yyval.p) = op2(BLTIN, itonp((yyvsp[(1) - (4)].i)), (yyvsp[(3) - (4)].p)); } + break; + + case 146: +#line 365 "awkgram.y" + { (yyval.p) = op2(BLTIN, itonp((yyvsp[(1) - (1)].i)), rectonode()); } + break; + + case 147: +#line 366 "awkgram.y" + { (yyval.p) = op2(CALL, celltonode((yyvsp[(1) - (3)].cp),CVAR), NIL); } + break; + + case 148: +#line 367 "awkgram.y" + { (yyval.p) = op2(CALL, celltonode((yyvsp[(1) - (4)].cp),CVAR), (yyvsp[(3) - (4)].p)); } + break; + + case 149: +#line 368 "awkgram.y" + { (yyval.p) = op1(CLOSE, (yyvsp[(2) - (2)].p)); } + break; + + case 150: +#line 369 "awkgram.y" + { (yyval.p) = op1(PREDECR, (yyvsp[(2) - (2)].p)); } + break; + + case 151: +#line 370 "awkgram.y" + { (yyval.p) = op1(PREINCR, (yyvsp[(2) - (2)].p)); } + break; + + case 152: +#line 371 "awkgram.y" + { (yyval.p) = op1(POSTDECR, (yyvsp[(1) - (2)].p)); } + break; + + case 153: +#line 372 "awkgram.y" + { (yyval.p) = op1(POSTINCR, (yyvsp[(1) - (2)].p)); } + break; + + case 154: +#line 373 "awkgram.y" + { (yyval.p) = op3(GETLINE, (yyvsp[(2) - (4)].p), itonp((yyvsp[(3) - (4)].i)), (yyvsp[(4) - (4)].p)); } + break; + + case 155: +#line 374 "awkgram.y" + { (yyval.p) = op3(GETLINE, NIL, itonp((yyvsp[(2) - (3)].i)), (yyvsp[(3) - (3)].p)); } + break; + + case 156: +#line 375 "awkgram.y" + { (yyval.p) = op3(GETLINE, (yyvsp[(2) - (2)].p), NIL, NIL); } + break; + + case 157: +#line 376 "awkgram.y" + { (yyval.p) = op3(GETLINE, NIL, NIL, NIL); } + break; + + case 158: +#line 378 "awkgram.y" + { (yyval.p) = op2(INDEX, (yyvsp[(3) - (6)].p), (yyvsp[(5) - (6)].p)); } + break; + + case 159: +#line 380 "awkgram.y" + { SYNTAX("index() doesn't permit regular expressions"); + (yyval.p) = op2(INDEX, (yyvsp[(3) - (6)].p), (Node*)(yyvsp[(5) - (6)].s)); } + break; + + case 160: +#line 382 "awkgram.y" + { (yyval.p) = (yyvsp[(2) - (3)].p); } + break; + + case 161: +#line 384 "awkgram.y" + { (yyval.p) = op3(MATCHFCN, NIL, (yyvsp[(3) - (6)].p), (Node*)makedfa((yyvsp[(5) - (6)].s), 1)); } + break; + + case 162: +#line 386 "awkgram.y" + { if (constnode((yyvsp[(5) - (6)].p))) + (yyval.p) = op3(MATCHFCN, NIL, (yyvsp[(3) - (6)].p), (Node*)makedfa(strnode((yyvsp[(5) - (6)].p)), 1)); + else + (yyval.p) = op3(MATCHFCN, (Node *)1, (yyvsp[(3) - (6)].p), (yyvsp[(5) - (6)].p)); } + break; + + case 163: +#line 390 "awkgram.y" + { (yyval.p) = celltonode((yyvsp[(1) - (1)].cp), CCON); } + break; + + case 164: +#line 392 "awkgram.y" + { (yyval.p) = op4(SPLIT, (yyvsp[(3) - (8)].p), makearr((yyvsp[(5) - (8)].p)), (yyvsp[(7) - (8)].p), (Node*)STRING); } + break; + + case 165: +#line 394 "awkgram.y" + { (yyval.p) = op4(SPLIT, (yyvsp[(3) - (8)].p), makearr((yyvsp[(5) - (8)].p)), (Node*)makedfa((yyvsp[(7) - (8)].s), 1), (Node *)REGEXPR); } + break; + + case 166: +#line 396 "awkgram.y" + { (yyval.p) = op4(SPLIT, (yyvsp[(3) - (6)].p), makearr((yyvsp[(5) - (6)].p)), NIL, (Node*)STRING); } + break; + + case 167: +#line 397 "awkgram.y" + { (yyval.p) = op1((yyvsp[(1) - (4)].i), (yyvsp[(3) - (4)].p)); } + break; + + case 168: +#line 398 "awkgram.y" + { (yyval.p) = celltonode((yyvsp[(1) - (1)].cp), CCON); } + break; + + case 169: +#line 400 "awkgram.y" + { (yyval.p) = op4((yyvsp[(1) - (6)].i), NIL, (Node*)makedfa((yyvsp[(3) - (6)].s), 1), (yyvsp[(5) - (6)].p), rectonode()); } + break; + + case 170: +#line 402 "awkgram.y" + { if (constnode((yyvsp[(3) - (6)].p))) + (yyval.p) = op4((yyvsp[(1) - (6)].i), NIL, (Node*)makedfa(strnode((yyvsp[(3) - (6)].p)), 1), (yyvsp[(5) - (6)].p), rectonode()); + else + (yyval.p) = op4((yyvsp[(1) - (6)].i), (Node *)1, (yyvsp[(3) - (6)].p), (yyvsp[(5) - (6)].p), rectonode()); } + break; + + case 171: +#line 407 "awkgram.y" + { (yyval.p) = op4((yyvsp[(1) - (8)].i), NIL, (Node*)makedfa((yyvsp[(3) - (8)].s), 1), (yyvsp[(5) - (8)].p), (yyvsp[(7) - (8)].p)); } + break; + + case 172: +#line 409 "awkgram.y" + { if (constnode((yyvsp[(3) - (8)].p))) + (yyval.p) = op4((yyvsp[(1) - (8)].i), NIL, (Node*)makedfa(strnode((yyvsp[(3) - (8)].p)), 1), (yyvsp[(5) - (8)].p), (yyvsp[(7) - (8)].p)); + else + (yyval.p) = op4((yyvsp[(1) - (8)].i), (Node *)1, (yyvsp[(3) - (8)].p), (yyvsp[(5) - (8)].p), (yyvsp[(7) - (8)].p)); } + break; + + case 173: +#line 414 "awkgram.y" + { (yyval.p) = op3(SUBSTR, (yyvsp[(3) - (8)].p), (yyvsp[(5) - (8)].p), (yyvsp[(7) - (8)].p)); } + break; + + case 174: +#line 416 "awkgram.y" + { (yyval.p) = op3(SUBSTR, (yyvsp[(3) - (6)].p), (yyvsp[(5) - (6)].p), NIL); } + break; + + case 177: +#line 422 "awkgram.y" + { (yyval.p) = op2(ARRAY, makearr((yyvsp[(1) - (4)].p)), (yyvsp[(3) - (4)].p)); } + break; + + case 178: +#line 423 "awkgram.y" + { (yyval.p) = op1(INDIRECT, celltonode((yyvsp[(1) - (1)].cp), CVAR)); } + break; + + case 179: +#line 424 "awkgram.y" + { (yyval.p) = op1(INDIRECT, (yyvsp[(2) - (2)].p)); } + break; + + case 180: +#line 428 "awkgram.y" + { arglist = (yyval.p) = 0; } + break; + + case 181: +#line 429 "awkgram.y" + { arglist = (yyval.p) = celltonode((yyvsp[(1) - (1)].cp),CVAR); } + break; + + case 182: +#line 430 "awkgram.y" + { + checkdup((yyvsp[(1) - (3)].p), (yyvsp[(3) - (3)].cp)); + arglist = (yyval.p) = linkum((yyvsp[(1) - (3)].p),celltonode((yyvsp[(3) - (3)].cp),CVAR)); } + break; + + case 183: +#line 436 "awkgram.y" + { (yyval.p) = celltonode((yyvsp[(1) - (1)].cp), CVAR); } + break; + + case 184: +#line 437 "awkgram.y" + { (yyval.p) = op1(ARG, itonp((yyvsp[(1) - (1)].i))); } + break; + + case 185: +#line 438 "awkgram.y" + { (yyval.p) = op1(VARNF, (Node *) (yyvsp[(1) - (1)].cp)); } + break; + + case 186: +#line 443 "awkgram.y" + { (yyval.p) = notnull((yyvsp[(3) - (4)].p)); } + break; + + +/* Line 1267 of yacc.c. */ +#line 3496 "y.tab.c" + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (yymsg); + } + else + { + yyerror (YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse look-ahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse look-ahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + +#line 446 "awkgram.y" + + +void setfname(Cell *p) +{ + if (isarr(p)) + SYNTAX("%s is an array, not a function", p->nval); + else if (isfcn(p)) + SYNTAX("you can't define function %s more than once", p->nval); + curfname = p->nval; +} + +int constnode(Node *p) +{ + return isvalue(p) && ((Cell *) (p->narg[0]))->csub == CCON; +} + +char *strnode(Node *p) +{ + return ((Cell *)(p->narg[0]))->sval; +} + +Node *notnull(Node *n) +{ + switch (n->nobj) { + case LE: case LT: case EQ: case NE: case GT: case GE: + case BOR: case AND: case NOT: + return n; + default: + return op2(NE, n, nullnode); + } +} + +void checkdup(Node *vl, Cell *cp) /* check if name already in list */ +{ + char *s = cp->nval; + for ( ; vl; vl = vl->nnext) { + if (strcmp(s, ((Cell *)(vl->narg[0]))->nval) == 0) { + SYNTAX("duplicate argument %s", s); + break; + } + } +} + diff --git a/awk/Sources/awk/ytab.h b/awk/Sources/awk/ytab.h new file mode 100644 index 00000000..be2f9c97 --- /dev/null +++ b/awk/Sources/awk/ytab.h @@ -0,0 +1,257 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton interface for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + FIRSTTOKEN = 258, + PROGRAM = 259, + PASTAT = 260, + PASTAT2 = 261, + XBEGIN = 262, + XEND = 263, + NL = 264, + ARRAY = 265, + MATCH = 266, + NOTMATCH = 267, + MATCHOP = 268, + FINAL = 269, + DOT = 270, + ALL = 271, + CCL = 272, + NCCL = 273, + CHAR = 274, + OR = 275, + STAR = 276, + QUEST = 277, + PLUS = 278, + EMPTYRE = 279, + IGNORE_PRIOR_ATOM = 280, + AND = 281, + BOR = 282, + APPEND = 283, + EQ = 284, + GE = 285, + GT = 286, + LE = 287, + LT = 288, + NE = 289, + IN = 290, + ARG = 291, + BLTIN = 292, + BREAK = 293, + CLOSE = 294, + CONTINUE = 295, + DELETE = 296, + DO = 297, + EXIT = 298, + FOR = 299, + FUNC = 300, + SUB = 301, + GSUB = 302, + IF = 303, + INDEX = 304, + LSUBSTR = 305, + MATCHFCN = 306, + NEXT = 307, + NEXTFILE = 308, + ADD = 309, + MINUS = 310, + MULT = 311, + DIVIDE = 312, + MOD = 313, + ASSIGN = 314, + ASGNOP = 315, + ADDEQ = 316, + SUBEQ = 317, + MULTEQ = 318, + DIVEQ = 319, + MODEQ = 320, + POWEQ = 321, + PRINT = 322, + PRINTF = 323, + SPRINTF = 324, + ELSE = 325, + INTEST = 326, + CONDEXPR = 327, + POSTINCR = 328, + PREINCR = 329, + POSTDECR = 330, + PREDECR = 331, + VAR = 332, + IVAR = 333, + VARNF = 334, + CALL = 335, + NUMBER = 336, + STRING = 337, + REGEXPR = 338, + GETLINE = 339, + SUBSTR = 340, + SPLIT = 341, + RETURN = 342, + WHILE = 343, + CAT = 344, + UPLUS = 345, + UMINUS = 346, + NOT = 347, + POWER = 348, + INCR = 349, + DECR = 350, + INDIRECT = 351, + LASTTOKEN = 352 + }; +#endif +/* Tokens. */ +#define FIRSTTOKEN 258 +#define PROGRAM 259 +#define PASTAT 260 +#define PASTAT2 261 +#define XBEGIN 262 +#define XEND 263 +#define NL 264 +#define ARRAY 265 +#define MATCH 266 +#define NOTMATCH 267 +#define MATCHOP 268 +#define FINAL 269 +#define DOT 270 +#define ALL 271 +#define CCL 272 +#define NCCL 273 +#define CHAR 274 +#define OR 275 +#define STAR 276 +#define QUEST 277 +#define PLUS 278 +#define EMPTYRE 279 +#define IGNORE_PRIOR_ATOM 280 +#define AND 281 +#define BOR 282 +#define APPEND 283 +#define EQ 284 +#define GE 285 +#define GT 286 +#define LE 287 +#define LT 288 +#define NE 289 +#define IN 290 +#define ARG 291 +#define BLTIN 292 +#define BREAK 293 +#define CLOSE 294 +#define CONTINUE 295 +#define DELETE 296 +#define DO 297 +#define EXIT 298 +#define FOR 299 +#define FUNC 300 +#define SUB 301 +#define GSUB 302 +#define IF 303 +#define INDEX 304 +#define LSUBSTR 305 +#define MATCHFCN 306 +#define NEXT 307 +#define NEXTFILE 308 +#define ADD 309 +#define MINUS 310 +#define MULT 311 +#define DIVIDE 312 +#define MOD 313 +#define ASSIGN 314 +#define ASGNOP 315 +#define ADDEQ 316 +#define SUBEQ 317 +#define MULTEQ 318 +#define DIVEQ 319 +#define MODEQ 320 +#define POWEQ 321 +#define PRINT 322 +#define PRINTF 323 +#define SPRINTF 324 +#define ELSE 325 +#define INTEST 326 +#define CONDEXPR 327 +#define POSTINCR 328 +#define PREINCR 329 +#define POSTDECR 330 +#define PREDECR 331 +#define VAR 332 +#define IVAR 333 +#define VARNF 334 +#define CALL 335 +#define NUMBER 336 +#define STRING 337 +#define REGEXPR 338 +#define GETLINE 339 +#define SUBSTR 340 +#define SPLIT 341 +#define RETURN 342 +#define WHILE 343 +#define CAT 344 +#define UPLUS 345 +#define UMINUS 346 +#define NOT 347 +#define POWER 348 +#define INCR 349 +#define DECR 350 +#define INDIRECT 351 +#define LASTTOKEN 352 + + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +#line 41 "awkgram.y" +{ + Node *p; + Cell *cp; + int i; + char *s; +} +/* Line 1529 of yacc.c. */ +#line 250 "y.tab.h" + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + +extern __thread YYSTYPE yylval; + diff --git a/awk/Sources/ios_error.h b/awk/Sources/ios_error.h new file mode 100644 index 00000000..a4490144 --- /dev/null +++ b/awk/Sources/ios_error.h @@ -0,0 +1,103 @@ +// +// error.h +// shell_cmds_ios +// +// Created by Nicolas Holzschuch on 16/06/2017. +// Copyright © 2017 Nicolas Holzschuch. All rights reserved. +// + +#ifndef ios_error_h +#define ios_error_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +/* #define errx compileError +#define err compileError +#define warn compileError +#define warnx compileError +#ifndef printf +#define printf(...) fprintf (thread_stdout, ##__VA_ARGS__) +#endif */ + +#define putchar(a) fputc(a, thread_stdout) +#define getchar() fgetc(thread_stdin) +// these functions are defined differently in C++. The #define approach breaks things. +#ifndef __cplusplus + #define getwchar() fgetwc(thread_stdin) + #define putwchar(a) fputwc(a, thread_stdout) + // iswprint depends on the given locale, and setlocale() fails on iOS: + #define iswprint(a) 1 + #define write ios_write + #define fwrite ios_fwrite + #define puts ios_puts + #define fputs ios_fputs + #define fputc ios_fputc + #define putw ios_putw + #define fflush ios_fflush +#endif + +// Thread-local input and output streams +extern __thread FILE* thread_stdin; +extern __thread FILE* thread_stdout; +extern __thread FILE* thread_stderr; + +#define exit ios_exit +#define abort() ios_exit(1) +#define _exit ios_exit +#define kill ios_killpid +#define _kill ios_killpid +#define killpg ios_killpid +#define popen ios_popen +#define pclose fclose +#define system ios_system +#define execv ios_execv +#define execvp ios_execv +#define execve ios_execve +#define dup2 ios_dup2 +#define getenv ios_getenv + +extern int ios_executable(const char* cmd); // is this command part of the "shell" commands? +extern int ios_system(const char* inputCmd); // execute this command (executable file or builtin command) +extern FILE *ios_popen(const char *command, const char *type); // Execute this command and pipe the result +extern int ios_kill(void); // kill the current running command +extern int ios_killpid(pid_t pid, int sig); // kill the current running command + +extern void ios_exit(int errorCode) __dead2; // set error code and exits from the thread. +extern int ios_execv(const char *path, char* const argv[]); +extern int ios_execve(const char *path, char* const argv[], char** envlist); +extern int ios_dup2(int fd1, int fd2); +extern char * ios_getenv(const char *name); + +extern int ios_isatty(int fd); +extern pthread_t ios_getLastThreadId(void); // deprecated +extern pthread_t ios_getThreadId(pid_t pid); +extern void ios_storeThreadId(pthread_t thread); +extern void ios_releaseThread(pthread_t thread); +extern void ios_releaseThreadId(pid_t pid); +extern pid_t ios_currentPid(void); +extern int ios_getCommandStatus(void); +extern const char* ios_progname(void); +extern pid_t ios_fork(void); +extern void ios_waitpid(pid_t pid); +extern void ios_signal(int signal); + +extern ssize_t ios_write(int fildes, const void *buf, size_t nbyte); +extern size_t ios_fwrite(const void *ptr, size_t size, size_t nitems, FILE *stream); +extern int ios_puts(const char *s); +extern int ios_fputs(const char* s, FILE *stream); +extern int ios_fputc(int c, FILE *stream); +extern int ios_putw(int w, FILE *stream); +extern int ios_fflush(FILE *stream); +extern int ios_gettty(void); + +#ifdef __cplusplus +} +#endif +#endif /* ios_error_h */ diff --git a/awk/Tests/LinuxMain.swift b/awk/Tests/LinuxMain.swift new file mode 100644 index 00000000..acc48e88 --- /dev/null +++ b/awk/Tests/LinuxMain.swift @@ -0,0 +1,7 @@ +import XCTest + +import awkTests + +var tests = [XCTestCaseEntry]() +tests += awkTests.allTests() +XCTMain(tests) diff --git a/awk/Tests/awkTests/XCTestManifests.swift b/awk/Tests/awkTests/XCTestManifests.swift new file mode 100644 index 00000000..eab6d697 --- /dev/null +++ b/awk/Tests/awkTests/XCTestManifests.swift @@ -0,0 +1,9 @@ +import XCTest + +#if !canImport(ObjectiveC) +public func allTests() -> [XCTestCaseEntry] { + return [ + testCase(awkTests.allTests), + ] +} +#endif diff --git a/awk/Tests/awkTests/awkTests.swift b/awk/Tests/awkTests/awkTests.swift new file mode 100644 index 00000000..f695f0d8 --- /dev/null +++ b/awk/Tests/awkTests/awkTests.swift @@ -0,0 +1,15 @@ +import XCTest +@testable import awk + +final class awkTests: XCTestCase { + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct + // results. + XCTAssertEqual(awk().text, "Hello, World!") + } + + static var allTests = [ + ("testExample", testExample), + ] +} diff --git a/awk/awk.h b/awk/awk.h deleted file mode 100644 index cfd092e4..00000000 --- a/awk/awk.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// awk.h -// awk -// -// Created by Nicolas Holzschuch on 25/03/2018. -// Copyright © 2018 Nicolas Holzschuch. All rights reserved. -// - -#import - -//! Project version number for awk. -FOUNDATION_EXPORT double awkVersionNumber; - -//! Project version string for awk. -FOUNDATION_EXPORT const unsigned char awkVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import - - diff --git a/curl_ios_static.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/curl_ios_static.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/curl_ios_static.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/curl_ios_static.xcodeproj/xcuserdata/holzschu.xcuserdatad/xcschemes/xcschememanagement.plist b/curl_ios_static.xcodeproj/xcuserdata/holzschu.xcuserdatad/xcschemes/xcschememanagement.plist index 3a332fc4..fdb1e399 100644 --- a/curl_ios_static.xcodeproj/xcuserdata/holzschu.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/curl_ios_static.xcodeproj/xcuserdata/holzschu.xcuserdatad/xcschemes/xcschememanagement.plist @@ -14,6 +14,11 @@ orderHint 7 + curl_ios_static.xcscheme_^#shared#^_ + + orderHint + 7 + diff --git a/files/.gitignore b/files/.gitignore new file mode 100644 index 00000000..95c43209 --- /dev/null +++ b/files/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ diff --git a/files/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/files/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/files/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/files/.swiftpm/xcode/xcshareddata/xcschemes/files.xcscheme b/files/.swiftpm/xcode/xcshareddata/xcschemes/files.xcscheme new file mode 100644 index 00000000..352da58d --- /dev/null +++ b/files/.swiftpm/xcode/xcshareddata/xcschemes/files.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/Package.swift b/files/Package.swift new file mode 100644 index 00000000..495bdad3 --- /dev/null +++ b/files/Package.swift @@ -0,0 +1,36 @@ +// swift-tools-version:5.2 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "files", + // thread-local variables are only available with iOS 11+. This setting is required for compilation. + platforms: [.iOS(.v11)], + products: [ + // Products define the executables and libraries produced by a package, and make them visible to other packages. + .library( + name: "files", + targets: ["files"]), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0"), + // Depends on the local package, ios_system: + .package(path: "../ios_system") + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages which this package depends on. + .target( + name: "files", + dependencies: ["ios_system"], + exclude: ["gzip/unbzip2.c", "gzip/zuncompress.c", "gzip/unpack.c"], + cSettings: [.define("COLORLS", to: "1"), + .headerSearchPath("./"), + .headerSearchPath("../"), + .unsafeFlags([""])], + linkerSettings: [.linkedLibrary("libz2"), .linkedLibrary("libncurses")] + ), + ] +) diff --git a/files/README.md b/files/README.md new file mode 100644 index 00000000..3de1e0a3 --- /dev/null +++ b/files/README.md @@ -0,0 +1,8 @@ +# files: command for processing files + +Swift package for all commands for processing files: chflags, chmod, chown, cksum, compress, cp, df, du, gzip, less, ln, ls, mkdir, mv, rm, rmdir, stat, touch. + +This package depends on the `ios_system` package (also included in this repository). + +The `ios_error.h` file was copied from `ios_system` (because C compilers have issues with loading files from inside a package). +TODO: make it a resource downloaded from the repository (will require the next version of Xcode, currently in beta). diff --git a/files/Sources/files/chflags/chflags.1 b/files/Sources/files/chflags/chflags.1 new file mode 100644 index 00000000..5eb3dfc8 --- /dev/null +++ b/files/Sources/files/chflags/chflags.1 @@ -0,0 +1,193 @@ +.\"- +.\" Copyright (c) 1989, 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" 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. +.\" +.\" @(#)chflags.1 8.4 (Berkeley) 5/2/95 +.\" $FreeBSD: src/bin/chflags/chflags.1,v 1.30.2.1.2.1 2009/10/25 01:10:29 kensmith Exp $ +.\" +.Dd March 3, 2006 +.Dt CHFLAGS 1 +.Os +.Sh NAME +.Nm chflags +.Nd change file flags +.Sh SYNOPSIS +.Nm +.Op Fl fhv +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Ar flags +.Ar +.Sh DESCRIPTION +The +.Nm +utility modifies the file flags of the listed files +as specified by the +.Ar flags +operand. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl f +Do not display a diagnostic message if +.Nm +could not modify the flags for +.Va file , +nor modify the exit status to reflect such failures. +.It Fl H +If the +.Fl R +option is specified, symbolic links on the command line are followed. +(Symbolic links encountered in the tree traversal are not followed.) +.It Fl h +If the +.Ar file +is a symbolic link, +change the file flags of the link itself rather than the file to which it points. +.It Fl L +If the +.Fl R +option is specified, all symbolic links are followed. +.It Fl P +If the +.Fl R +option is specified, no symbolic links are followed. +This is the default. +.It Fl R +Change the file flags for the file hierarchies rooted +in the files instead of just the files themselves. +.It Fl v +Cause +.Nm +to be verbose, showing filenames as the flags are modified. +If the +.Fl v +option is specified more than once, the old and new flags of the file +will also be printed, in octal notation. +.El +.Pp +The flags are specified as an octal number or a comma separated list +of keywords. +The following keywords are currently defined: +.Pp +.Bl -tag -offset indent -width ".Cm opaque" +.It Cm arch , archived +set the archived flag (super-user only) +.It Cm opaque +set the opaque flag (owner or super-user only). +[Directory is opaque when viewed through a union mount] +.It Cm nodump +set the nodump flag (owner or super-user only) +.It Cm sappnd , sappend +set the system append-only flag (super-user only) +.It Cm schg , schange , simmutable +set the system immutable flag (super-user only) +.It Cm uappnd , uappend +set the user append-only flag (owner or super-user only) +.It Cm uchg , uchange , uimmutable +set the user immutable flag (owner or super-user only) +.It Cm hidden +set the hidden flag +[Hide item from GUI] +.El +.Pp +As discussed in +.Xr chflags 2 , +the +.Ar sappnd +and +.Ar schg +flags may only be unset when the system is in single-user mode. +.Pp +Putting the letters +.Dq Ar no +before or removing the letters +.Dq Ar no +from a keyword causes the flag to be cleared. +For example: +.Pp +.Bl -tag -offset indent -width "nouchg" -compact +.It Ar nouchg +clear the user immutable flag (owner or super-user only) +.It Ar dump +clear the nodump flag (owner or super-user only) +.El +.Pp +Unless the +.Fl H +or +.Fl L +options are given, +.Nm +on a symbolic link always succeeds and has no effect. +The +.Fl H , +.Fl L +and +.Fl P +options are ignored unless the +.Fl R +option is specified. +In addition, these options override each other and the +command's actions are determined by the last one specified. +.Pp +You can use "ls -lO" to see the flags of existing files. +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr ls 1 , +.Xr chflags 2 , +.Xr stat 2 , +.Xr fts 3 , +.Xr symlink 7 +.Sh HISTORY +The +.Nm +command first appeared in +.Bx 4.4 . +.Sh BUGS +Only a limited number of utilities are +.Nm +aware. +Some of these tools include +.Xr ls 1 , +.Xr cp 1 , +.Xr find 1 , +.Xr install 1 , +.Xr dump 8 , +and +.Xr restore 8 . +In particular a tool which is not currently +.Nm +aware is the +.Xr pax 1 +utility. diff --git a/files/Sources/files/chflags/chflags.c b/files/Sources/files/chflags/chflags.c new file mode 100644 index 00000000..8f5d478c --- /dev/null +++ b/files/Sources/files/chflags/chflags.c @@ -0,0 +1,214 @@ +/*- + * Copyright (c) 1992, 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. + */ + +#if 0 +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif + +#ifndef lint +static char sccsid[] = "@(#)chflags.c 8.5 (Berkeley) 4/1/94"; +#endif +#endif + +#include +__FBSDID("$FreeBSD: src/bin/chflags/chflags.c,v 1.26.2.1.2.1 2009/10/25 01:10:29 kensmith Exp $"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "ios_error.h" + + +static void usage(void); + +int +chflags_main(int argc, char *argv[]) +{ + FTS *ftsp; + FTSENT *p; + u_long clear, newflags, set; + long val; + int Hflag, Lflag, Rflag, fflag, hflag, vflag; + int ch, fts_options, oct, rval; + char *flags, *ep; + int (*change_flags)(const char *, u_int); + + Hflag = Lflag = Rflag = fflag = hflag = vflag = 0; + optind = 1; opterr = 1; optreset = 1; + + while ((ch = getopt(argc, argv, "HLPRfhv")) != -1) + switch (ch) { + case 'H': + Hflag = 1; + Lflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = 0; + break; + case 'P': + Hflag = Lflag = 0; + break; + case 'R': + Rflag = 1; + break; + case 'f': + fflag = 1; + break; + case 'h': + hflag = 1; + break; + case 'v': + vflag++; + break; + case '?': + default: + usage(); + } + argv += optind; + argc -= optind; + + if (argc < 2) + usage(); + + if (Rflag) { + fts_options = FTS_PHYSICAL; + if (hflag) { + errx(1, "the -R and -h options " + "may not be specified together"); + } + if (Hflag) + fts_options |= FTS_COMFOLLOW; + if (Lflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + } else + fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL; + + if (hflag) + change_flags = lchflags; + else + change_flags = chflags; + + flags = *argv; + if (*flags >= '0' && *flags <= '7') { + errno = 0; + val = strtol(flags, &ep, 8); + if (val < 0) + errno = ERANGE; + if (errno) { + err(1, "invalid flags: %s", flags); + } + if (*ep) { + errx(1, "invalid flags: %s", flags); + } + set = val; + oct = 1; + } else { + if (strtofflags(&flags, &set, &clear)) { + errx(1, "invalid flag: %s", flags); + } + clear = ~clear; + oct = 0; + } + + if ((ftsp = fts_open(++argv, fts_options , 0)) == NULL) { + err(1, NULL); + } + + for (rval = 0; (p = fts_read(ftsp)) != NULL;) { + switch (p->fts_info) { + case FTS_D: /* Change it at FTS_DP if we're recursive. */ + if (!Rflag) + fts_set(ftsp, p, FTS_SKIP); + continue; + case FTS_DNR: /* Warn, chflag, continue. */ + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + break; + case FTS_ERR: /* Warn, continue. */ + case FTS_NS: + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + continue; + case FTS_SL: /* Ignore. */ + case FTS_SLNONE: + /* + * The only symlinks that end up here are ones that + * don't point to anything and ones that we found + * doing a physical walk. + */ + if (!hflag) + continue; + /* FALLTHROUGH */ + default: + break; + } + if (oct) + newflags = set; + else + newflags = (p->fts_statp->st_flags | set) & clear; + if (newflags == p->fts_statp->st_flags) + continue; + if ((*change_flags)(p->fts_accpath, (u_int)newflags) && !fflag) { + warn("%s", p->fts_path); + rval = 1; + } else if (vflag) { + (void)fprintf(thread_stdout, "%s", p->fts_path); + if (vflag > 1) + (void)fprintf(thread_stdout, ": 0%lo -> 0%lo", + (u_long)p->fts_statp->st_flags, + newflags); + (void)fprintf(thread_stdout, "\n"); + } + } + if (errno) { + err(1, "fts_read"); + } + exit(rval); +} + +static void +usage(void) +{ + (void)fprintf(stderr, + "usage: chflags [-fhv] [-R [-H | -L | -P]] flags file ...\n"); + exit(1); +} diff --git a/files/Sources/files/chmod/chmod.1 b/files/Sources/files/chmod/chmod.1 new file mode 100644 index 00000000..dccb9539 --- /dev/null +++ b/files/Sources/files/chmod/chmod.1 @@ -0,0 +1,583 @@ +.\" Copyright (c) 1989, 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 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. +.\" +.\" @(#)chmod.1 8.4 (Berkeley) 3/31/94 +.\" $FreeBSD: src/bin/chmod/chmod.1,v 1.33 2002/10/01 20:32:59 trhodes Exp $ +.\" +.Dd July 08, 2004 +.Dt CHMOD 1 +.Os +.Sh NAME +.Nm chmod +.Nd change file modes or Access Control Lists +.Sh SYNOPSIS +.Nm chmod +.Op Fl fv +.Op Fl R Op Fl H | L | P +.Ar mode +.Ar +.Nm chmod +.Op Fl fv +.Op Fl R Op Fl H | L | P +.Op -a | +a | =a +.Ar ACE +.Ar +.Nm chmod +.Op Fl fhv +.Op Fl R Op Fl H | L | P +.Op Fl E +.Ar +.Nm chmod +.Op Fl fhv +.Op Fl R Op Fl H | L | P +.Op Fl C +.Ar +.Nm chmod +.Op Fl fhv +.Op Fl R Op Fl H | L | P +.Op Fl N +.Ar +.Sh DESCRIPTION +The +.Nm chmod +utility modifies the file mode bits of the listed files +as specified by the +.Ar mode +operand. It may also be used to modify the Access Control +Lists (ACLs) associated with the listed files. +.Pp +The generic options are as follows: +.Bl -tag -width Ds +.It Fl f +Do not display a diagnostic message if +.Nm chmod +could not modify the mode for +.Va file . +.It Fl H +If the +.Fl R +option is specified, symbolic links on the command line are followed. +(Symbolic links encountered in the tree traversal are not followed by +default.) +.It Fl h +If the file is a symbolic link, change the mode of the link itself +rather than the file that the link points to. +.It Fl L +If the +.Fl R +option is specified, all symbolic links are followed. +.It Fl P +If the +.Fl R +option is specified, no symbolic links are followed. +This is the default. +.It Fl R +Change the modes of the file hierarchies rooted in the files +instead of just the files themselves. +.It Fl v +Cause +.Nm chmod +to be verbose, showing filenames as the mode is modified. +If the +.Fl v +flag is specified more than once, the old and new modes of the file +will also be printed, in both octal and symbolic notation. +.El +.Pp +The +.Fl H , +.Fl L +and +.Fl P +options are ignored unless the +.Fl R +option is specified. +In addition, these options override each other and the +command's actions are determined by the last one specified. +.Pp +Only the owner of a file or the super-user is permitted to change +the mode of a file. +.Sh DIAGNOSTICS +.Ex -std +.Sh MODES +Modes may be absolute or symbolic. +An absolute mode is an octal number constructed from the sum of +one or more of the following values: +.Pp +.Bl -tag -width 6n -compact -offset indent +.It Li 4000 +(the set-user-ID-on-execution bit) Executable files with this bit set +will run with effective uid set to the uid of the file owner. +Directories with the set-user-id bit set will force all files and +sub-directories created in them to be owned by the directory owner +and not by the uid of the creating process, if the underlying file +system supports this feature: see +.Xr chmod 2 +and the +.Ar suiddir +option to +.Xr mount 8 . +.It Li 2000 +(the set-group-ID-on-execution bit) Executable files with this bit set +will run with effective gid set to the gid of the file owner. +.It Li 1000 +(the sticky bit) +See +.Xr chmod 2 +and +.Xr sticky 8 . +.It Li 0400 +Allow read by owner. +.It Li 0200 +Allow write by owner. +.It Li 0100 +For files, allow execution by owner. +For directories, allow the owner to +search in the directory. +.It Li 0040 +Allow read by group members. +.It Li 0020 +Allow write by group members. +.It Li 0010 +For files, allow execution by group members. +For directories, allow +group members to search in the directory. +.It Li 0004 +Allow read by others. +.It Li 0002 +Allow write by others. +.It Li 0001 +For files, allow execution by others. +For directories allow others to +search in the directory. +.El +.Pp +For example, the absolute mode that permits read, write and execute by +the owner, read and execute by group members, read and execute by +others, and no set-uid or set-gid behaviour is 755 +(400+200+100+040+010+004+001). +.Pp +The symbolic mode is described by the following grammar: +.Bd -literal -offset indent +mode ::= clause [, clause ...] +clause ::= [who ...] [action ...] action +action ::= op [perm ...] +who ::= a | u | g | o +op ::= + | \- | = +perm ::= r | s | t | w | x | X | u | g | o +.Ed +.Pp +The +.Ar who +symbols ``u'', ``g'', and ``o'' specify the user, group, and other parts +of the mode bits, respectively. +The +.Ar who +symbol ``a'' is equivalent to ``ugo''. +.Pp +The +.Ar perm +symbols represent the portions of the mode bits as follows: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It r +The read bits. +.It s +The set-user-ID-on-execution and set-group-ID-on-execution bits. +.It t +The sticky bit. +.It w +The write bits. +.It x +The execute/search bits. +.It X +The execute/search bits if the file is a directory or any of the +execute/search bits are set in the original (unmodified) mode. +Operations with the +.Ar perm +symbol ``X'' are only meaningful in conjunction with the +.Ar op +symbol ``+'', and are ignored in all other cases. +.It u +The user permission bits in the original mode of the file. +.It g +The group permission bits in the original mode of the file. +.It o +The other permission bits in the original mode of the file. +.El +.Pp +The +.Ar op +symbols represent the operation performed, as follows: +.Bl -tag -width 4n +.It + +If no value is supplied for +.Ar perm , +the ``+'' operation has no effect. +If no value is supplied for +.Ar who , +each permission bit specified in +.Ar perm , +for which the corresponding bit in the file mode creation mask +is clear, is set. +Otherwise, the mode bits represented by the specified +.Ar who +and +.Ar perm +values are set. +.It \&\- +If no value is supplied for +.Ar perm , +the ``\-'' operation has no effect. +If no value is supplied for +.Ar who , +each permission bit specified in +.Ar perm , +for which the corresponding bit in the file mode creation mask +is clear, is cleared. +Otherwise, the mode bits represented by the specified +.Ar who +and +.Ar perm +values are cleared. +.It = +The mode bits specified by the +.Ar who +value are cleared, or, if no who value is specified, the owner, group +and other mode bits are cleared. +Then, if no value is supplied for +.Ar who , +each permission bit specified in +.Ar perm , +for which the corresponding bit in the file mode creation mask +is clear, is set. +Otherwise, the mode bits represented by the specified +.Ar who +and +.Ar perm +values are set. +.El +.Pp +Each +.Ar clause +specifies one or more operations to be performed on the mode +bits, and each operation is applied to the mode bits in the +order specified. +.Pp +Operations upon the other permissions only (specified by the symbol +``o'' by itself), in combination with the +.Ar perm +symbols ``s'' or ``t'', are ignored. +.Sh EXAMPLES OF VALID MODES +.Bl -tag -width "u=rwx,go=u-w" -compact +.It Li 644 +make a file readable by anyone and writable by the owner only. +.Pp +.It Li go-w +deny write permission to group and others. +.Pp +.It Li =rw,+X +set the read and write permissions to the usual defaults, but +retain any execute permissions that are currently set. +.Pp +.It Li +X +make a directory or file searchable/executable by everyone if it is +already searchable/executable by anyone. +.Pp +.It Li 755 +.It Li u=rwx,go=rx +.It Li u=rwx,go=u-w +make a file readable/executable by everyone and writable by the owner only. +.Pp +.It Li go= +clear all mode bits for group and others. +.Pp +.It Li g=u-w +set the group bits equal to the user bits, but clear the group write bit. +.El +.Sh ACL MANIPULATION OPTIONS +ACLs are manipulated using extensions to the symbolic mode +grammar. Each file has one ACL, containing an ordered list of entries. +Each entry refers to a user or group, and grants or denies a set of +permissions. +In cases where a user and a group exist with the same name, the +user/group name can be prefixed with "user:" or "group:" in order to +specify the type of name. +.Pp +If the user or group name contains spaces you can use ':' as the delimiter +between name and permission. +.Pp +The following permissions are applicable to all filesystem objects: +.Bl -tag -width 6n -compact -offset indent +.It delete +Delete the item. Deletion may be granted by either this permission +on an object or the delete_child right on the containing directory. +.It readattr +Read an objects basic attributes. This is implicitly granted if +the object can be looked up and not explicitly denied. +.It writeattr +Write an object's basic attributes. +.It readextattr +Read extended attributes. +.It writeextattr +Write extended attributes. +.It readsecurity +Read an object's extended security information (ACL). +.It writesecurity +Write an object's security information (ownership, mode, ACL). +.It chown +Change an object's ownership. +.El +.Pp +The following permissions are applicable to directories: +.Bl -tag -width 6n -compact -offset indent +.It list +List entries. +.It search +Look up files by name. +.It add_file +Add a file. +.It add_subdirectory +Add a subdirectory. +.It delete_child +Delete a contained object. See the file delete permission above. +.El +.Pp +The following permissions are applicable to non-directory filesystem objects: +.Bl -tag -width 6n -compact -offset indent +.It read +Open for reading. +.It write +Open for writing. +.It append +Open for writing, but in a fashion that only allows writes into areas of +the file not previously written. +.It execute +Execute the file as a script or program. +.El +.Pp +ACL inheritance is controlled with the following permissions words, which +may only be applied to directories: +.Bl -tag -width 6n -compact -offset indent +.It file_inherit +Inherit to files. +.It directory_inherit +Inherit to directories. +.It limit_inherit +This flag is only relevant to entries inherited by subdirectories; it +causes the directory_inherit flag to be cleared in the entry that is +inherited, preventing further nested subdirectories from also +inheriting the entry. +.It only_inherit +The entry is inherited by created items but not considered when processing +the ACL. +.El +.Pp +The ACL manipulation options are as follows: +.Bl -tag -width Ds +.It \fB+a\fR +The +a mode parses a new ACL entry from the next argument on +the commandline and inserts it into the canonical location in the +ACL. If the supplied entry refers to an identity already listed, the +two entries are combined. +.Pp +\fBExamples\fR + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + # chmod +a "admin allow write" file1 + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: admin allow write + # chmod +a "guest deny read" file1 + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: guest deny read + 2: admin allow write + # chmod +a "admin allow delete" file1 + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: guest deny read + 2: admin allow write,delete + # chmod +a "User 1:allow:read" file + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: guest deny read + 2: User 1 allow read + 3: admin allow write,delete +.Pp +The +a mode strives to maintain correct canonical form for the ACL. + local deny + local allow + inherited deny + inherited allow +.Pp +By default, chmod adds entries to the top of the local deny and local +allow lists. Inherited entries are added by using the +ai mode. +.Pp +\fBExamples\fR + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: guest deny read + 2: admin allow write,delete + 3: juser inherited deny delete + 4: admin inherited allow delete + 5: backup inherited deny read + 6: admin inherited allow write-security + # chmod +ai "others allow read" file1 + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: guest deny read + 2: admin allow write,delete + 3: juser inherited deny delete + 4: others inherited allow read + 5: admin inherited allow delete + 6: backup inherited deny read + 7: admin inherited allow write-security +.It \fB+a#\fR +When a specific ordering is required, the exact location at which an +entry will be inserted is specified with the +a# mode. +.Pp +\fBExamples\fR + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: guest deny read + 2: admin allow write + # chmod +a# 2 "others deny read" file1 + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: guest deny read + 2: others deny read + 3: admin allow write +.Pp +The +ai# mode may be used to insert inherited entries at a specific +location. Note that these modes allow non-canonical ACL ordering to +be constructed. +.It Fl a +The -a mode is used to delete ACL entries. All entries exactly +matching the supplied entry will be deleted. If the entry lists a +subset of rights granted by an entry, only the rights listed are +removed. Entries may also be deleted by index using the -a# mode. +.Pp +\fBExamples\fR + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: guest deny read + 2: admin allow write,delete + # chmod -a# 1 file1 + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: admin allow write,delete + # chmod -a "admin allow write" file1 + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: admin allow delete +.Pp +Inheritance is not considered when processing the -a mode; rights and +entries will be removed regardless of their inherited state. +.Pp +If the user or group name contains spaces you can use ':' as the delimiter +.Pp +\fBExample\fR + # chmod +a "User 1:allow:read" file +.It \fB=a#\fR +Individual entries are rewritten using the =a# mode. +.Pp +\fBExamples\fR + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: admin allow delete + # chmod =a# 1 "admin allow write,chown" + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: admin allow write,chown +.Pp +This mode may not be used to add new entries. +.It Fl E +Reads the ACL information from stdin, as a sequential list +of ACEs, separated by newlines. If the information parses correctly, +the existing information is replaced. +.It Fl C +Returns false if any of the named files have ACLs in non-canonical order. +.It Fl i +Removes the 'inherited' bit from all entries in the named file(s) ACLs. +.It Fl I +Removes all inherited entries from the named file(s) ACL(s). +.It Fl N +Removes the ACL from the named file(s). +.El +.Sh COMPATIBILITY +The +.Fl v +option is non-standard and its use in scripts is not recommended. +.Sh SEE ALSO +.Xr chflags 1 , +.Xr fsaclctl 1 , +.Xr install 1 , +.Xr chmod 2 , +.Xr stat 2 , +.Xr umask 2 , +.Xr fts 3 , +.Xr setmode 3 , +.Xr symlink 7 , +.Xr chown 8 , +.Xr mount 8 , +.Xr sticky 8 +.Sh STANDARDS +The +.Nm chmod +utility is expected to be +.St -p1003.2 +compatible with the exception of the +.Ar perm +symbol +.Dq t +which is not included in that standard. +.Sh HISTORY +A +.Nm chmod +command appeared in +.At v1 . diff --git a/files/Sources/files/chmod/chmod.c b/files/Sources/files/chmod/chmod.c new file mode 100644 index 00000000..92a2ed9c --- /dev/null +++ b/files/Sources/files/chmod/chmod.c @@ -0,0 +1,469 @@ +/* + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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 +#ifndef lint +__used static char const copyright[] = +"@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)chmod.c 8.8 (Berkeley) 4/1/94"; +#endif +#endif /* not lint */ +#include +__RCSID("$FreeBSD: src/bin/chmod/chmod.c,v 1.27 2002/08/04 05:29:13 obrien Exp $"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include "chmod_acl.h" +#include "ios_error.h" // remap exit to pthread_exit + +#endif /*__APPLE__*/ + +__thread int chmod_fflag = 0; + +int chmod_main(int, char *[]); +void chmod_usage(void); + +int +chmod_main(int argc, char *argv[]) +{ + chmod_fflag = 0; + FTS *ftsp = NULL; + FTSENT *p = NULL; + mode_t *set = NULL; + long val = 0; + int oct = 0; + int Hflag, Lflag, Pflag, Rflag, ch, fts_options, hflag, rval; + int vflag; + char *ep, *mode; + mode_t newmode, omode; +#ifdef __APPLE__ + unsigned int acloptflags = 0; + long aclpos = -1; + int inheritance_level = 0; + int index = 0; + size_t acloptlen = 0; + int ace_arg_not_required = 0; + acl_t acl_input = NULL; + optind = 1; opterr = 1; optreset = 1; +#endif /* __APPLE__*/ + int (*change_mode)(const char *, mode_t); + + set = NULL; + omode = 0; + Hflag = Lflag = Pflag = Rflag = chmod_fflag = hflag = vflag = 0; +#ifndef __APPLE__ + while ((ch = getopt(argc, argv, "HLPRXfghorstuvwx")) != -1) +#else + while ((ch = getopt(argc, argv, "ACEHILNPRVXafghinorstuvwx")) != -1) +#endif + switch (ch) { + case 'H': + Hflag = 1; + Lflag = 0; + Pflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = 0; + Pflag = 0; + break; + case 'P': + Hflag = Lflag = 0; + Pflag = 1; + break; + case 'R': + Rflag = 1; + break; + case 'f': + chmod_fflag = 1; + break; + case 'h': + /* + * In System V (and probably POSIX.2) the -h option + * causes chmod to change the mode of the symbolic + * link. 4.4BSD's symbolic links didn't have modes, + * so it was an undocumented noop. In FreeBSD 3.0, + * lchmod(2) is introduced and this option does real + * work. + */ + hflag = 1; + break; +#ifdef __APPLE__ + case 'a': + if (argv[optind - 1][0] == '-' && + argv[optind - 1][1] == ch) + --optind; + goto done; + case 'A': +// acloptflags |= ACL_FLAG | ACL_TO_STDOUT; +// ace_arg_not_required = 1; + errx(1, "-A not implemented"); + goto done; + case 'E': + acloptflags |= ACL_FLAG | ACL_FROM_STDIN; + goto done; + case 'C': + acloptflags |= ACL_FLAG | ACL_CHECK_CANONICITY; + ace_arg_not_required = 1; + goto done; + case 'i': + acloptflags |= ACL_FLAG | ACL_REMOVE_INHERIT_FLAG; + ace_arg_not_required = 1; + goto done; + case 'I': + acloptflags |= ACL_FLAG | ACL_REMOVE_INHERITED_ENTRIES; + ace_arg_not_required = 1; + goto done; + case 'n': + acloptflags |= ACL_FLAG | ACL_NO_TRANSLATE; + break; + case 'N': + acloptflags |= ACL_FLAG | ACL_CLEAR_FLAG; + ace_arg_not_required = 1; + goto done; + case 'V': +// acloptflags |= ACL_FLAG | ACL_INVOKE_EDITOR; +// ace_arg_not_required = 1; + errx(1, "-V not implemented"); + goto done; +#endif /* __APPLE__ */ + /* + * XXX + * "-[rwx]" are valid mode commands. If they are the entire + * argument, getopt has moved past them, so decrement optind. + * Regardless, we're done argument processing. + */ + case 'g': case 'o': case 'r': case 's': + case 't': case 'u': case 'w': case 'X': case 'x': + if (argv[optind - 1][0] == '-' && + argv[optind - 1][1] == ch && + argv[optind - 1][2] == '\0') + --optind; + goto done; + case 'v': + vflag++; + break; + case '?': + default: + chmod_usage(); + } +done: argv += optind; + argc -= optind; + +#ifdef __APPLE__ + if (argc < ((acloptflags & ACL_FLAG) ? 1 : 2)) + chmod_usage(); + if (!Rflag && (Hflag || Lflag || Pflag)) + warnx("options -H, -L, -P only useful with -R"); +#else /* !__APPLE__ */ + if (argc < 2) + chmod_usage(); +#endif /* __APPLE__ */ + +#ifdef __APPLE__ + if (!(acloptflags & ACL_FLAG) && ((acloptlen = strlen(argv[0])) > 1) && (argv[0][1] == 'a')) { + acloptflags |= ACL_FLAG; + switch (argv[0][0]) { + case '+': + acloptflags |= ACL_SET_FLAG; + break; + case '-': + acloptflags |= ACL_DELETE_FLAG; + break; + case '=': + acloptflags |= ACL_REWRITE_FLAG; + break; + default: + acloptflags &= ~ACL_FLAG; + goto apnoacl; + } + + if (argc < 3) + chmod_usage(); + + if (acloptlen > 2) { + for (index = 2; index < acloptlen; index++) { + switch (argv[0][index]) { + case '#': + acloptflags |= ACL_ORDER_FLAG; + + if (argc < ((acloptflags & ACL_DELETE_FLAG) + ? 3 : 4)) + chmod_usage(); + argv++; + argc--; + errno = 0; + aclpos = strtol(argv[0], &ep, 0); + + if (aclpos > ACL_MAX_ENTRIES + || aclpos < 0) + errno = ERANGE; + if (errno || *ep) { + errx(1, "Invalid ACL entry number: %ld", aclpos); + } + if (acloptflags & ACL_DELETE_FLAG) + ace_arg_not_required = 1; + + goto apdone; + case 'i': + acloptflags |= ACL_INHERIT_FLAG; + /* The +aii.. syntax to specify + * inheritance level is rather unwieldy, + * find an alternative. + */ + inheritance_level++; + if (inheritance_level > 1) + warnx("Inheritance across more than one generation is not currently supported"); + if (inheritance_level >= MAX_INHERITANCE_LEVEL) + goto apdone; + break; + default: + errno = EINVAL; + chmod_usage(); + } + } + } +apdone: + argv++; + argc--; + } +apnoacl: +#endif /*__APPLE__*/ + + if (Rflag) { + fts_options = FTS_PHYSICAL; + if (hflag) { + errx(1, "the -R and -h options may not be specified together."); + } + if (Hflag) + fts_options |= FTS_COMFOLLOW; + if (Lflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + } else + fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL; + + if (hflag) + change_mode = lchmod; + else + change_mode = chmod; +#ifdef __APPLE__ + if (acloptflags & ACL_FROM_STDIN) { + ssize_t readval = 0; + size_t readtotal = 0; + + mode = (char *) malloc(MAX_ACL_TEXT_SIZE); + + if (mode == NULL) { + err(1, "Unable to allocate mode string"); + } + /* Read the ACEs from STDIN */ + do { + readtotal += readval; + readval = read(fileno(thread_stdin), mode + readtotal, + MAX_ACL_TEXT_SIZE); + } while ((readval > 0) && (readtotal <= MAX_ACL_TEXT_SIZE)); + + if (0 == readtotal) { + errx(1, "-E specified, but read from STDIN failed"); + } + else + mode[readtotal - 1] = '\0'; + --argv; + } + else +#endif /* __APPLE */ + mode = *argv; + +#ifdef __APPLE__ + if ((acloptflags & ACL_FLAG)) { + + /* Are we deleting by entry number, verifying + * canonicity or performing some other operation that + * does not require an input entry? If so, there's no + * entry to convert. + */ + if (ace_arg_not_required) { + --argv; + } + else { + /* Parse the text into an ACL*/ + acl_input = parse_acl_entries(mode); + if (acl_input == NULL) { + errx(1, "Invalid ACL specification: %s", mode); + } + } + } + else { +#endif /* __APPLE__*/ + if (*mode >= '0' && *mode <= '7') { + errno = 0; + val = strtol(mode, &ep, 8); + if (val > USHRT_MAX || val < 0) + errno = ERANGE; + if (errno) { + err(1, "Invalid file mode: %s", mode); + } + if (*ep) { + errx(1, "Invalid file mode: %s", mode); + } + omode = (mode_t)val; + oct = 1; + } else { + if ((set = setmode(mode)) == NULL) { + errx(1, "Invalid file mode: %s", mode); + } + oct = 0; + } +#ifdef __APPLE__ + } +#endif /* __APPLE__*/ + if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL) { + err(1, "fts_open"); + } + for (rval = 0; (p = fts_read(ftsp)) != NULL;) { + switch (p->fts_info) { + case FTS_D: + if (!Rflag) + (void)fts_set(ftsp, p, FTS_SKIP); + break; + case FTS_DNR: /* Warn, chmod, continue. */ + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + break; + case FTS_DP: /* Already changed at FTS_D. */ + continue; + case FTS_NS: + if (acloptflags & ACL_FLAG) /* don't need stat for -N */ + break; + case FTS_ERR: /* Warn, continue. */ + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + continue; + case FTS_SL: /* Ignore. */ + case FTS_SLNONE: + /* + * The only symlinks that end up here are ones that + * don't point to anything and ones that we found + * doing a physical walk. + */ + if (!hflag) + continue; + /* else */ + /* FALLTHROUGH */ + default: + break; + } +#ifdef __APPLE__ +/* If an ACL manipulation option was specified, manipulate */ + if (acloptflags & ACL_FLAG) { + if (0 != modify_file_acl(acloptflags, p->fts_accpath, acl_input, (int)aclpos, inheritance_level, !hflag)) + rval = 1; + } + else { +#endif /* __APPLE__ */ + newmode = oct ? omode : getmode(set, p->fts_statp->st_mode); + if ((newmode & ALLPERMS) == (p->fts_statp->st_mode & ALLPERMS)) + continue; + if ((*change_mode)(p->fts_accpath, newmode) && !chmod_fflag) { + warn("Unable to change file mode on %s", p->fts_path); + rval = 1; + } else { + if (vflag) { + (void)fprintf(thread_stdout, "%s", p->fts_accpath); + + if (vflag > 1) { + char m1[12], m2[12]; + + strmode(p->fts_statp->st_mode, m1); + strmode((p->fts_statp->st_mode & + S_IFMT) | newmode, m2); + + (void)fprintf(thread_stdout, ": 0%o [%s] -> 0%o [%s]", + p->fts_statp->st_mode, m1, + (p->fts_statp->st_mode & S_IFMT) | + newmode, m2); + } + (void)fprintf(thread_stdout, "\n"); + } + + } +#ifdef __APPLE__ + } +#endif /* __APPLE__*/ + } + if (errno) { + err(1, "fts_read"); + } +#ifdef __APPLE__ + if (acl_input) + acl_free(acl_input); + if (mode && (acloptflags & ACL_FROM_STDIN)) + free(mode); + +#endif /* __APPLE__ */ + if (set) + free(set); + exit(rval); +} + +void +chmod_usage(void) +{ +#ifdef __APPLE__ + (void)fprintf(stderr, + "usage:\tchmod [-fhv] [-R [-H | -L | -P]] [-a | +a | =a [i][# [ n]]] mode|entry file ...\n" + "\tchmod [-fhv] [-R [-H | -L | -P]] [-E | -C | -N | -i | -I] file ...\n"); /* add -A and -V when implemented */ +#else + (void)fprintf(stderr, + "usage: chmod [-fhv] [-R [-H | -L | -P]] mode file ...\n"); +#endif /* __APPLE__ */ + exit(1); +} diff --git a/files/Sources/files/chmod/chmod_acl.c b/files/Sources/files/chmod/chmod_acl.c new file mode 100644 index 00000000..a2b77a9d --- /dev/null +++ b/files/Sources/files/chmod/chmod_acl.c @@ -0,0 +1,881 @@ +/* + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "chmod_acl.h" +#include "ios_error.h" + +extern void chmod_usage(void); + +#ifdef __APPLE__ +static struct { + acl_perm_t perm; + char *name; + int flags; +#define ACL_PERM_DIR (1<<0) +#define ACL_PERM_FILE (1<<1) +} acl_perms[] = { + {ACL_READ_DATA, "read", ACL_PERM_FILE}, + {ACL_LIST_DIRECTORY, "list", ACL_PERM_DIR}, + {ACL_WRITE_DATA, "write", ACL_PERM_FILE}, + {ACL_ADD_FILE, "add_file", ACL_PERM_DIR}, + {ACL_EXECUTE, "execute", ACL_PERM_FILE}, + {ACL_SEARCH, "search", ACL_PERM_DIR}, + {ACL_DELETE, "delete", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_APPEND_DATA, "append", ACL_PERM_FILE}, + {ACL_ADD_SUBDIRECTORY, "add_subdirectory", ACL_PERM_DIR}, + {ACL_DELETE_CHILD, "delete_child", ACL_PERM_DIR}, + {ACL_READ_ATTRIBUTES, "readattr", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_WRITE_ATTRIBUTES, "writeattr", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_READ_EXTATTRIBUTES, "readextattr", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_WRITE_EXTATTRIBUTES, "writeextattr", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_READ_SECURITY, "readsecurity", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_WRITE_SECURITY, "writesecurity", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_CHANGE_OWNER, "chown", ACL_PERM_FILE | ACL_PERM_DIR}, + {0, NULL, 0} +}; + +static struct { + acl_flag_t flag; + char *name; + int flags; +} acl_flags[] = { + {ACL_ENTRY_INHERITED, "inherited", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_ENTRY_FILE_INHERIT, "file_inherit", ACL_PERM_DIR}, + {ACL_ENTRY_DIRECTORY_INHERIT, "directory_inherit", ACL_PERM_DIR}, + {ACL_ENTRY_LIMIT_INHERIT, "limit_inherit", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_ENTRY_ONLY_INHERIT, "only_inherit", ACL_PERM_DIR}, + {0, NULL, 0} +}; + +/* TBD - Many of these routines could potentially be considered for + * inclusion in a library. If that is done, either avoid use of "err" + * and implement a better fall-through strategy in case of errors, + * or use err_set_exit() and make various structures globals. + */ + +#define NAME_USER (1) +#define NAME_GROUP (2) +#define NAME_EITHER (NAME_USER | NAME_GROUP) + +/* Perform a name to uuid mapping - calls through to memberd */ + +uuid_t * +name_to_uuid(char *tok, int nametype) { + uuid_t *entryg = NULL; + size_t len = strlen(tok); + + if ((entryg = (uuid_t *) calloc(1, sizeof(uuid_t))) == NULL) { + errx(1, "Unable to allocate a uuid"); + } + + if ((nametype & NAME_USER) && mbr_identifier_to_uuid(ID_TYPE_USERNAME, tok, len, *entryg) == 0) { + return entryg; + } + + if ((nametype & NAME_GROUP) && mbr_identifier_to_uuid(ID_TYPE_GROUPNAME, tok, len, *entryg) == 0) { + return entryg; + } + + free(entryg); + errx(1, "Unable to translate '%s' to a UUID", tok); + return NULL; // never reached +} + +/* Convert an acl entry in string form to an acl_entry_t */ +int +parse_entry(char *entrybuf, acl_entry_t newent) { + char *tok; + char *pebuf; + uuid_t *entryg; + + acl_tag_t tag; + acl_permset_t perms; + acl_flagset_t flags; + unsigned permcount = 0; + unsigned pindex = 0; + char *delimiter = " "; + int nametype = NAME_EITHER; + + acl_get_permset(newent, &perms); + acl_get_flagset_np(newent, &flags); + + pebuf = entrybuf; + + if (0 == strncmp(entrybuf, "user:", 5)) { + nametype = NAME_USER; + pebuf += 5; + } else if (0 == strncmp(entrybuf, "group:", 6)) { + nametype = NAME_GROUP; + pebuf += 6; + } + + if (strchr(pebuf, ':')) /* User/Group names can have spaces */ + delimiter = ":"; + tok = strsep(&pebuf, delimiter); + + if ((tok == NULL) || *tok == '\0') { + errx(1, "Invalid entry format -- expected user or group name"); + } + + /* parse the name into a qualifier */ + entryg = name_to_uuid(tok, nametype); + + tok = strsep(&pebuf, ": "); /* Stick with delimiter? */ + if ((tok == NULL) || *tok == '\0') { + errx(1, "Invalid entry format -- expected allow or deny"); + } + + /* is the verb 'allow' or 'deny'? */ + if (!strcmp(tok, "allow")) { + tag = ACL_EXTENDED_ALLOW; + } else if (!strcmp(tok, "deny")) { + tag = ACL_EXTENDED_DENY; + } else { + errx(1, "Unknown tag type '%s'", tok); + } + + /* parse permissions */ + for (; (tok = strsep(&pebuf, ",")) != NULL;) { + if (*tok != '\0') { + /* is it a permission? */ + for (pindex = 0; acl_perms[pindex].name != NULL; pindex++) { + if (!strcmp(acl_perms[pindex].name, tok)) { + /* got one */ + acl_add_perm(perms, acl_perms[pindex].perm); + permcount++; + goto found; + } + } + /* is it a flag? */ + for (pindex = 0; acl_flags[pindex].name != NULL; pindex++) { + if (!strcmp(acl_flags[pindex].name, tok)) { + /* got one */ + acl_add_flag_np(flags, acl_flags[pindex].flag); + permcount++; + goto found; + } + } + errx(1,"Invalid permission type '%s'", tok); + found: + continue; + } + } + if (0 == permcount) { + errx(1, "No permissions specified"); + } + acl_set_tag_type(newent, tag); + acl_set_qualifier(newent, entryg); + acl_set_permset(newent, perms); + acl_set_flagset_np(newent, flags); + free(entryg); + entryg = NULL; + + return(0); +} + +/* Convert one or more acl entries in string form to an acl_t */ +acl_t +parse_acl_entries(const char *input) { + acl_t acl_input; + acl_entry_t newent; + char *inbuf; + char *oinbuf; + + char **bufp, *entryv[ACL_MAX_ENTRIES]; +#if 0 +/* XXX acl_from_text(), when implemented, will presumably use the canonical + * text representation format, which is what chmod should be using + * We may need to add an entry number to the input + */ + /* Translate the user supplied ACL entry */ + /* acl_input = acl_from_text(input); */ +#else + inbuf = malloc(MAX_ACL_TEXT_SIZE); + + if (inbuf == NULL) { + err(1, "malloc() failed"); + } + strncpy(inbuf, input, MAX_ACL_TEXT_SIZE); + inbuf[MAX_ACL_TEXT_SIZE - 1] = '\0'; + + if ((acl_input = acl_init(1)) == NULL) { + err(1, "acl_init() failed"); + } + + oinbuf = inbuf; + + for (bufp = entryv; (*bufp = strsep(&oinbuf, "\n")) != NULL;) + if (**bufp != '\0') { + if (0 != acl_create_entry(&acl_input, &newent)) { + err(1, "acl_create_entry() failed"); + } + if (0 != parse_entry(*bufp, newent)) { + errx(1, "Failed parsing entry '%s'", *bufp); + } + if (++bufp >= &entryv[ACL_MAX_ENTRIES - 1]) { + errx(1, "Too many entries"); + } + } + + free(inbuf); + return acl_input; +#endif /* #if 0 */ +} + +/* XXX No Libc support for inherited entries and generation determination yet */ +unsigned +get_inheritance_level(acl_entry_t entry) { +/* XXX to be implemented */ + return 1; +} + +/* Determine a "score" for an acl entry. The entry scores higher if it's + * tagged ACL_EXTENDED_DENY, and non-inherited entries are ranked higher + * than inherited entries. + */ + +int +score_acl_entry(acl_entry_t entry) { + + acl_tag_t tag; + acl_flagset_t flags; + acl_permset_t perms; + + int score = 0; + + if (entry == NULL) + return (MINIMUM_TIER); + + if (acl_get_tag_type(entry, &tag) != 0) { + err(1, "Malformed ACL entry, no tag present"); + } + if (acl_get_flagset_np(entry, &flags) != 0){ + err(1, "Unable to obtain flagset"); + } + if (acl_get_permset(entry, &perms) != 0) { + err(1, "Malformed ACL entry, no permt present"); + } + + switch(tag) { + case ACL_EXTENDED_ALLOW: + break; + case ACL_EXTENDED_DENY: + score++; + break; + default: + errx(1, "Unknown tag type %d present in ACL entry", tag); + /* NOTREACHED */ + } + + if (acl_get_flag_np(flags, ACL_ENTRY_INHERITED)) + score += get_inheritance_level(entry) * INHERITANCE_TIER; + + return score; +} + +int +compare_acl_qualifiers(uuid_t *qa, uuid_t *qb) { + return bcmp(qa, qb, sizeof(uuid_t)); +} + +/* Compare two ACL permsets. + * Returns : + * MATCH_SUBSET if bperms is a subset of aperms + * MATCH_SUPERSET if bperms is a superset of aperms + * MATCH_PARTIAL if the two permsets have a common subset + * MATCH_EXACT if the two permsets are identical + * MATCH_NONE if they are disjoint + */ + +int +compare_acl_permsets(acl_permset_t aperms, acl_permset_t bperms) +{ + int i; +/* TBD Implement other match levels as needed */ + for (i = 0; acl_perms[i].name != NULL; i++) { + if (acl_get_perm_np(aperms, acl_perms[i].perm) != + acl_get_perm_np(bperms, acl_perms[i].perm)) + return MATCH_NONE; + } + return MATCH_EXACT; +} + +static int +compare_acl_flagsets(acl_flagset_t aflags, acl_flagset_t bflags) +{ + int i; +/* TBD Implement other match levels as needed */ + for (i = 0; acl_flags[i].name != NULL; i++) { + if (acl_get_flag_np(aflags, acl_flags[i].flag) != + acl_get_flag_np(bflags, acl_flags[i].flag)) + return MATCH_NONE; + } + return MATCH_EXACT; +} + +/* Compares two ACL entries for equality */ +int +compare_acl_entries(acl_entry_t a, acl_entry_t b) +{ + acl_tag_t atag, btag; + acl_permset_t aperms, bperms; + acl_flagset_t aflags, bflags; + int pcmp = 0, fcmp = 0; + void *aqual, *bqual; + + aqual = acl_get_qualifier(a); + bqual = acl_get_qualifier(b); + + int compare = compare_acl_qualifiers(aqual, bqual); + acl_free(aqual); + acl_free(bqual); + + if (compare != 0) + return MATCH_NONE; + + if (0 != acl_get_tag_type(a, &atag)) { + err(1, "No tag type present in entry"); + } + if (0!= acl_get_tag_type(b, &btag)) { + err(1, "No tag type present in entry"); + } + + if (atag != btag) + return MATCH_NONE; + + if ((acl_get_permset(a, &aperms) != 0) || + (acl_get_flagset_np(a, &aflags) != 0) || + (acl_get_permset(b, &bperms) != 0) || + (acl_get_flagset_np(b, &bflags) != 0)) { + err(1, "error fetching permissions"); + } + + pcmp = compare_acl_permsets(aperms, bperms); + fcmp = compare_acl_flagsets(aflags, bflags); + + if ((pcmp == MATCH_NONE) || (fcmp == MATCH_NONE)) + return(MATCH_PARTIAL); + else + return(MATCH_EXACT); +} + +/* Verify that an ACL is in canonical order. Currently, the canonical + * form is: + * local deny + * local allow + * inherited deny (parent) + * inherited allow (parent) + * inherited deny (grandparent) + * inherited allow (grandparent) + * ... + */ +unsigned int +is_canonical(acl_t acl) { + + unsigned aindex; + acl_entry_t entry; + int score = 0, next_score = 0; + +/* XXX - is a zero entry ACL in canonical form? */ + if (0 != acl_get_entry(acl, ACL_FIRST_ENTRY, &entry)) + return 1; + + score = score_acl_entry(entry); + + for (aindex = 0; acl_get_entry(acl, ACL_NEXT_ENTRY, &entry) == 0; + aindex++) { + if (score < (next_score = score_acl_entry(entry))) + return 0; + score = next_score; + } + return 1; +} + + +/* Iterate through an ACL, and find the canonical position for the + * specified entry + */ +unsigned int +find_canonical_position(acl_t acl, acl_entry_t modifier) { + + acl_entry_t entry; + int mscore = 0; + unsigned mpos = 0; + + /* Check if there's an entry with the same qualifier + * and tag type; if not, find the appropriate slot + * for the score. + */ + + if (0 != acl_get_entry(acl, ACL_FIRST_ENTRY, &entry)) + return 0; + + mscore = score_acl_entry(modifier); + + while (mscore < score_acl_entry(entry)) { + + mpos++; + + if (0 != acl_get_entry(acl, ACL_NEXT_ENTRY, &entry)) + break; + + } + return mpos; +} + +int canonicalize_acl_entries(acl_t acl); + +/* For a given acl_entry_t "modifier", find the first exact or + * partially matching entry from the specified acl_t acl + */ + +int +find_matching_entry (acl_t acl, acl_entry_t modifier, acl_entry_t *rentryp, + unsigned match_inherited) { + + acl_entry_t entry = NULL; + + unsigned aindex; + int cmp, fcmp = MATCH_NONE; + + for (aindex = 0; + acl_get_entry(acl, entry == NULL ? ACL_FIRST_ENTRY : + ACL_NEXT_ENTRY, &entry) == 0; + aindex++) { + cmp = compare_acl_entries(entry, modifier); + if ((cmp == MATCH_EXACT) || (cmp == MATCH_PARTIAL)) { + if (match_inherited) { + acl_flagset_t eflags, mflags; + + if (0 != acl_get_flagset_np(modifier, &mflags)) { + err(1, "Unable to get flagset"); + } + + if (0 != acl_get_flagset_np(entry, &eflags)) { + err(1, "Unable to get flagset"); + } + + if (compare_acl_flagsets(mflags, eflags) == MATCH_EXACT) { + *rentryp = entry; + fcmp = cmp; + } + } + else { + *rentryp = entry; + fcmp = cmp; + } + } + if (fcmp == MATCH_EXACT) + break; + } + return fcmp; +} + +/* Remove all perms specified in modifier from rentry*/ +int +subtract_from_entry(acl_entry_t rentry, acl_entry_t modifier, int* valid_perms) +{ + acl_permset_t rperms, mperms; + acl_flagset_t rflags, mflags; + if (valid_perms) + *valid_perms = 0; + int i; + + if ((acl_get_permset(rentry, &rperms) != 0) || + (acl_get_flagset_np(rentry, &rflags) != 0) || + (acl_get_permset(modifier, &mperms) != 0) || + (acl_get_flagset_np(modifier, &mflags) != 0)) { + err(1, "error computing ACL modification"); + } + + for (i = 0; acl_perms[i].name != NULL; i++) { + if (acl_get_perm_np(mperms, acl_perms[i].perm)) + acl_delete_perm(rperms, acl_perms[i].perm); + else if (valid_perms && acl_get_perm_np(rperms, acl_perms[i].perm)) + (*valid_perms)++; + } + for (i = 0; acl_flags[i].name != NULL; i++) { + if (acl_get_flag_np(mflags, acl_flags[i].flag)) + acl_delete_flag_np(rflags, acl_flags[i].flag); + } + acl_set_permset(rentry, rperms); + acl_set_flagset_np(rentry, rflags); + return 0; +} +/* Add the perms specified in modifier to rentry */ +static int +merge_entry_perms(acl_entry_t rentry, acl_entry_t modifier) +{ + acl_permset_t rperms, mperms; + acl_flagset_t rflags, mflags; + int i; + + if ((acl_get_permset(rentry, &rperms) != 0) || + (acl_get_flagset_np(rentry, &rflags) != 0) || + (acl_get_permset(modifier, &mperms) != 0) || + (acl_get_flagset_np(modifier, &mflags) != 0)) { + err(1, "error computing ACL modification"); + } + + for (i = 0; acl_perms[i].name != NULL; i++) { + if (acl_get_perm_np(mperms, acl_perms[i].perm)) + acl_add_perm(rperms, acl_perms[i].perm); + } + for (i = 0; acl_flags[i].name != NULL; i++) { + if (acl_get_flag_np(mflags, acl_flags[i].flag)) + acl_add_flag_np(rflags, acl_flags[i].flag); + } + acl_set_permset(rentry, rperms); + acl_set_flagset_np(rentry, rflags); + return 0; +} + +int +modify_acl(acl_t *oaclp, acl_entry_t modifier, unsigned int optflags, + int position, int inheritance_level, + unsigned flag_new_acl, const char* path) { + + unsigned cpos = 0; + acl_entry_t newent = NULL; + int dmatch = 0; + acl_entry_t rentry = NULL; + unsigned retval = 0; + acl_t oacl = *oaclp; + +/* Add the inherited flag if requested by the user*/ + if (modifier && (optflags & ACL_INHERIT_FLAG)) { + acl_flagset_t mflags; + + acl_get_flagset_np(modifier, &mflags); + acl_add_flag_np(mflags, ACL_ENTRY_INHERITED); + acl_set_flagset_np(modifier, mflags); + } + + if (optflags & ACL_SET_FLAG) { + if (position != -1) { + if (0 != acl_create_entry_np(&oacl, &newent, position)) { + err(1, "acl_create_entry() failed"); + } + acl_copy_entry(newent, modifier); + } else { +/* If an entry exists, add the new permissions to it, else add an + * entry in the canonical position. + */ + +/* First, check for a matching entry - if one exists, merge flags */ + dmatch = find_matching_entry(oacl, modifier, &rentry, 1); + + if (dmatch != MATCH_NONE) { + if (dmatch == MATCH_EXACT) +/* Nothing to be done */ + goto ma_exit; + + if (dmatch == MATCH_PARTIAL) { + merge_entry_perms(rentry, modifier); + goto ma_exit; + } + } +/* Insert the entry in canonical order */ + cpos = find_canonical_position(oacl, modifier); + if (0!= acl_create_entry_np(&oacl, &newent, cpos)) { + err(1, "acl_create_entry() failed"); + } + acl_copy_entry(newent, modifier); + } + } else if (optflags & ACL_DELETE_FLAG) { + if (flag_new_acl) { + warnx("No ACL present '%s'", path); + retval = 1; + } else if (position != -1 ) { + if (0 != acl_get_entry(oacl, position, &rentry)) { + warnx("Invalid entry number '%s'", path); + retval = 1; + } else { + acl_delete_entry(oacl, rentry); + } + } else { + unsigned match_found = 0, aindex; + for (aindex = 0; + acl_get_entry(oacl, rentry == NULL ? + ACL_FIRST_ENTRY : + ACL_NEXT_ENTRY, &rentry) == 0; + aindex++) { + unsigned cmp; + cmp = compare_acl_entries(rentry, modifier); + if ((cmp == MATCH_EXACT) || + (cmp == MATCH_PARTIAL)) { + match_found++; + if (cmp == MATCH_EXACT) + acl_delete_entry(oacl, rentry); + else { + int valid_perms; +/* In the event of a partial match, remove the specified perms from the + * entry */ + subtract_from_entry(rentry, modifier, &valid_perms); + /* if no perms survived then delete the entry */ + if (valid_perms == 0) + acl_delete_entry(oacl, rentry); + } + } + } + if (0 == match_found) { + warnx("Entry not found when attempting delete '%s'",path); + retval = 1; + } + } + } else if (optflags & ACL_REWRITE_FLAG) { + acl_entry_t rentry; + + if (-1 == position) { + chmod_usage(); + } + if (0 == flag_new_acl) { + if (0 != acl_get_entry(oacl, position, + &rentry)) { + err(1, "Invalid entry number '%s'", path); + } + + if (0 != acl_delete_entry(oacl, rentry)) { + err(1, "Unable to delete entry '%s'", path); + } + } + if (0!= acl_create_entry_np(&oacl, &newent, position)) { + err(1, "acl_create_entry() failed"); + } + acl_copy_entry(newent, modifier); + } +ma_exit: + *oaclp = oacl; + return retval; +} + +int +modify_file_acl(unsigned int optflags, const char *path, acl_t modifier, int position, int inheritance_level, int follow) { + + acl_t oacl = NULL; + unsigned aindex = 0, flag_new_acl = 0; + acl_entry_t newent = NULL; + acl_entry_t entry = NULL; + unsigned retval = 0; + + extern __thread int chmod_fflag; + +/* XXX acl_get_file() returns a zero entry ACL if an ACL was previously + * associated with the file, and has had its entries removed. + * However, POSIX 1003.1e states that a zero entry ACL should be + * returned if the caller asks for ACL_TYPE_DEFAULT, and no ACL is + * associated with the path; it + * does not specifically state that a request for ACL_TYPE_EXTENDED + * should not return a zero entry ACL, however. + */ + +/* Determine if we've been given a zero entry ACL, or create an ACL if + * none exists. There are some issues to consider here: Should we create + * a zero-entry ACL for a delete or check canonicity operation? + */ + + if (path == NULL) + chmod_usage(); + + if (optflags & ACL_CLEAR_FLAG) { + filesec_t fsec = filesec_init(); + if (fsec == NULL) { + err(1, "filesec_init() failed"); + } + if (filesec_set_property(fsec, FILESEC_ACL, _FILESEC_REMOVE_ACL) != 0) { + err(1, "filesec_set_property() failed"); + } + if (follow) { + if (chmodx_np(path, fsec) != 0) { + if (!chmod_fflag) { + warn("Failed to clear ACL on file %s", path); + } + retval = 1; + } + } else { + int fd = open(path, O_SYMLINK); + if (fd != -1) { + if (fchmodx_np(fd, fsec) != 0) { + if (!chmod_fflag) { + warn("Failed to clear ACL on file %s", path); + } + retval = 1; + } + close(fd); + } else { + if (!chmod_fflag) { + warn("Failed to open file %s", path); + } + retval = 1; + } + } + filesec_free(fsec); + return (retval); + } + + if (optflags & ACL_FROM_STDIN) { + oacl = acl_dup(modifier); + } else { + if (follow) { + oacl = acl_get_file(path, ACL_TYPE_EXTENDED); + } else { + int fd = open(path, O_SYMLINK); + if (fd != -1) { + oacl = acl_get_fd_np(fd, ACL_TYPE_EXTENDED); + close(fd); + } + } + if ((oacl == NULL) || + (acl_get_entry(oacl,ACL_FIRST_ENTRY, &newent) != 0)) { + if ((oacl = acl_init(1)) == NULL) { + err(1, "acl_init() failed"); + } + flag_new_acl = 1; + position = 0; + } + + if ((0 == flag_new_acl) && (optflags & (ACL_REMOVE_INHERIT_FLAG | + ACL_REMOVE_INHERITED_ENTRIES))) { + acl_t facl = NULL; + if ((facl = acl_init(1)) == NULL) { + err(1, "acl_init() failed"); + } + for (aindex = 0; + acl_get_entry(oacl, + (entry == NULL ? ACL_FIRST_ENTRY : + ACL_NEXT_ENTRY), &entry) == 0; + aindex++) { + acl_flagset_t eflags; + acl_entry_t fent = NULL; + if (acl_get_flagset_np(entry, &eflags) != 0) { + err(1, "Unable to obtain flagset"); + } + + if (acl_get_flag_np(eflags, ACL_ENTRY_INHERITED)) { + if (optflags & ACL_REMOVE_INHERIT_FLAG) { + acl_delete_flag_np(eflags, ACL_ENTRY_INHERITED); + acl_set_flagset_np(entry, eflags); + acl_create_entry(&facl, &fent); + acl_copy_entry(fent, entry); + } + } + else { + acl_create_entry(&facl, &fent); + acl_copy_entry(fent, entry); + } + } + if (oacl) + acl_free(oacl); + oacl = facl; + } else if (optflags & ACL_TO_STDOUT) { + ssize_t len; /* need to get printacl() from ls(1) */ + char *text = acl_to_text(oacl, &len); + puts(text); + acl_free(text); + } else if (optflags & ACL_CHECK_CANONICITY) { + if (flag_new_acl) { + warnx("No ACL currently associated with file '%s'", path); + } + retval = is_canonical(oacl); + } else if ((optflags & ACL_SET_FLAG) && (position == -1) && + (!is_canonical(oacl))) { + warnx("The specified file '%s' does not have an ACL in canonical order, please specify a position with +a# ", path); + retval = 1; + } else if (((optflags & ACL_DELETE_FLAG) && (position != -1)) + || (optflags & ACL_CHECK_CANONICITY)) { + retval = modify_acl(&oacl, NULL, optflags, position, + inheritance_level, flag_new_acl, path); + } else if ((optflags & (ACL_REMOVE_INHERIT_FLAG|ACL_REMOVE_INHERITED_ENTRIES)) && flag_new_acl) { + warnx("No ACL currently associated with file '%s'", path); + retval = 1; + } else { + if (!modifier) { /* avoid bus error in acl_get_entry */ + errx(1, "Internal error: modifier should not be NULL"); + } + for (aindex = 0; + acl_get_entry(modifier, + (entry == NULL ? ACL_FIRST_ENTRY : + ACL_NEXT_ENTRY), &entry) == 0; + aindex++) { + + retval += modify_acl(&oacl, entry, optflags, + position, inheritance_level, + flag_new_acl, path); + } + } + } + +/* XXX Potential race here, since someone else could've modified or + * read the ACL on this file (with the intention of modifying it) in + * the interval from acl_get_file() to acl_set_file(); we can + * minimize one aspect of this window by comparing the original acl + * to a fresh one from acl_get_file() but we could consider a + * "changeset" mechanism, common locking strategy, or kernel + * supplied reservation mechanism to prevent this race. + */ + if (!(optflags & (ACL_TO_STDOUT|ACL_CHECK_CANONICITY))) { + int status = -1; + if (follow) { + status = acl_set_file(path, ACL_TYPE_EXTENDED, oacl); + } else { + int fd = open(path, O_SYMLINK); + if (fd != -1) { + status = acl_set_fd_np(fd, oacl, + ACL_TYPE_EXTENDED); + close(fd); + } + } + if (status != 0) { + if (!chmod_fflag) + warn("Failed to set ACL on file '%s'", path); + retval = 1; + } + } + + if (oacl) + acl_free(oacl); + + return retval; +} + +#endif /*__APPLE__*/ diff --git a/files/Sources/files/chmod/chmod_acl.h b/files/Sources/files/chmod/chmod_acl.h new file mode 100644 index 00000000..c76d076d --- /dev/null +++ b/files/Sources/files/chmod/chmod_acl.h @@ -0,0 +1,85 @@ +/* + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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. + */ + +#ifdef __APPLE__ +#include +#include +#include +#include +#include +#include + +#define ACL_FLAG (1<<0) +#define ACL_SET_FLAG (1<<1) +#define ACL_DELETE_FLAG (1<<2) +#define ACL_REWRITE_FLAG (1<<3) +#define ACL_ORDER_FLAG (1<<4) +#define ACL_INHERIT_FLAG (1<<5) +#define ACL_FOLLOW_LINK (1<<6) +#define ACL_FROM_STDIN (1<<7) +#define ACL_CHECK_CANONICITY (1<<8) +#define ACL_REMOVE_INHERIT_FLAG (1<<9) +#define ACL_REMOVE_INHERITED_ENTRIES (1<<10) +#define ACL_NO_TRANSLATE (1<<11) +#define ACL_INVOKE_EDITOR (1<<12) +#define ACL_TO_STDOUT (1<<13) +#define ACL_CLEAR_FLAG (1<<14) + +#define INHERITANCE_TIER (-5) +#define MINIMUM_TIER (-1000) + +#define MATCH_EXACT (2) +#define MATCH_PARTIAL (1) +#define MATCH_NONE (-1) +#define MATCH_SUBSET (-2) +#define MATCH_SUPERSET (-3) + +#define MAX_ACL_TEXT_SIZE 4096 +#define MAX_INHERITANCE_LEVEL 1024 + +extern int search_acl_block(char *tok); +extern int parse_entry(char *entrybuf, acl_entry_t newent); +extern acl_t parse_acl_entries(const char *input); +extern int score_acl_entry(acl_entry_t entry); +extern unsigned get_inheritance_level(acl_entry_t entry); +extern int compare_acl_qualifiers(uuid_t *qa, uuid_t *qb); +extern int compare_acl_permsets(acl_permset_t aperms, acl_permset_t bperms); +extern int compare_acl_entries(acl_entry_t a, acl_entry_t b); +extern unsigned is_canonical(acl_t acl); +extern int find_matching_entry (acl_t acl, acl_entry_t modifier, acl_entry_t *rentry, unsigned match_inherited); +extern unsigned find_canonical_position(acl_t acl, acl_entry_t modifier); +extern int subtract_from_entry(acl_entry_t rentry, acl_entry_t modifier, int *valid_perms); +extern int modify_acl(acl_t *oaclp, acl_entry_t modifier, unsigned int optflags, int position, int inheritance_level, unsigned flag_new_acl, const char* path); +extern int modify_file_acl(unsigned int optflags, const char *path, acl_t modifier, int position, int inheritance_level, int follow); +extern uuid_t *name_to_uuid(char *tok, int nametype); +#endif /* __APPLE__*/ diff --git a/files/Sources/files/chown/chgrp.1 b/files/Sources/files/chown/chgrp.1 new file mode 100644 index 00000000..769c3d55 --- /dev/null +++ b/files/Sources/files/chown/chgrp.1 @@ -0,0 +1,142 @@ +.\" Copyright (c) 1983, 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 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. +.\" +.\" @(#)chgrp.1 8.3 (Berkeley) 3/31/94 +.\" $FreeBSD: src/usr.sbin/chown/chgrp.1,v 1.13 2001/08/15 09:09:46 ru Exp $ +.\" +.Dd March 31, 1994 +.Dt CHGRP 1 +.Os +.Sh NAME +.Nm chgrp +.Nd change group +.Sh SYNOPSIS +.Nm chgrp +.Op Fl fhv +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Ar group +.Ar +.Sh DESCRIPTION +The +.Nm chgrp +utility sets the group ID of the file named by each +.Ar file +operand to the +.Ar group +ID specified by the group operand. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl f +The force option ignores errors, except for usage errors and doesn't +query about strange modes (unless the user does not have proper permissions). +.It Fl H +If the +.Fl R +option is specified, symbolic links on the command line are followed. +(Symbolic links encountered in the tree traversal are not followed). +.It Fl h +If the file is a symbolic link, the group ID of the link itself is changed +rather than the file that is pointed to. +.It Fl L +If the +.Fl R +option is specified, all symbolic links are followed. +.It Fl P +If the +.Fl R +option is specified, no symbolic links are followed. +This is the default. Use +.Fl h +to change the group ID of a symbolic link. +.It Fl R +Change the group ID for the file hierarchies rooted +in the files instead of just the files themselves. +.It Fl v +Cause +.Nm chgrp +to be verbose, showing files as the group is modified. +.El +.Pp +The +.Fl H , +.Fl L +and +.Fl P +options are ignored unless the +.Fl R +option is specified. +In addition, these options override each other and the +command's actions are determined by the last one specified. +.Pp +The +.Ar group +operand can be either a group name from the group database, +or a numeric group ID. +If a group name is also a numeric group ID, the operand is used as a +group name. +.Pp +The user invoking +.Nm chgrp +must belong to the specified group and be the owner of the file, +or be the super-user. +.Sh DIAGNOSTICS +.Ex -std +.Sh COMPATIBILITY +In previous versions of this system, symbolic links did not have groups. +.Pp +The +.Fl v +option is non-standard and its use in scripts is not recommended. +.Sh FILES +.Bl -tag -width /etc/group -compact +.It Pa /etc/group +group ID file +.El +.Sh SEE ALSO +.Xr chown 2 , +.Xr fts 3 , +.Xr group 5 , +.Xr passwd 5 , +.Xr symlink 7 , +.Xr chown 8 +.Sh STANDARDS +The +.Nm chgrp +utility is expected to be +.St -p1003.2 +compatible. diff --git a/files/Sources/files/chown/chown.8 b/files/Sources/files/chown/chown.8 new file mode 100644 index 00000000..a3ffa093 --- /dev/null +++ b/files/Sources/files/chown/chown.8 @@ -0,0 +1,175 @@ +.\" Copyright (c) 1990, 1991, 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 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. +.\" +.\" @(#)chown.8 8.3 (Berkeley) 3/31/94 +.\" $FreeBSD: src/usr.sbin/chown/chown.8,v 1.20 2002/07/14 14:42:43 charnier Exp $ +.\" +.Dd March 31, 1994 +.Dt CHOWN 8 +.Os +.Sh NAME +.Nm chown +.Nd change file owner and group +.Sh SYNOPSIS +.Nm chown +.Op Fl fhv +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Ar owner Ns Op : Ns Ar group +.Ar +.Nm chown +.Op Fl fhv +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.No : Ns Ar group +.Ar +.Sh DESCRIPTION +The +.Nm chown +utility changes the user ID and/or the group ID of the specified files. +Symbolic links named by arguments are silently left unchanged unless +.Fl h +is used. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl f +Don't report any failure to change file owner or group, nor modify +the exit status to reflect such failures. +.It Fl H +If the +.Fl R +option is specified, symbolic links on the command line are followed. +(Symbolic links encountered in the tree traversal are not followed.) +.It Fl h +If the file is a symbolic link, change the user ID and/or the +group ID of the link itself. +.It Fl L +If the +.Fl R +option is specified, all symbolic links are followed. +.It Fl P +If the +.Fl R +option is specified, no symbolic links are followed. +Instead, the user and/or group ID of the link itself are modified. +This is the default. Use +.Fl h +to change the user ID and/or the group of symbolic links. +.It Fl R +Change the user ID and/or the group ID for the file hierarchies rooted +in the files instead of just the files themselves. +.It Fl v +Cause +.Nm chown +to be verbose, showing files as the owner is modified. +.El +.Pp +The +.Fl H , +.Fl L +and +.Fl P +options are ignored unless the +.Fl R +option is specified. +In addition, these options override each other and the +command's actions are determined by the last one specified. +.Pp +The +.Ar owner +and +.Ar group +operands are both optional; +however, at least one must be specified. +If the +.Ar group +operand is specified, it must be preceded by a colon (``:'') character. +.Pp +The +.Ar owner +may be either a numeric user ID or a user name. +If a user name is also a numeric user ID, the operand is used as a +user name. +The +.Ar group +may be either a numeric group ID or a group name. +If a group name is also a numeric group ID, the operand is used as a +group name. +.Pp +For obvious security reasons, +the ownership of a file may only be altered by a super-user. +Similarly, only a member of a group can change a file's group ID +to that group. +.Sh DIAGNOSTICS +.Ex -std +.Sh COMPATIBILITY +Previous versions of the +.Nm chown +utility used the dot (``.'') character to distinguish the group name. +This has been changed to be a colon (``:'') character, +so that user and group names may contain the dot character. +.Pp +On previous versions of this system, +symbolic links did not have owners. +.Pp +The +.Fl v +option is non-standard and its use in scripts is not recommended. +.Sh LEGACY DESCRIPTION +In legacy mode, the +.Fl R +and +.Fl RP +options do not change the user ID +or the group ID of symbolic links. +.Sh SEE ALSO +.Xr chgrp 1 , +.Xr find 1 , +.Xr chown 2 , +.Xr fts 3 , +.Xr compat 5 , +.Xr symlink 7 +.Sh STANDARDS +The +.Nm chown +utility is expected to be +.St -p1003.2 +compliant. +.Sh HISTORY +A +.Nm chown +utility appeared in +.At v1 . diff --git a/files/Sources/files/chown/chown.c b/files/Sources/files/chown/chown.c new file mode 100644 index 00000000..9b7db8cf --- /dev/null +++ b/files/Sources/files/chown/chown.c @@ -0,0 +1,321 @@ +/* + * Copyright (c) 1988, 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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 +#ifndef lint +__used static const char copyright[] = +"@(#) Copyright (c) 1988, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)chown.c 8.8 (Berkeley) 4/4/94"; +#endif +#endif /* not lint */ + +#include +__RCSID("$FreeBSD: src/usr.sbin/chown/chown.c,v 1.24 2002/07/17 16:22:24 dwmalone Exp $"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ios_error.h" + +#ifdef __APPLE__ +// #include +// #else +#define COMPAT_MODE(a,b) (1) +#endif /* __APPLE__ */ + +static void a_gid(const char *); +static void a_uid(const char *); +static void chownerr(const char *); +static uid_t id(const char *, const char *); +static void usage(void); + +static uid_t uid; +static gid_t gid; +static int ischown; +static const char *gname; + +int +chown_main(int argc, char **argv) +{ + FTS *ftsp; + FTSENT *p; + int Hflag, Lflag, Pflag, Rflag, fflag, hflag, vflag; + int ch, fts_options, rval; + char *cp; + int unix2003_compat = 0; + int symlink_found = 0; + + if (argc < 1) + usage(); + cp = strrchr(argv[0], '/'); + cp = (cp != NULL) ? cp + 1 : argv[0]; + ischown = (strcmp(cp, "chown") == 0); + + Hflag = Lflag = Pflag = Rflag = fflag = hflag = vflag = 0; + optind = 1; opterr = 1; optreset = 1; + while ((ch = getopt(argc, argv, "HLPRfhv")) != -1) + switch (ch) { + case 'H': + Hflag = 1; + Lflag = Pflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = Pflag = 0; + break; + case 'P': + Pflag = 1; + Hflag = Lflag = 0; + break; + case 'R': + Rflag = 1; + break; + case 'f': + fflag = 1; + break; + case 'h': + hflag = 1; + break; + case 'v': + vflag = 1; + break; + case '?': + default: + usage(); + } + argv += optind; + argc -= optind; + + if (argc < 2) + usage(); + if (!Rflag && (Hflag || Lflag || Pflag)) + warnx("options -H, -L, -P only useful with -R"); + + if (Rflag) { + fts_options = FTS_PHYSICAL; + if (hflag && (Hflag || Lflag)) { + errx(1, "the -R%c and -h options may not be " + "specified together\n", Hflag ? 'H' : 'L'); + } + if (Hflag) + fts_options |= FTS_COMFOLLOW; + else if (Lflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + } else + fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL; + + uid = (uid_t)-1; + gid = (gid_t)-1; + if (ischown) { + unix2003_compat = COMPAT_MODE("bin/chown", "Unix2003"); + if ((cp = strchr(*argv, ':')) != NULL) { + *cp++ = '\0'; + a_gid(cp); + } +#ifdef SUPPORT_DOT + else if ((cp = strchr(*argv, '.')) != NULL) { + warnx("separation of user and group with a period is deprecated"); + *cp++ = '\0'; + a_gid(cp); + } +#endif + a_uid(*argv); + } else { + unix2003_compat = COMPAT_MODE("bin/chgrp", "Unix2003"); + a_gid(*argv); + } + + if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL) { + err(1, NULL); + } + + for (rval = 0; (p = fts_read(ftsp)) != NULL;) { + symlink_found = 0; + switch (p->fts_info) { + case FTS_D: /* Change it at FTS_DP. */ + if (!Rflag) + fts_set(ftsp, p, FTS_SKIP); + continue; + case FTS_DNR: /* Warn, chown. */ + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + break; + case FTS_ERR: /* Warn, continue. */ + case FTS_NS: + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + continue; + case FTS_SL: + case FTS_SLNONE: + /* + * The only symlinks that end up here are ones that + * don't point to anything and ones that we found + * doing a physical walk. + */ + if (hflag) + break; + else { + symlink_found = 1; + if (unix2003_compat) { + if (Hflag || Lflag) { /* -H or -L was specified */ + if (p->fts_errno) { + warnx("%s: %s", p->fts_name, strerror(p->fts_errno)); + rval = 1; + continue; + } + } + break; /* Otherwise symlinks keep going */ + } + continue; + } + default: + break; + } + if (unix2003_compat) { + /* Can only avoid updating times if both uid and gid are -1 */ + if ((uid == (uid_t)-1) && (gid == (gid_t)-1)) + continue; + } else { + if ((uid == (uid_t)-1 || uid == p->fts_statp->st_uid) && + (gid == (gid_t)-1 || gid == p->fts_statp->st_gid)) + continue; + } + if (((hflag || symlink_found) ? lchown : chown)(p->fts_accpath, uid, gid) == -1) { + if (!fflag) { + chownerr(p->fts_path); + rval = 1; + } + } else { + if (vflag) + fprintf(thread_stdout, "%s\n", p->fts_path); + } + } + if (errno) { + err(1, "fts_read"); + } + exit(rval); +} + +void +a_gid(const char *s) +{ + struct group *gr; + + if (*s == '\0') /* Argument was "uid[:.]". */ + return; + gname = s; + gid = ((gr = getgrnam(s)) != NULL) ? gr->gr_gid : id(s, "group"); +} + +void +a_uid(const char *s) +{ + struct passwd *pw; + + if (*s == '\0') /* Argument was "[:.]gid". */ + return; + uid = ((pw = getpwnam(s)) != NULL) ? pw->pw_uid : id(s, "user"); +} + +static uid_t +id(const char *name, const char *type) +{ + unsigned long val; + char *ep; + + errno = 0; + val = strtoul(name, &ep, 10); + if (errno || *ep != '\0' || val > UID_MAX) { + errx(1, "%s: illegal %s name", name, type); + } + return (uid_t)val; +} + +void +chownerr(const char *file) +{ + static uid_t euid = -1; + static int ngroups = -1; + gid_t groups[NGROUPS_MAX]; + + /* Check for chown without being root. */ + if (errno != EPERM || (uid != (uid_t)-1 && + euid == (uid_t)-1 && (euid = geteuid()) != 0)) { + warn("%s", file); + return; + } + + /* Check group membership; kernel just returns EPERM. */ + if (gid != (gid_t)-1 && ngroups == -1 && + euid == (uid_t)-1 && (euid = geteuid()) != 0) { + ngroups = getgroups(NGROUPS_MAX, groups); + while (--ngroups >= 0 && gid != groups[ngroups]); + if (ngroups < 0) { + warnx("you are not a member of group %s", gname); + return; + } + } + warn("%s", file); +} + +void +usage(void) +{ + + if (ischown) + (void)fprintf(stderr, "%s\n%s\n", + "usage: chown [-fhv] [-R [-H | -L | -P]] owner[:group]" + " file ...", + " chown [-fhv] [-R [-H | -L | -P]] :group file ..."); + else + (void)fprintf(stderr, "%s\n", + "usage: chgrp [-fhv] [-R [-H | -L | -P]] group file ..."); + exit(1); +} diff --git a/files/Sources/files/cksum/cksum.1 b/files/Sources/files/cksum/cksum.1 new file mode 100644 index 00000000..b4161fee --- /dev/null +++ b/files/Sources/files/cksum/cksum.1 @@ -0,0 +1,182 @@ +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 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. +.\" +.\" @(#)cksum.1 8.2 (Berkeley) 4/28/95 +.\" $FreeBSD: src/usr.bin/cksum/cksum.1,v 1.19 2005/01/17 07:44:13 ru Exp $ +.\" +.Dd April 28, 1995 +.Dt CKSUM 1 +.Os +.Sh NAME +.Nm cksum , +.Nm sum +.Nd display file checksums and block counts +.Sh SYNOPSIS +.Nm +.Op Fl o Ar 1 | 2 | 3 +.Op Ar +.Nm sum +.Op Ar +.Sh DESCRIPTION +The +.Nm +utility writes to the standard output three whitespace separated +fields for each input file. +These fields are a checksum +.Tn CRC , +the total number of octets in the file and the file name. +If no file name is specified, the standard input is used and no file name +is written. +.Pp +The +.Nm sum +utility is identical to the +.Nm +utility, except that it defaults to using historic algorithm 1, as +described below. +It is provided for compatibility only. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl o +Use historic algorithms instead of the (superior) default one. +.Pp +Algorithm 1 is the algorithm used by historic +.Bx +systems as the +.Xr sum 1 +algorithm and by historic +.At V +systems as the +.Xr sum 1 +algorithm when using the +.Fl r +option. +This is a 16-bit checksum, with a right rotation before each addition; +overflow is discarded. +.Pp +Algorithm 2 is the algorithm used by historic +.At V +systems as the +default +.Xr sum 1 +algorithm. +This is a 32-bit checksum, and is defined as follows: +.Bd -unfilled -offset indent +s = sum of all bytes; +r = s % 2^16 + (s % 2^32) / 2^16; +cksum = (r % 2^16) + r / 2^16; +.Ed +.Pp +Algorithm 3 is what is commonly called the +.Ql 32bit CRC +algorithm. +This is a 32-bit checksum. +.Pp +Both algorithm 1 and 2 write to the standard output the same fields as +the default algorithm except that the size of the file in bytes is +replaced with the size of the file in blocks. +For historic reasons, the block size is 1024 for algorithm 1 and 512 +for algorithm 2. +Partial blocks are rounded up. +.El +.Pp +The default +.Tn CRC +used is based on the polynomial used for +.Tn CRC +error checking +in the networking standard +.St -iso8802-3 . +The +.Tn CRC +checksum encoding is defined by the generating polynomial: +.Pp +.Bd -unfilled -offset indent +G(x) = x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1 +.Ed +.Pp +Mathematically, the +.Tn CRC +value corresponding to a given file is defined by +the following procedure: +.Bd -ragged -offset indent +The +.Ar n +bits to be evaluated are considered to be the coefficients of a mod 2 +polynomial M(x) of degree +.Ar n Ns \-1 . +These +.Ar n +bits are the bits from the file, with the most significant bit being the most +significant bit of the first octet of the file and the last bit being the least +significant bit of the last octet, padded with zero bits (if necessary) to +achieve an integral number of octets, followed by one or more octets +representing the length of the file as a binary value, least significant octet +first. +The smallest number of octets capable of representing this integer are used. +.Pp +M(x) is multiplied by x^32 (i.e., shifted left 32 bits) and divided by +G(x) using mod 2 division, producing a remainder R(x) of degree <= 31. +.Pp +The coefficients of R(x) are considered to be a 32-bit sequence. +.Pp +The bit sequence is complemented and the result is the CRC. +.Ed +.Sh EXIT STATUS +.Ex -std cksum sum +.Sh SEE ALSO +.Xr md5 1 +.Pp +The default calculation is identical to that given in pseudo-code +in the following +.Tn ACM +article. +.Rs +.%T "Computation of Cyclic Redundancy Checks Via Table Lookup" +.%A Dilip V. Sarwate +.%J "Communications of the" Tn ACM +.%D "August 1988" +.Re +.Sh STANDARDS +The +.Nm +utility is expected to conform to +.St -p1003.2-92 . +.Sh HISTORY +The +.Nm +utility appeared in +.Bx 4.4 . diff --git a/files/Sources/files/cksum/cksum.c b/files/Sources/files/cksum/cksum.c new file mode 100644 index 00000000..82015913 --- /dev/null +++ b/files/Sources/files/cksum/cksum.c @@ -0,0 +1,151 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * James W. Williams of NASA Goddard Space Flight Center. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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 +#ifndef lint +__used static const char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cksum.c 8.2 (Berkeley) 4/28/95"; +#endif +#endif /* not lint */ + +#include +__FBSDID("$FreeBSD: src/usr.bin/cksum/cksum.c,v 1.17 2003/03/13 23:32:28 robert Exp $"); + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "extern.h" +#include +#include "ios_error.h" // remap exit to pthread_exit + +static void usage(void); + +int +chksum_main(int argc, char **argv) +{ + uint32_t val; + int ch, fd, rval; + off_t len; + char *fn, *p; + int (*cfncn)(int, uint32_t *, off_t *); + void (*pfncn)(char *, u_int32_t, off_t); + + cfncn=NULL; + optind = 1; opterr = 1; optreset = 1; + + if(*argv) { + if ((p = rindex(argv[0], '/')) == NULL) + p = argv[0]; + else + ++p; + if (!strcmp(p, "sum")) { + cfncn = csum1; + pfncn = psum1; + ++argv; + } + } + + if(!cfncn) { + cfncn = crc; + pfncn = pcrc; + + while ((ch = getopt(argc, argv, "o:")) != -1) + switch (ch) { + case 'o': + if (!strcmp(optarg, "1")) { + cfncn = csum1; + pfncn = psum1; + } else if (!strcmp(optarg, "2")) { + cfncn = csum2; + pfncn = psum2; + } else if (!strcmp(optarg, "3")) { + cfncn = chksum_crc32; + pfncn = pcrc; + } else { + warnx("illegal argument to -o option"); + usage(); + } + break; + case '?': + default: + usage(); + } +// argc -= optind; + argv += optind; + } + + fd = fileno(thread_stdin); + fn = NULL; + rval = 0; + do { + if (*argv) { + fn = *argv++; + if ((fd = open(fn, O_RDONLY, 0)) < 0) { + warn("%s", fn); + rval = 1; + continue; + } + } + if (cfncn(fd, &val, &len)) { + warn("%s", fn ? fn : "stdin"); + rval = 1; + } else + pfncn(fn, val, len); + (void)close(fd); + } while (*argv); + exit(rval); +} + +static void +usage(void) +{ + (void)fprintf(stderr, "usage: cksum [-o 1 | 2 | 3] [file ...]\n"); + (void)fprintf(stderr, " sum [file ...]\n"); + exit(1); +} diff --git a/files/Sources/files/cksum/crc.c b/files/Sources/files/cksum/crc.c new file mode 100644 index 00000000..57179d5d --- /dev/null +++ b/files/Sources/files/cksum/crc.c @@ -0,0 +1,148 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * James W. Williams of NASA Goddard Space Flight Center. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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 +#if 0 +static char sccsid[] = "@(#)crc.c 8.1 (Berkeley) 6/17/93"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: src/usr.bin/cksum/crc.c,v 1.8 2003/03/13 23:32:28 robert Exp $"); + +#include + +#include +#include + +#include "extern.h" + +static const uint32_t crctab[] = { + 0x0, + 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, + 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, + 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, + 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, + 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, + 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, + 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, + 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, + 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, + 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, + 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, + 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, + 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, + 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, + 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, + 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, + 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, + 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, + 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, + 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, + 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, + 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, + 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, + 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, + 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, + 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, + 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, + 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, + 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, + 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, + 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, + 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, + 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, + 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, + 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, + 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, + 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, + 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +}; + +/* + * Compute a POSIX 1003.2 checksum. This routine has been broken out so that + * other programs can use it. It takes a file descriptor to read from and + * locations to store the crc and the number of bytes read. It returns 0 on + * success and 1 on failure. Errno is set on failure. + */ +uint32_t crc_total = ~0; /* The crc over a number of files. */ + +int +crc(int fd, uint32_t *cval, off_t *clen) +{ + uint32_t lcrc; + ssize_t nr; + off_t len; + u_char *p; + u_char buf[16 * 1024]; + +#define COMPUTE(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)] + + lcrc = 0; + len = 0; + crc_total = ~crc_total; + while ((nr = read(fd, buf, sizeof(buf))) > 0) + for (len += nr, p = buf; nr--; ++p) { + COMPUTE(lcrc, *p); + COMPUTE(crc_total, *p); + } + if (nr < 0) + return (1); + + *clen = len; + + /* Include the length of the file. */ + for (; len != 0; len >>= 8) { + COMPUTE(lcrc, len & 0xff); + COMPUTE(crc_total, len & 0xff); + } + + *cval = ~lcrc; + crc_total = ~crc_total; + return (0); +} diff --git a/files/Sources/files/cksum/crc32.c b/files/Sources/files/cksum/crc32.c new file mode 100644 index 00000000..0dfc89f5 --- /dev/null +++ b/files/Sources/files/cksum/crc32.c @@ -0,0 +1,122 @@ +/* + * This code implements the AUTODIN II polynomial used by Ethernet, + * and can be used to calculate multicast address hash indices. + * It assumes that the low order bits will be transmitted first, + * and consequently the low byte should be sent first when + * the crc computation is finished. The crc should be complemented + * before transmission. + * The variable corresponding to the macro argument "crc" should + * be an unsigned long and should be preset to all ones for Ethernet + * use. An error-free packet will leave 0xDEBB20E3 in the crc. + * Spencer Garrett + */ + +#include +__FBSDID("$FreeBSD: src/usr.bin/cksum/crc32.c,v 1.9 2003/03/13 23:32:28 robert Exp $"); + +#include + +#include +#include +#include + +#include "extern.h" + +#define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff]) + +/* generated using the AUTODIN II polynomial + * x^32 + x^26 + x^23 + x^22 + x^16 + + * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1 + */ +static const uint32_t crctab[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, +}; + +uint32_t crc32_total = 0; + +int +chksum_crc32(int fd, uint32_t *cval, off_t *clen) +{ + uint32_t lcrc = ~0; + ssize_t nr; + off_t len ; + char buf[BUFSIZ], *p ; + + len = 0 ; + crc32_total = ~crc32_total ; + while ((nr = read(fd, buf, sizeof(buf))) > 0) + for (len += nr, p = buf; nr--; ++p) { + CRC(lcrc, *p) ; + CRC(crc32_total, *p) ; + } + if (nr < 0) + return 1 ; + + *clen = len ; + *cval = ~lcrc ; + crc32_total = ~crc32_total ; + return 0 ; +} diff --git a/files/Sources/files/cksum/extern.h b/files/Sources/files/cksum/extern.h new file mode 100644 index 00000000..addf8b48 --- /dev/null +++ b/files/Sources/files/cksum/extern.h @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 1991, 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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. + * + * @(#)extern.h 8.1 (Berkeley) 6/6/93 + * $FreeBSD: src/usr.bin/cksum/extern.h,v 1.6 2003/03/13 23:32:28 robert Exp $ + */ + +#include + +__BEGIN_DECLS +int crc(int, uint32_t *, off_t *); +void pcrc(char *, uint32_t, off_t); +void psum1(char *, uint32_t, off_t); +void psum2(char *, uint32_t, off_t); +int csum1(int, uint32_t *, off_t *); +int csum2(int, uint32_t *, off_t *); +int chksum_crc32(int, uint32_t *, off_t *); +__END_DECLS diff --git a/files/Sources/files/cksum/print.c b/files/Sources/files/cksum/print.c new file mode 100644 index 00000000..8b9c99c5 --- /dev/null +++ b/files/Sources/files/cksum/print.c @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 1991, 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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 +#if 0 +static char sccsid[] = "@(#)print.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* not lint */ + +#include +__FBSDID("$FreeBSD: src/usr.bin/cksum/print.c,v 1.7 2003/03/13 23:32:28 robert Exp $"); + +#include + +#include +#include + +#include "extern.h" +#include "ios_error.h" + +void +pcrc(char *fn, uint32_t val, off_t len) +{ + (void)fprintf(thread_stdout, "%lu %jd", (u_long)val, (intmax_t)len); + if (fn != NULL) + (void)fprintf(thread_stdout, " %s", fn); + (void)fprintf(thread_stdout, "\n"); +} + +void +psum1(char *fn, uint32_t val, off_t len) +{ + (void)fprintf(thread_stdout, "%lu %jd", (u_long)val, (intmax_t)(len + 1023) / 1024); + if (fn != NULL) + (void)fprintf(thread_stdout, " %s", fn); + (void)fprintf(thread_stdout, "\n"); +} + +void +psum2(char *fn, uint32_t val, off_t len) +{ + (void)fprintf(thread_stdout, "%lu %jd", (u_long)val, (intmax_t)(len + 511) / 512); + if (fn != NULL) + (void)fprintf(thread_stdout, " %s", fn); + (void)fprintf(thread_stdout, "\n"); +} diff --git a/files/Sources/files/cksum/sum.1 b/files/Sources/files/cksum/sum.1 new file mode 100644 index 00000000..db048008 --- /dev/null +++ b/files/Sources/files/cksum/sum.1 @@ -0,0 +1 @@ +.so man1/cksum.1 diff --git a/files/Sources/files/cksum/sum1.c b/files/Sources/files/cksum/sum1.c new file mode 100644 index 00000000..fa016ce2 --- /dev/null +++ b/files/Sources/files/cksum/sum1.c @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 1991, 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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 +#if 0 +static char sccsid[] = "@(#)sum1.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* not lint */ + +#include +__FBSDID("$FreeBSD: src/usr.bin/cksum/sum1.c,v 1.8 2003/03/13 23:32:28 robert Exp $"); + +#include + +#include +#include + +#include "extern.h" + +int +csum1(int fd, uint32_t *cval, off_t *clen) +{ + ssize_t nr; + u_int lcrc; + off_t total; + u_char *p; + u_char buf[8192]; + + /* + * 16-bit checksum, rotating right before each addition; + * overflow is discarded. + */ + lcrc = 0; + total = 0; + while ((nr = read(fd, buf, sizeof(buf))) > 0) + for (total += nr, p = buf; nr--; ++p) { + if (lcrc & 1) + lcrc |= 0x10000; + lcrc = ((lcrc >> 1) + *p) & 0xffff; + } + if (nr < 0) + return (1); + + *cval = lcrc; + *clen = total; + return (0); +} diff --git a/files/Sources/files/cksum/sum2.c b/files/Sources/files/cksum/sum2.c new file mode 100644 index 00000000..126b4280 --- /dev/null +++ b/files/Sources/files/cksum/sum2.c @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 1991, 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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 +#if 0 +static char sccsid[] = "@(#)sum2.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: src/usr.bin/cksum/sum2.c,v 1.8 2003/03/13 23:32:28 robert Exp $"); + +#include + +#include +#include + +#include "extern.h" + +int +csum2(int fd, uint32_t *cval, off_t *clen) +{ + uint32_t lcrc; + ssize_t nr; + off_t total; + u_char *p; + u_char buf[8192]; + + /* + * Draft 8 POSIX 1003.2: + * + * s = sum of all bytes + * r = s % 2^16 + (s % 2^32) / 2^16 + * lcrc = (r % 2^16) + r / 2^16 + */ + lcrc = 0; + total = 0; + while ((nr = read(fd, buf, sizeof(buf))) > 0) + for (total += nr, p = buf; nr--; ++p) + lcrc += *p; + if (nr < 0) + return (1); + + lcrc = (lcrc & 0xffff) + (lcrc >> 16); + lcrc = (lcrc & 0xffff) + (lcrc >> 16); + + *cval = lcrc; + *clen = total; + return (0); +} diff --git a/files/Sources/files/compress/compress.1 b/files/Sources/files/compress/compress.1 new file mode 100644 index 00000000..a353c19a --- /dev/null +++ b/files/Sources/files/compress/compress.1 @@ -0,0 +1,253 @@ +.\" Copyright (c) 1986, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" James A. Woods, derived from original work by Spencer Thomas +.\" and Joseph Orost. +.\" +.\" 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. +.\" +.\" @(#)compress.1 8.2 (Berkeley) 4/18/94 +.\" $FreeBSD: src/usr.bin/compress/compress.1,v 1.23 2010/12/11 08:32:16 joel Exp $ +.\" +.Dd May 17, 2002 +.Dt COMPRESS 1 +.Os +.Sh NAME +.Nm compress , +.Nm uncompress +.Nd compress and expand data +.Sh SYNOPSIS +.Nm +.Op Fl fv +.Op Fl b Ar bits +.Op Ar +.Nm +.Fl c +.Op Fl b Ar bits +.Op Ar file +.Nm uncompress +.Op Fl fv +.Op Ar +.Nm uncompress +.Fl c +.Op Ar file +.Sh DESCRIPTION +The +.Nm +utility reduces the size of files using adaptive Lempel-Ziv coding. +Each +.Ar file +is renamed to the same name plus the extension +.Pa .Z . +A +.Ar file +argument with a +.Pa .Z +extension will be ignored except it will cause an +error exit after other arguments are processed. +If compression would not reduce the size of a +.Ar file , +the file is ignored. +.Pp +The +.Nm uncompress +utility restores compressed files to their original form, renaming the +files by deleting the +.Pa .Z +extensions. +A file specification need not include the file's +.Pa .Z +extension. +If a file's name in its file system does not have a +.Pa .Z +extension, it will not be uncompressed and it will cause +an error exit after other arguments are processed. +.Pp +If renaming the files would cause files to be overwritten and the standard +input device is a terminal, the user is prompted (on the standard error +output) for confirmation. +If prompting is not possible or confirmation is not received, the files +are not overwritten. +.Pp +As many of the modification time, access time, file flags, file mode, +user ID, and group ID as allowed by permissions are retained in the +new file. +.Pp +If no files are specified or a +.Ar file +argument is a single dash +.Pq Sq Fl , +the standard input is compressed or uncompressed to the standard output. +If either the input and output files are not regular files, the checks for +reduction in size and file overwriting are not performed, the input file is +not removed, and the attributes of the input file are not retained +in the output file. +.Pp +The options are as follows: +.Bl -tag -width ".Fl b Ar bits" +.It Fl b Ar bits +The code size (see below) is limited to +.Ar bits , +which must be in the range 9..16. +The default is 16. +.It Fl c +Compressed or uncompressed output is written to the standard output. +No files are modified. +The +.Fl v +option is ignored. +Compression is attempted even if the results will be larger than the +original. +.It Fl f +Files are overwritten without prompting for confirmation. +Also, for +.Nm compress , +files are compressed even if they are not actually reduced in size. +.It Fl v +Print the percentage reduction of each file. +Ignored by +.Nm uncompress +or if the +.Fl c +option is also used. +.El +.Pp +The +.Nm +utility uses a modified Lempel-Ziv algorithm. +Common substrings in the file are first replaced by 9-bit codes 257 and up. +When code 512 is reached, the algorithm switches to 10-bit codes and +continues to use more bits until the +limit specified by the +.Fl b +option or its default is reached. +.Pp +After the limit is reached, +.Nm +periodically checks the compression ratio. +If it is increasing, +.Nm +continues to use the existing code dictionary. +However, if the compression ratio decreases, +.Nm +discards the table of substrings and rebuilds it from scratch. +This allows +the algorithm to adapt to the next "block" of the file. +.Pp +The +.Fl b +option is unavailable for +.Nm uncompress +since the +.Ar bits +parameter specified during compression +is encoded within the output, along with +a magic number to ensure that neither decompression of random data nor +recompression of compressed data is attempted. +.Pp +The amount of compression obtained depends on the size of the +input, the number of +.Ar bits +per code, and the distribution of common substrings. +Typically, text such as source code or English is reduced by 50\-60%. +Compression is generally much better than that achieved by Huffman +coding (as used in the historical command pack), or adaptive Huffman +coding (as used in the historical command compact), and takes less +time to compute. +.Sh EXIT STATUS +.Ex -std compress uncompress +.Pp +The +.Nm compress +utility exits 2 if attempting to compress a file would not reduce its size +and the +.Fl f +option was not specified and if no other error occurs. +.Sh SEE ALSO +.Xr gunzip 1 , +.Xr gzexe 1 , +.Xr gzip 1 , +.Xr zcat 1 , +.Xr zmore 1 , +.Xr znew 1 +.Rs +.%A Welch, Terry A. +.%D June, 1984 +.%T "A Technique for High Performance Data Compression" +.%J "IEEE Computer" +.%V 17:6 +.%P pp. 8-19 +.Re +.Sh STANDARDS +The +.Nm compress +and +.Nm uncompress +utilities conform to +.St -p1003.1-2001 . +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . +.Sh BUGS +Some of these might be considered otherwise-undocumented features. +.Pp +.Nm compress : +If the utility does not compress a file because doing so would not +reduce its size, and a file of the same name except with an +.Pa .Z +extension exists, the named file is not really ignored as stated above; +it causes a prompt to confirm the overwriting of the file with the extension. +If the operation is confirmed, that file is deleted. +.Pp +.Nm uncompress : +If an empty file is compressed (using +.Fl f ) , +the resulting +.Pa .Z +file is also empty. +That seems right, but if +.Nm uncompress +is then used on that file, an error will occur. +.Pp +Both utilities: If a +.Sq Fl +argument is used and the utility prompts the user, the standard input +is taken as the user's reply to the prompt. +.Pp +Both utilities: +If the specified file does not exist, but a similarly-named one with (for +.Nm compress ) +or without (for +.Nm uncompress ) +a +.Pa .Z +extension does exist, the utility will waste the user's time by not +immediately emitting an error message about the missing file and +continuing. +Instead, it first asks for confirmation to overwrite +the existing file and then does not overwrite it. diff --git a/files/Sources/files/compress/compress.c b/files/Sources/files/compress/compress.c new file mode 100644 index 00000000..e83cd1a9 --- /dev/null +++ b/files/Sources/files/compress/compress.c @@ -0,0 +1,465 @@ +/*- + * Copyright (c) 1992, 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. + */ + +#include +#ifndef lint +__used static const char copyright[] = +"@(#) Copyright (c) 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)compress.c 8.2 (Berkeley) 1/7/94"; +#endif +#endif + +#include +__FBSDID("$FreeBSD: src/usr.bin/compress/compress.c,v 1.23 2010/12/11 08:32:16 joel Exp $"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "zopen.h" +#include "ios_error.h" + +static void compress(const char *, const char *, int); +static void cwarn(const char *, ...) __printflike(1, 2); +static void cwarnx(const char *, ...) __printflike(1, 2); +static void decompress(const char *, const char *, int); +static int permission(const char *); +static void setfile(const char *, struct stat *); +static void usage(int); + +static int eval, force, verbose, cat; + +int +compress_main(int argc, char *argv[]) +{ + enum {COMPRESS, DECOMPRESS} style = 0; + size_t len; + int bits, ch; + char *p, newname[MAXPATHLEN]; + + if (argc < 1) + usage(1); + // init all flags + eval = force = verbose = cat = 0; + optind = 1; opterr = 1; optreset = 1; + if ((p = rindex(argv[0], '/')) == NULL) + p = argv[0]; + else + ++p; + if (!strcmp(p, "uncompress")) + style = DECOMPRESS; + else if (!strcmp(p, "compress")) + style = COMPRESS; + else if (!strcmp(p, "zcat")) { + cat = 1; + style = DECOMPRESS; + } else { + // errx(1, "unknown program name"); + fprintf(thread_stderr, "unknown program name: %s\n", p); + ios_exit(1); + } + + bits = 0; + while ((ch = getopt(argc, argv, "b:cdfv")) != -1) + switch(ch) { + case 'b': + bits = strtol(optarg, &p, 10); + if (*p) { + errx(1, "illegal bit count -- %s", optarg); + } + break; + case 'c': + cat = 1; + break; + case 'd': /* Backward compatible. */ + style = DECOMPRESS; + break; + case 'f': + force = 1; + break; + case 'v': + verbose = 1; + break; + case '?': + default: + usage(style == COMPRESS); + } + argc -= optind; + argv += optind; + + if (argc == 0) { + cat = 1; + switch(style) { + case COMPRESS: + (void)compress("/dev/stdin", "/dev/stdout", bits); + break; + case DECOMPRESS: + (void)decompress("/dev/stdin", "/dev/stdout", bits); + break; + } + exit (eval); + } + + if (cat == 1 && argc > 1) { + errx(1, "the -c option permits only a single file argument"); + } + + for (; *argv; ++argv) + switch(style) { + case COMPRESS: + if (strcmp(*argv, "-") == 0) { + cat = 1; + compress("/dev/stdin", "/dev/stdout", bits); + break; + } else if (cat) { + compress(*argv, "/dev/stdout", bits); + break; + } + if ((p = rindex(*argv, '.')) != NULL && + !strcmp(p, ".Z")) { + cwarnx("%s: name already has trailing .Z", + *argv); + break; + } + len = strlen(*argv); + if (len > sizeof(newname) - 3) { + cwarnx("%s: name too long", *argv); + break; + } + memmove(newname, *argv, len); + newname[len] = '.'; + newname[len + 1] = 'Z'; + newname[len + 2] = '\0'; + compress(*argv, newname, bits); + break; + case DECOMPRESS: + if (strcmp(*argv, "-") == 0) { + cat = 1; + decompress("/dev/stdin", "/dev/stdout", bits); + break; + } + len = strlen(*argv); + if ((p = rindex(*argv, '.')) == NULL || + strcmp(p, ".Z")) { + if (len > sizeof(newname) - 3) { + cwarnx("%s: name too long", *argv); + break; + } + memmove(newname, *argv, len); + newname[len] = '.'; + newname[len + 1] = 'Z'; + newname[len + 2] = '\0'; + decompress(newname, + cat ? "/dev/stdout" : *argv, bits); + } else { + if (len - 2 > sizeof(newname) - 1) { + cwarnx("%s: name too long", *argv); + break; + } + memmove(newname, *argv, len - 2); + newname[len - 2] = '\0'; + decompress(*argv, + cat ? "/dev/stdout" : newname, bits); + } + break; + } + exit (eval); +} + +void +compress(const char *in, const char *out, int bits) +{ + size_t nr; + struct stat isb, sb; + FILE *ifp = NULL, *ofp = NULL; + int exists, isreg, oreg; + u_char buf[1024]; + + exists = !stat(out, &sb); + if (!force && exists && S_ISREG(sb.st_mode) && !cat && !permission(out)) { + cwarnx("%s already exists", out); + return; + } + isreg = oreg = !exists || S_ISREG(sb.st_mode); + + if ((ifp = fopen(in, "r")) == NULL) { + cwarn("%s", in); + return; + } + if (stat(in, &isb)) { /* DON'T FSTAT! */ + cwarn("%s", in); + goto err; + } + if (!S_ISREG(isb.st_mode)) + isreg = 0; + + if ((ofp = zopen(out, "w", bits)) == NULL) { + cwarn("%s", out); + goto err; + } + while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0) + if (fwrite(buf, 1, nr, ofp) != nr) { + cwarn("%s", out); + goto err; + } + + if (ferror(ifp) || fclose(ifp)) { + cwarn("%s", in); + goto err; + } + ifp = NULL; + + if (fclose(ofp)) { + cwarn("%s", out); + goto err; + } + ofp = NULL; + + if (!cat && isreg) { + if (stat(out, &sb)) { + cwarn("%s", out); + goto err; + } + + if (!force && sb.st_size >= isb.st_size) { + if (verbose) + (void)fprintf(stderr, "compress: %s: file would grow; left unmodified\n", + in); + eval = 2; + if (unlink(out)) + cwarn("%s", out); + goto err; + } + + setfile(out, &isb); + + if (unlink(in)) + cwarn("%s", in); + + if (verbose) { + (void)fprintf(stderr, "%s: ", out); + if (isb.st_size > sb.st_size) + (void)fprintf(stderr, "%.0f%% compression\n", + ((float)sb.st_size / isb.st_size) * 100.0); + else + (void)fprintf(stderr, "%.0f%% expansion\n", + ((float)isb.st_size / sb.st_size) * 100.0); + } + } + return; + +err: if (ofp) { + if (!cat && oreg) + (void)unlink(out); + (void)fclose(ofp); + } + if (ifp) + (void)fclose(ifp); +} + +void +decompress(const char *in, const char *out, int bits) +{ + size_t nr; + struct stat sb; + FILE *ifp, *ofp; + int exists, isreg, oreg; + u_char buf[1024]; + + exists = !stat(out, &sb); + if (!force && exists && S_ISREG(sb.st_mode) && !cat && !permission(out)) { + cwarnx("%s already exists", out); + return; + } + isreg = oreg = !exists || S_ISREG(sb.st_mode); + + ofp = NULL; + if ((ifp = zopen(in, "r", bits)) == NULL) { + cwarn("%s", in); + return; + } + if (stat(in, &sb)) { + cwarn("%s", in); + goto err; + } + if (!S_ISREG(sb.st_mode)) + isreg = 0; + + /* + * Try to read the first few uncompressed bytes from the input file + * before blindly truncating the output file. + */ + if ((nr = fread(buf, 1, sizeof(buf), ifp)) == 0) { + cwarn("%s", in); + (void)fclose(ifp); + return; + } + if ((ofp = fopen(out, "w")) == NULL || + (nr != 0 && fwrite(buf, 1, nr, ofp) != nr)) { + cwarn("%s", out); + (void)fclose(ifp); + return; + } + + while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0) + if (fwrite(buf, 1, nr, ofp) != nr) { + cwarn("%s", out); + goto err; + } + + if (ferror(ifp) || fclose(ifp)) { + cwarn("%s", in); + goto err; + } + ifp = NULL; + + if (fclose(ofp)) { + cwarn("%s", out); + goto err; + } + + if (!cat && isreg) { + setfile(out, &sb); + + if (unlink(in)) + cwarn("%s", in); + if (verbose) { + struct stat isb = sb; + stat(out, &sb); + (void)fprintf(stderr, "%s: ", out); + if (isb.st_size > sb.st_size) + (void)fprintf(stderr, "%.0f%% compression\n", + ((float)sb.st_size / isb.st_size) * 100.0); + else + (void)fprintf(stderr, "%.0f%% expansion\n", + ((float)isb.st_size / sb.st_size) * 100.0); + } + } + return; + +err: if (ofp) { + if (!cat && oreg) + (void)unlink(out); + (void)fclose(ofp); + } + if (ifp) + (void)fclose(ifp); +} + +void +setfile(const char *name, struct stat *fs) +{ + static struct timeval tv[2]; + + fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; + + TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); + TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); + if (utimes(name, tv)) + cwarn("utimes: %s", name); + + /* + * Changing the ownership probably won't succeed, unless we're root + * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting + * the mode; current BSD behavior is to remove all setuid bits on + * chown. If chown fails, lose setuid/setgid bits. + */ + if (chown(name, fs->st_uid, fs->st_gid)) { + if (errno != EPERM) + cwarn("chown: %s", name); + fs->st_mode &= ~(S_ISUID|S_ISGID); + } + if (chmod(name, fs->st_mode) && errno != ENOTSUP) + cwarn("chmod: %s", name); + + if (chflags(name, fs->st_flags) && errno != ENOTSUP) + cwarn("chflags: %s", name); +} + +int +permission(const char *fname) +{ + int ch, first; + + if (!ios_isatty(STDERR_FILENO)) + return (0); + (void)fprintf(stderr, "overwrite %s? ", fname); + fflush(stderr); + first = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + return (first == 'y'); +} + +void +usage(int iscompress) +{ + if (iscompress) + (void)fprintf(stderr, + "usage: compress [-cfv] [-b bits] [file ...]\n"); + else + (void)fprintf(stderr, + "usage: uncompress [-cfv] [-b bits] [file ...]\n"); + exit(1); +} + +void +cwarnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); + eval = 1; +} + +void +cwarn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarn(fmt, ap); + va_end(ap); + eval = 1; +} diff --git a/files/Sources/files/compress/doc/NOTES b/files/Sources/files/compress/doc/NOTES new file mode 100644 index 00000000..4ced6896 --- /dev/null +++ b/files/Sources/files/compress/doc/NOTES @@ -0,0 +1,142 @@ + + $FreeBSD: src/usr.bin/compress/doc/NOTES,v 1.3 2011/10/16 14:30:28 eadler Exp $ + +From: James A. Woods + +>From vn Fri Dec 2 18:05:27 1988 +Subject: Re: Looking for C source for RSA +Newsgroups: sci.crypt + +# Illegitimi noncarborundum + +Patents are a tar pit. + +A good case can be made that most are just a license to sue, and nothing +is illegal until a patent is upheld in court. + +For example, if you receive netnews by means other than 'nntp', +these very words are being modulated by 'compress', +a variation on the patented Lempel-Ziv-Welch algorithm. + +Original Ziv-Lempel is patent number 4,464,650, and the more powerful +LZW method is #4,558,302. Yet despite any similarities between 'compress' +and LZW (the public-domain 'compress' code was designed and given to the +world before the ink on the Welch patent was dry), no attorneys from Sperry +(the assignee) have asked you to unplug your Usenet connection. + +Why? I can't speak for them, but it is possible the claims are too broad, +or, just as bad, not broad enough. ('compress' does things not mentioned +in the Welch patent.) Maybe they realize that they can commercialize +LZW better by selling hardware implementations rather than by licensing +software. Again, the LZW software delineated in the patent is *not* +the same as that of 'compress'. + +At any rate, court-tested software patents are a different animal; +corporate patents in a portfolio are usually traded like baseball cards +to shut out small fry rather than actually be defended before +non-technical juries. Perhaps RSA will undergo this test successfully, +although the grant to "exclude others from making, using, or selling" +the invention would then only apply to the U.S. (witness the +Genentech patent of the TPA molecule in the U.S. but struck down +in Great Britain as too broad.) + +The concept is still exotic for those who learned in school the rule of thumb +that one may patent "apparatus" but not an "idea". +Apparently this all changed in Diamond v. Diehr (1981) when the U. S. Supreme +Court reversed itself. + +Scholars should consult the excellent article in the Washington and Lee +Law Review (fall 1984, vol. 41, no. 4) by Anthony and Colwell for a +comprehensive survey of an area which will remain murky for some time. + +Until the dust clears, how you approach ideas which are patented depends +on how paranoid you are of a legal onslaught. Arbitrary? Yes. But +the patent bar the CCPA (Court of Customs and Patent Appeals) +thanks you for any uncertainty as they, at least, stand to gain +from any trouble. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +From: James A. Woods +Subject: Re: Looking for C source for RSA (actually 'compress' patents) + + In article <2042@eos.UUCP> you write: + >The concept is still exotic for those who learned in school the rule of thumb + >that one may patent "apparatus" but not an "idea". + +A rule of thumb that has never been completely valid, as any chemical +engineer can tell you. (Chemical processes were among the earliest patents, +as I recall.) + + ah yes -- i date myself when relaying out-of-date advice from elderly + attorneys who don't even specialize in patents. one other interesting + class of patents include the output of optical lens design programs, + which yield formulae which can then fairly directly can be molded + into glass. although there are restrictions on patenting equations, + the "embedded systems" seem to fly past the legal gauntlets. + + anyway, I'm still learning about intellectual property law after + several conversations from a Unisys (nee sperry) lawyer re 'compress'. + + it's more complicated than this, but they're letting (oral + communication only) software versions of 'compress' slide + as far as licensing fees go. this includes 'arc', 'stuffit', + and other commercial wrappers for 'compress'. yet they are + signing up licensees for hardware chips. Hewlett-Packard + supposedly has an active vlsi project, and Unisys has + board-level LZW-based tape controllers. (to build LZW into + a disk controller would be strange, as you'd have to build + in a filesystem too!) + + it's byzantine + that Unisys is in a tiff with HP regarding the patents, + after discovering some sort of "compress" button on some + HP terminal product. why? well, professor Abraham Lempel jumped + from being department chairman of computer science at technion in + Israel to sperry (where he got the first patent), but then to work + at Hewlett-Packard on sabbatical. the second Welch patent + is only weakly derivative of the first, so they want chip + licenses and HP relented. however, everyone agrees something + like the current Unix implementation is the way to go with + software, so HP (and UCB) long ago asked spencer Thomas and i to sign + off on copyright permission (although they didn't need to, it being pd). + Lempel, HP, and Unisys grumbles they can't make money off the + software since a good free implementation (not the best -- + i have more ideas!) escaped via Usenet. (Lempel's own pascal + code was apparently horribly slow.) + i don't follow the IBM 'arc' legal bickering; my impression + is that the pc folks are making money off the archiver/wrapper + look/feel of the thing [if ms-dos can be said to have a look and feel]. + + now where is telebit with the compress firmware? in a limbo + netherworld, probably, with sperry still welcoming outfits + to sign patent licenses, a common tactic to bring other small fry + into the fold. the guy who crammed 12-bit compress into the modem + there left. also what is transpiring with 'compress' and sys 5 rel 4? + beats me, but if sperry got a hold of them on these issues, + at&t would likely re-implement another algorithm if they + thought 'compress' infringes. needful to say, i don't think + it does after the above mentioned legal conversation. + my own beliefs on whether algorithms should be patentable at all + change with the weather. if the courts finally nail down + patent protection for algorithms, academic publication in + textbooks will be somewhat at odds with the engineering world, + where the textbook codes will simply be a big tease to get + money into the patent holder coffers... + + oh, if you implement LZW from the patent, you won't get + good rates because it doesn't mention adaptive table reset, + lack thereof being *the* serious deficiency of Thomas' first version. + + now i know that patent law generally protects against independent + re-invention (like the 'xor' hash function pleasantly mentioned + in the patent [but not the paper]). + but the upshot is that if anyone ever wanted to sue us, + we're partially covered with + independently-developed twists, plus the fact that some of us work + in a bureaucratic morass (as contractor to a public agency in my case). + + quite a mess, huh? I've wanted to tell someone this stuff + for a long time, for posterity if nothing else. + +james + diff --git a/files/Sources/files/compress/doc/README b/files/Sources/files/compress/doc/README new file mode 100644 index 00000000..0828cdfb --- /dev/null +++ b/files/Sources/files/compress/doc/README @@ -0,0 +1,284 @@ + + @(#)README 8.1 (Berkeley) 6/9/93 + $FreeBSD: src/usr.bin/compress/doc/README,v 1.3 2002/12/30 21:18:11 schweikh Exp $ + +Compress version 4.0 improvements over 3.0: + o compress() speedup (10-50%) by changing division hash to xor + o decompress() speedup (5-10%) + o Memory requirements reduced (3-30%) + o Stack requirements reduced to less than 4kb + o Removed 'Big+Fast' compress code (FBITS) because of compress speedup + o Portability mods for Z8000 and PC/XT (but not zeus 3.2) + o Default to 'quiet' mode + o Unification of 'force' flags + o Manual page overhaul + o Portability enhancement for M_XENIX + o Removed text on #else and #endif + o Added "-V" switch to print version and options + o Added #defines for SIGNED_COMPARE_SLOW + o Added Makefile and "usermem" program + o Removed all floating point computations + o New programs: [deleted] + +The "usermem" script attempts to determine the maximum process size. Some +editing of the script may be necessary (see the comments). [It should work +fine on 4.3 BSD.] If you can't get it to work at all, just create file +"USERMEM" containing the maximum process size in decimal. + +The following preprocessor symbols control the compilation of "compress.c": + + o USERMEM Maximum process memory on the system + o SACREDMEM Amount to reserve for other processes + o SIGNED_COMPARE_SLOW Unsigned compare instructions are faster + o NO_UCHAR Don't use "unsigned char" types + o BITS Overrules default set by USERMEM-SACREDMEM + o vax Generate inline assembler + o interdata Defines SIGNED_COMPARE_SLOW + o M_XENIX Makes arrays < 65536 bytes each + o pdp11 BITS=12, NO_UCHAR + o z8000 BITS=12 + o pcxt BITS=12 + o BSD4_2 Allow long filenames ( > 14 characters) & + Call setlinebuf(stderr) + +The difference "usermem-sacredmem" determines the maximum BITS that can be +specified with the "-b" flag. + +memory: at least BITS +------ -- ----- ---- + 433,484 16 + 229,600 15 + 127,536 14 + 73,464 13 + 0 12 + +The default is BITS=16. + +The maximum bits can be overruled by specifying "-DBITS=bits" at +compilation time. + +WARNING: files compressed on a large machine with more bits than allowed by +a version of compress on a smaller machine cannot be decompressed! Use the +"-b12" flag to generate a file on a large machine that can be uncompressed +on a 16-bit machine. + +The output of compress 4.0 is fully compatible with that of compress 3.0. +In other words, the output of compress 4.0 may be fed into uncompress 3.0 or +the output of compress 3.0 may be fed into uncompress 4.0. + +The output of compress 4.0 not compatible with that of +compress 2.0. However, compress 4.0 still accepts the output of +compress 2.0. To generate output that is compatible with compress +2.0, use the undocumented "-C" flag. + + -from mod.sources, submitted by vax135!petsd!joe (Joe Orost), 8/1/85 +-------------------------------- + +Enclosed is compress version 3.0 with the following changes: + +1. "Block" compression is performed. After the BITS run out, the + compression ratio is checked every so often. If it is decreasing, + the table is cleared and a new set of substrings are generated. + + This makes the output of compress 3.0 not compatible with that of + compress 2.0. However, compress 3.0 still accepts the output of + compress 2.0. To generate output that is compatible with compress + 2.0, use the undocumented "-C" flag. + +2. A quiet "-q" flag has been added for use by the news system. + +3. The character chaining has been deleted and the program now uses + hashing. This improves the speed of the program, especially + during decompression. Other speed improvements have been made, + such as using putc() instead of fwrite(). + +4. A large table is used on large machines when a relatively small + number of bits is specified. This saves much time when compressing + for a 16-bit machine on a 32-bit virtual machine. Note that the + speed improvement only occurs when the input file is > 30000 + characters, and the -b BITS is less than or equal to the cutoff + described below. + +Most of these changes were made by James A. Woods (ames!jaw). Thank you +James! + +To compile compress: + + cc -O -DUSERMEM=usermem -o compress compress.c + +Where "usermem" is the amount of physical user memory available (in bytes). +If any physical memory is to be reserved for other processes, put in +"-DSACREDMEM sacredmem", where "sacredmem" is the amount to be reserved. + +The difference "usermem-sacredmem" determines the maximum BITS that can be +specified, and the cutoff bits where the large+fast table is used. + +memory: at least BITS cutoff +------ -- ----- ---- ------ + 4,718,592 16 13 + 2,621,440 16 12 + 1,572,864 16 11 + 1,048,576 16 10 + 631,808 16 -- + 329,728 15 -- + 178,176 14 -- + 99,328 13 -- + 0 12 -- + +The default memory size is 750,000 which gives a maximum BITS=16 and no +large+fast table. + +The maximum bits can be overruled by specifying "-DBITS=bits" at +compilation time. + +If your machine doesn't support unsigned characters, define "NO_UCHAR" +when compiling. + +If your machine has "int" as 16-bits, define "SHORT_INT" when compiling. + +After compilation, move "compress" to a standard executable location, such +as /usr/local. Then: + cd /usr/local + ln compress uncompress + ln compress zcat + +On machines that have a fixed stack size (such as Perkin-Elmer), set the +stack to at least 12kb. ("setstack compress 12" on Perkin-Elmer). + +Next, install the manual (compress.l). + cp compress.l /usr/man/manl + cd /usr/man/manl + ln compress.l uncompress.l + ln compress.l zcat.l + + - or - + + cp compress.l /usr/man/man1/compress.1 + cd /usr/man/man1 + ln compress.1 uncompress.1 + ln compress.1 zcat.1 + + regards, + petsd!joe + +Here is a note from the net: + +>From hplabs!pesnta!amd!turtlevax!ken Sat Jan 5 03:35:20 1985 +Path: ames!hplabs!pesnta!amd!turtlevax!ken +From: ken@turtlevax.UUCP (Ken Turkowski) +Newsgroups: net.sources +Subject: Re: Compress release 3.0 : sample Makefile +Organization: CADLINC, Inc. @ Menlo Park, CA + +In the compress 3.0 source recently posted to mod.sources, there is a +#define variable which can be set for optimum performance on a machine +with a large amount of memory. A program (usermem) to calculate the +usable amount of physical user memory is enclosed, as well as a sample +4.2BSD Vax Makefile for compress. + +Here is the README file from the previous version of compress (2.0): + +>Enclosed is compress.c version 2.0 with the following bugs fixed: +> +>1. The packed files produced by compress are different on different +> machines and dependent on the vax sysgen option. +> The bug was in the different byte/bit ordering on the +> various machines. This has been fixed. +> +> This version is NOT compatible with the original vax posting +> unless the '-DCOMPATIBLE' option is specified to the C +> compiler. The original posting has a bug which I fixed, +> causing incompatible files. I recommend you NOT to use this +> option unless you already have a lot of packed files from +> the original posting by Thomas. +>2. The exit status is not well defined (on some machines) causing the +> scripts to fail. +> The exit status is now 0,1 or 2 and is documented in +> compress.l. +>3. The function getopt() is not available in all C libraries. +> The function getopt() is no longer referenced by the +> program. +>4. Error status is not being checked on the fwrite() and fflush() calls. +> Fixed. +> +>The following enhancements have been made: +> +>1. Added facilities of "compact" into the compress program. "Pack", +> "Unpack", and "Pcat" are no longer required (no longer supplied). +>2. Installed work around for C compiler bug with "-O". +>3. Added a magic number header (\037\235). Put the bits specified +> in the file. +>4. Added "-f" flag to force overwrite of output file. +>5. Added "-c" flag and "zcat" program. 'ln compress zcat' after you +> compile. +>6. The 'uncompress' script has been deleted; simply +> 'ln compress uncompress' after you compile and it will work. +>7. Removed extra bit masking for machines that support unsigned +> characters. If your machine doesn't support unsigned characters, +> define "NO_UCHAR" when compiling. +> +>Compile "compress.c" with "-O -o compress" flags. Move "compress" to a +>standard executable location, such as /usr/local. Then: +> cd /usr/local +> ln compress uncompress +> ln compress zcat +> +>On machines that have a fixed stack size (such as Perkin-Elmer), set the +>stack to at least 12kb. ("setstack compress 12" on Perkin-Elmer). +> +>Next, install the manual (compress.l). +> cp compress.l /usr/man/manl - or - +> cp compress.l /usr/man/man1/compress.1 +> +>Here is the README that I sent with my first posting: +> +>>Enclosed is a modified version of compress.c, along with scripts to make it +>>run identically to pack(1), unpack(1), and pcat(1). Here is what I +>>(petsd!joe) and a colleague (petsd!peora!srd) did: +>> +>>1. Removed VAX dependencies. +>>2. Changed the struct to separate arrays; saves mucho memory. +>>3. Did comparisons in unsigned, where possible. (Faster on Perkin-Elmer.) +>>4. Sorted the character next chain and changed the search to stop +>>prematurely. This saves a lot on the execution time when compressing. +>> +>>This version is totally compatible with the original version. Even though +>>lint(1) -p has no complaints about compress.c, it won't run on a 16-bit +>>machine, due to the size of the arrays. +>> +>>Here is the README file from the original author: +>> +>>>Well, with all this discussion about file compression (for news batching +>>>in particular) going around, I decided to implement the text compression +>>>algorithm described in the June Computer magazine. The author claimed +>>>blinding speed and good compression ratios. It's certainly faster than +>>>compact (but, then, what wouldn't be), but it's also the same speed as +>>>pack, and gets better compression than both of them. On 350K bytes of +>>>Unix-wizards, compact took about 8 minutes of CPU, pack took about 80 +>>>seconds, and compress (herein) also took 80 seconds. But, compact and +>>>pack got about 30% compression, whereas compress got over 50%. So, I +>>>decided I had something, and that others might be interested, too. +>>> +>>>As is probably true of compact and pack (although I haven't checked), +>>>the byte order within a word is probably relevant here, but as long as +>>>you stay on a single machine type, you should be ok. (Can anybody +>>>elucidate on this?) There are a couple of asm's in the code (extv and +>>>insv instructions), so anyone porting it to another machine will have to +>>>deal with this anyway (and could probably make it compatible with Vax +>>>byte order at the same time). Anyway, I've linted the code (both with +>>>and without -p), so it should run elsewhere. Note the longs in the +>>>code, you can take these out if you reduce BITS to <= 15. +>>> +>>>Have fun, and as always, if you make good enhancements, or bug fixes, +>>>I'd like to see them. +>>> +>>>=Spencer (thomas@utah-20, {harpo,hplabs,arizona}!utah-cs!thomas) +>> +>> regards, +>> joe +>> +>>-- +>>Full-Name: Joseph M. Orost +>>UUCP: ..!{decvax,ucbvax,ihnp4}!vax135!petsd!joe +>>US Mail: MS 313; Perkin-Elmer; 106 Apple St; Tinton Falls, NJ 07724 +>>Phone: (201) 870-5844 diff --git a/files/Sources/files/compress/doc/revision.log b/files/Sources/files/compress/doc/revision.log new file mode 100644 index 00000000..04c96e68 --- /dev/null +++ b/files/Sources/files/compress/doc/revision.log @@ -0,0 +1,118 @@ +/* $FreeBSD: src/usr.bin/compress/doc/revision.log,v 1.5 2011/03/31 14:35:33 emaste Exp $ */ + +/* + * $Header: compress.c,v 4.0 85/07/30 12:50:00 joe Release $ + * + * Revision 4.0 85/07/30 12:50:00 joe + * Removed ferror() calls in output routine on every output except first. + * Prepared for release to the world. + * + * Revision 3.6 85/07/04 01:22:21 joe + * Remove much wasted storage by overlaying hash table with the tables + * used by decompress: tab_suffix[1<putc] and + * added signal catcher [plus beef in writeerr()] to delete effluvia. + * + * Revision 2.0 84/08/28 22:00:00 petsd!joe + * Add check for foreground before prompting user. Insert maxbits into + * compressed file. Force file being uncompressed to end with ".Z". + * Added "-c" flag and "zcat". Prepared for release. + * + * Revision 1.10 84/08/24 18:28:00 turtlevax!ken + * Will only compress regular files (no directories), added a magic number + * header (plus an undocumented -n flag to handle old files without headers), + * added -f flag to force overwriting of possibly existing destination file, + * otherwise the user is prompted for a response. Will tack on a .Z to a + * filename if it doesn't have one when decompressing. Will only replace + * file if it was compressed. + * + * Revision 1.9 84/08/16 17:28:00 turtlevax!ken + * Removed scanargs(), getopt(), added .Z extension and unlimited number of + * filenames to compress. Flags may be clustered (-Ddvb12) or separated + * (-D -d -v -b 12), or combination thereof. Modes and other status is + * copied with copystat(). -O bug for 4.2 seems to have disappeared with + * 1.8. + * + * Revision 1.8 84/08/09 23:15:00 joe + * Made it compatible with vax version, installed jim's fixes/enhancements + * + * Revision 1.6 84/08/01 22:08:00 joe + * Sped up algorithm significantly by sorting the compress chain. + * + * Revision 1.5 84/07/13 13:11:00 srd + * Added C version of vax asm routines. Changed structure to arrays to + * save much memory. Do unsigned compares where possible (faster on + * Perkin-Elmer) + * + * Revision 1.4 84/07/05 03:11:11 thomas + * Clean up the code a little and lint it. (Lint complains about all + * the regs used in the asm, but I'm not going to "fix" this.) + * + * Revision 1.3 84/07/05 02:06:54 thomas + * Minor fixes. + * + * Revision 1.2 84/07/05 00:27:27 thomas + * Add variable bit length output. + * + */ + +static char rcs_ident[] = + "$Header: compress.c,v 4.0 85/07/30 12:50:00 joe Release $"; diff --git a/files/Sources/files/compress/uncompress.1 b/files/Sources/files/compress/uncompress.1 new file mode 100644 index 00000000..208c3ce8 --- /dev/null +++ b/files/Sources/files/compress/uncompress.1 @@ -0,0 +1 @@ +.so man1/compress.1 \ No newline at end of file diff --git a/files/Sources/files/compress/zcat.sh b/files/Sources/files/compress/zcat.sh new file mode 100644 index 00000000..6799f461 --- /dev/null +++ b/files/Sources/files/compress/zcat.sh @@ -0,0 +1,38 @@ +#!/bin/sh - +# $NetBSD: zcat.sh,v 1.4 1995/03/26 19:54:37 glass Exp $ +# +# Copyright (c) 1992, 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. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 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. +# +# @(#)zcat.sh 8.1 (Berkeley) 6/6/93 +# + +uncompress -c $* diff --git a/files/Sources/files/compress/zopen.3 b/files/Sources/files/compress/zopen.3 new file mode 100644 index 00000000..f6c7f344 --- /dev/null +++ b/files/Sources/files/compress/zopen.3 @@ -0,0 +1,137 @@ +.\" Copyright (c) 1992, 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. +.\" +.\" @(#)zopen.3 8.1 (Berkeley) 6/9/93 +.\" $FreeBSD: src/usr.bin/compress/zopen.3,v 1.9 2010/12/11 08:32:16 joel Exp $ +.\" +.Dd June 9, 1993 +.Dt ZOPEN 3 +.Os +.Sh NAME +.Nm zopen +.Nd compressed stream open function +.Sh SYNOPSIS +.Fd #include \&"zopen.h\&" +.Ft FILE * +.Fn zopen "const char *path" "const char *mode" "int bits" +.Sh DESCRIPTION +The +.Fn zopen +function +opens the compressed file whose name is the string pointed to by +.Fa path +and associates a stream with it. +.Pp +The argument +.Fa mode +points to one of the following one-character strings: +.Bl -tag -width indent +.It Dq Li r +Open compressed file for reading. +The stream is positioned at the beginning of the file. +.It Dq Li w +Truncate file to zero length or create compressed file for writing. +The stream is positioned at the beginning of the file. +.El +.Pp +Any created files will have mode +.Pf \\*q Dv S_IRUSR +\&| +.Dv S_IWUSR +\&| +.Dv S_IRGRP +\&| +.Dv S_IWGRP +\&| +.Dv S_IROTH +\&| +.Dv S_IWOTH Ns \\*q +.Pq Li 0666 , +as modified by the process' +umask value (see +.Xr umask 2 ) . +.Pp +Files may only be read or written. +Seek operations are not allowed. +.Pp +The +.Fa bits +argument, if non-zero, is set to the bits code limit. +If zero, the default is 16. +See +.Xr compress 1 +for more information. +.Sh RETURN VALUES +Upon successful completion +.Fn zopen +returns a +.Tn FILE +pointer. +Otherwise, +.Dv NULL +is returned and the global variable +.Va errno +is set to indicate the error. +.Sh ERRORS +.Bl -tag -width [EINVAL] +.It Bq Er EINVAL +The +.Fa mode +or +.Fa bits +arguments specified to +.Fn zopen +were invalid. +.It Bq Er EFTYPE +The compressed file starts with an invalid header, or the compressed +file is compressed with more bits than can be handled. +.El +.Pp +The +.Fn zopen +function may also fail and set +.Va errno +for any of the errors specified for the routines +.Xr fopen 3 +or +.Xr funopen 3 . +.Sh SEE ALSO +.Xr compress 1 , +.Xr fopen 3 , +.Xr funopen 3 +.Sh HISTORY +The +.Nm +function +first appeared in +.Bx 4.4 . +.Sh BUGS +The +.Fn zopen +function +may not be portable to systems other than +.Bx . diff --git a/files/Sources/files/compress/zopen.c b/files/Sources/files/compress/zopen.c new file mode 100644 index 00000000..589ce525 --- /dev/null +++ b/files/Sources/files/compress/zopen.c @@ -0,0 +1,738 @@ +/*- + * Copyright (c) 1985, 1986, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis and James A. Woods, derived from original + * work by Spencer Thomas and Joseph Orost. + * + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)zopen.c 8.1 (Berkeley) 6/27/93"; +#endif /* LIBC_SCCS and not lint */ + +#include +__FBSDID("$FreeBSD: src/usr.bin/compress/zopen.c,v 1.17 2011/09/28 08:47:17 bz Exp $"); + +/*- + * fcompress.c - File compression ala IEEE Computer, June 1984. + * + * Compress authors: + * Spencer W. Thomas (decvax!utah-cs!thomas) + * Jim McKie (decvax!mcvax!jim) + * Steve Davies (decvax!vax135!petsd!peora!srd) + * Ken Turkowski (decvax!decwrl!turtlevax!ken) + * James A. Woods (decvax!ihnp4!ames!jaw) + * Joe Orost (decvax!vax135!petsd!joe) + * + * Cleaned up and converted to library returning I/O streams by + * Diomidis Spinellis . + * + * zopen(filename, mode, bits) + * Returns a FILE * that can be used for read or write. The modes + * supported are only "r" and "w". Seeking is not allowed. On + * reading the file is decompressed, on writing it is compressed. + * The output is compatible with compress(1) with 16 bit tables. + * Any file produced by compress(1) can be read. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "zopen.h" + +#define BITS 16 /* Default bits. */ +#define HSIZE 69001 /* 95% occupancy */ + +/* A code_int must be able to hold 2**BITS values of type int, and also -1. */ +typedef long code_int; +typedef long count_int; + +typedef u_char char_type; +static char_type magic_header[] = + {'\037', '\235'}; /* 1F 9D */ + +#define BIT_MASK 0x1f /* Defines for third byte of header. */ +#define BLOCK_MASK 0x80 + +/* + * Masks 0x40 and 0x20 are free. I think 0x20 should mean that there is + * a fourth header byte (for expansion). + */ +#define INIT_BITS 9 /* Initial number of bits/code. */ + +#define MAXCODE(n_bits) ((1 << (n_bits)) - 1) + +struct s_zstate { + FILE *zs_fp; /* File stream for I/O */ + char zs_mode; /* r or w */ + enum { + S_START, S_MIDDLE, S_EOF + } zs_state; /* State of computation */ + u_int zs_n_bits; /* Number of bits/code. */ + u_int zs_maxbits; /* User settable max # bits/code. */ + code_int zs_maxcode; /* Maximum code, given n_bits. */ + code_int zs_maxmaxcode; /* Should NEVER generate this code. */ + count_int zs_htab [HSIZE]; + u_short zs_codetab [HSIZE]; + code_int zs_hsize; /* For dynamic table sizing. */ + code_int zs_free_ent; /* First unused entry. */ + /* + * Block compression parameters -- after all codes are used up, + * and compression rate changes, start over. + */ + int zs_block_compress; + int zs_clear_flg; + long zs_ratio; + count_int zs_checkpoint; + u_int zs_offset; + long zs_in_count; /* Length of input. */ + long zs_bytes_out; /* Length of compressed output. */ + long zs_out_count; /* # of codes output (for debugging). */ + char_type zs_buf[BITS]; + union { + struct { + long zs_fcode; + code_int zs_ent; + code_int zs_hsize_reg; + int zs_hshift; + } w; /* Write parameters */ + struct { + char_type *zs_stackp; + int zs_finchar; + code_int zs_code, zs_oldcode, zs_incode; + int zs_roffset, zs_size; + char_type zs_gbuf[BITS]; + } r; /* Read parameters */ + } u; +}; + +/* Definitions to retain old variable names */ +#define fp zs->zs_fp +#define zmode zs->zs_mode +#define state zs->zs_state +#define n_bits zs->zs_n_bits +#define maxbits zs->zs_maxbits +#define maxcode zs->zs_maxcode +#define maxmaxcode zs->zs_maxmaxcode +#define htab zs->zs_htab +#define codetab zs->zs_codetab +#define hsize zs->zs_hsize +#define free_ent zs->zs_free_ent +#define block_compress zs->zs_block_compress +#define clear_flg zs->zs_clear_flg +#define ratio zs->zs_ratio +#define checkpoint zs->zs_checkpoint +#define offset zs->zs_offset +#define in_count zs->zs_in_count +#define bytes_out zs->zs_bytes_out +#define out_count zs->zs_out_count +#define buf zs->zs_buf +#define fcode zs->u.w.zs_fcode +#define hsize_reg zs->u.w.zs_hsize_reg +#define ent zs->u.w.zs_ent +#define hshift zs->u.w.zs_hshift +#define stackp zs->u.r.zs_stackp +#define finchar zs->u.r.zs_finchar +#define code zs->u.r.zs_code +#define oldcode zs->u.r.zs_oldcode +#define incode zs->u.r.zs_incode +#define roffset zs->u.r.zs_roffset +#define size zs->u.r.zs_size +#define gbuf zs->u.r.zs_gbuf + +/* + * To save much memory, we overlay the table used by compress() with those + * used by decompress(). The tab_prefix table is the same size and type as + * the codetab. The tab_suffix table needs 2**BITS characters. We get this + * from the beginning of htab. The output stack uses the rest of htab, and + * contains characters. There is plenty of room for any possible stack + * (stack used to be 8000 characters). + */ + +#define htabof(i) htab[i] +#define codetabof(i) codetab[i] + +#define tab_prefixof(i) codetabof(i) +#define tab_suffixof(i) ((char_type *)(htab))[i] +#define de_stack ((char_type *)&tab_suffixof(1 << BITS)) + +#define CHECK_GAP 10000 /* Ratio check interval. */ + +/* + * the next two codes should not be changed lightly, as they must not + * lie within the contiguous general code space. + */ +#define FIRST 257 /* First free entry. */ +#define CLEAR 256 /* Table clear output code. */ + +static int cl_block(struct s_zstate *); +static void cl_hash(struct s_zstate *, count_int); +static code_int getcode(struct s_zstate *); +static int output(struct s_zstate *, code_int); +static int zclose(void *); +static int zread(void *, char *, int); +static int zwrite(void *, const char *, int); + +/*- + * Algorithm from "A Technique for High Performance Data Compression", + * Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19. + * + * Algorithm: + * Modified Lempel-Ziv method (LZW). Basically finds common + * substrings and replaces them with a variable size code. This is + * deterministic, and can be done on the fly. Thus, the decompression + * procedure needs no input table, but tracks the way the table was built. + */ + +/*- + * compress write + * + * Algorithm: use open addressing double hashing (no chaining) on the + * prefix code / next character combination. We do a variant of Knuth's + * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime + * secondary probe. Here, the modular division first probe is gives way + * to a faster exclusive-or manipulation. Also do block compression with + * an adaptive reset, whereby the code table is cleared when the compression + * ratio decreases, but after the table fills. The variable-length output + * codes are re-sized at this point, and a special CLEAR code is generated + * for the decompressor. Late addition: construct the table according to + * file size for noticeable speed improvement on small files. Please direct + * questions about this implementation to ames!jaw. + */ +static int +zwrite(void *cookie, const char *wbp, int num) +{ + code_int i; + int c, disp; + struct s_zstate *zs; + const u_char *bp; + u_char tmp; + int count; + + if (num == 0) + return (0); + + zs = cookie; + count = num; + bp = (const u_char *)wbp; + if (state == S_MIDDLE) + goto middle; + state = S_MIDDLE; + + maxmaxcode = 1L << maxbits; + if (fwrite(magic_header, + sizeof(char), sizeof(magic_header), fp) != sizeof(magic_header)) + return (-1); + tmp = (u_char)((maxbits) | block_compress); + if (fwrite(&tmp, sizeof(char), sizeof(tmp), fp) != sizeof(tmp)) + return (-1); + + offset = 0; + bytes_out = 3; /* Includes 3-byte header mojo. */ + out_count = 0; + clear_flg = 0; + ratio = 0; + in_count = 1; + checkpoint = CHECK_GAP; + maxcode = MAXCODE(n_bits = INIT_BITS); + free_ent = ((block_compress) ? FIRST : 256); + + ent = *bp++; + --count; + + hshift = 0; + for (fcode = (long)hsize; fcode < 65536L; fcode *= 2L) + hshift++; + hshift = 8 - hshift; /* Set hash code range bound. */ + + hsize_reg = hsize; + cl_hash(zs, (count_int)hsize_reg); /* Clear hash table. */ + +middle: for (; count--;) { + c = *bp++; + in_count++; + fcode = (long)(((long)c << maxbits) + ent); + i = ((c << hshift) ^ ent); /* Xor hashing. */ + + if (htabof(i) == fcode) { + ent = codetabof(i); + continue; + } else if ((long)htabof(i) < 0) /* Empty slot. */ + goto nomatch; + disp = hsize_reg - i; /* Secondary hash (after G. Knott). */ + if (i == 0) + disp = 1; +probe: if ((i -= disp) < 0) + i += hsize_reg; + + if (htabof(i) == fcode) { + ent = codetabof(i); + continue; + } + if ((long)htabof(i) >= 0) + goto probe; +nomatch: if (output(zs, (code_int) ent) == -1) + return (-1); + out_count++; + ent = c; + if (free_ent < maxmaxcode) { + codetabof(i) = free_ent++; /* code -> hashtable */ + htabof(i) = fcode; + } else if ((count_int)in_count >= + checkpoint && block_compress) { + if (cl_block(zs) == -1) + return (-1); + } + } + return (num); +} + +static int +zclose(void *cookie) +{ + struct s_zstate *zs; + int rval; + + zs = cookie; + if (zmode == 'w') { /* Put out the final code. */ + if (output(zs, (code_int) ent) == -1) { + (void)fclose(fp); + free(zs); + return (-1); + } + out_count++; + if (output(zs, (code_int) - 1) == -1) { + (void)fclose(fp); + free(zs); + return (-1); + } + } + rval = fclose(fp) == EOF ? -1 : 0; + free(zs); + return (rval); +} + +/*- + * Output the given code. + * Inputs: + * code: A n_bits-bit integer. If == -1, then EOF. This assumes + * that n_bits =< (long)wordsize - 1. + * Outputs: + * Outputs code to the file. + * Assumptions: + * Chars are 8 bits long. + * Algorithm: + * Maintain a BITS character long buffer (so that 8 codes will + * fit in it exactly). Use the VAX insv instruction to insert each + * code in turn. When the buffer fills up empty it and start over. + */ + +static char_type lmask[9] = + {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00}; +static char_type rmask[9] = + {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; + +static int +output(struct s_zstate *zs, code_int ocode) +{ + int r_off; + u_int bits; + char_type *bp; + + r_off = offset; + bits = n_bits; + bp = buf; + if (ocode >= 0) { + /* Get to the first byte. */ + bp += (r_off >> 3); + r_off &= 7; + /* + * Since ocode is always >= 8 bits, only need to mask the first + * hunk on the left. + */ + *bp = (*bp & rmask[r_off]) | ((ocode << r_off) & lmask[r_off]); + bp++; + bits -= (8 - r_off); + ocode >>= 8 - r_off; + /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ + if (bits >= 8) { + *bp++ = ocode; + ocode >>= 8; + bits -= 8; + } + /* Last bits. */ + if (bits) + *bp = ocode; + offset += n_bits; + if (offset == (n_bits << 3)) { + bp = buf; + bits = n_bits; + bytes_out += bits; + if (fwrite(bp, sizeof(char), bits, fp) != bits) + return (-1); +// bp += bits; +// bits = 0; + offset = 0; + } + /* + * If the next entry is going to be too big for the ocode size, + * then increase it, if possible. + */ + if (free_ent > maxcode || (clear_flg > 0)) { + /* + * Write the whole buffer, because the input side won't + * discover the size increase until after it has read it. + */ + if (offset > 0) { + if (fwrite(buf, 1, n_bits, fp) != n_bits) + return (-1); + bytes_out += n_bits; + } + offset = 0; + + if (clear_flg) { + maxcode = MAXCODE(n_bits = INIT_BITS); + clear_flg = 0; + } else { + n_bits++; + if (n_bits == maxbits) + maxcode = maxmaxcode; + else + maxcode = MAXCODE(n_bits); + } + } + } else { + /* At EOF, write the rest of the buffer. */ + if (offset > 0) { + offset = (offset + 7) / 8; + if (fwrite(buf, 1, offset, fp) != offset) + return (-1); + bytes_out += offset; + } + offset = 0; + } + return (0); +} + +/* + * Decompress read. This routine adapts to the codes in the file building + * the "string" table on-the-fly; requiring no table to be stored in the + * compressed file. The tables used herein are shared with those of the + * compress() routine. See the definitions above. + */ +static int +zread(void *cookie, char *rbp, int num) +{ + u_int count; + struct s_zstate *zs; + u_char *bp, header[3]; + + if (num == 0) + return (0); + + zs = cookie; + count = num; + bp = (u_char *)rbp; + switch (state) { + case S_START: + state = S_MIDDLE; + break; + case S_MIDDLE: + goto middle; + case S_EOF: + goto eof; + } + + /* Check the magic number */ + if (fread(header, + sizeof(char), sizeof(header), fp) != sizeof(header) || + memcmp(header, magic_header, sizeof(magic_header)) != 0) { + errno = EFTYPE; + return (-1); + } + maxbits = header[2]; /* Set -b from file. */ + block_compress = maxbits & BLOCK_MASK; + maxbits &= BIT_MASK; + maxmaxcode = 1L << maxbits; + if (maxbits > BITS || maxbits < 12) { + errno = EFTYPE; + return (-1); + } + /* As above, initialize the first 256 entries in the table. */ + maxcode = MAXCODE(n_bits = INIT_BITS); + for (code = 255; code >= 0; code--) { + tab_prefixof(code) = 0; + tab_suffixof(code) = (char_type) code; + } + free_ent = block_compress ? FIRST : 256; + + finchar = oldcode = getcode(zs); + if (oldcode == -1) /* EOF already? */ + return (0); /* Get out of here */ + + /* First code must be 8 bits = char. */ + *bp++ = (u_char)finchar; + count--; + stackp = de_stack; + + while ((code = getcode(zs)) > -1) { + + if ((code == CLEAR) && block_compress) { + for (code = 255; code >= 0; code--) + tab_prefixof(code) = 0; + clear_flg = 1; + free_ent = FIRST; + oldcode = -1; + continue; + } + incode = code; + + /* Special case for kWkWk string. */ + if (code >= free_ent) { + if (code > free_ent || oldcode == -1) { + /* Bad stream. */ + errno = EINVAL; + return (-1); + } + *stackp++ = finchar; + code = oldcode; + } + /* + * The above condition ensures that code < free_ent. + * The construction of tab_prefixof in turn guarantees that + * each iteration decreases code and therefore stack usage is + * bound by 1 << BITS - 256. + */ + + /* Generate output characters in reverse order. */ + while (code >= 256) { + *stackp++ = tab_suffixof(code); + code = tab_prefixof(code); + } + *stackp++ = finchar = tab_suffixof(code); + + /* And put them out in forward order. */ +middle: do { + if (count-- == 0) + return (num); + *bp++ = *--stackp; + } while (stackp > de_stack); + + /* Generate the new entry. */ + if ((code = free_ent) < maxmaxcode && oldcode != -1) { + tab_prefixof(code) = (u_short) oldcode; + tab_suffixof(code) = finchar; + free_ent = code + 1; + } + + /* Remember previous code. */ + oldcode = incode; + } + state = S_EOF; +eof: return (num - count); +} + +/*- + * Read one code from the standard input. If EOF, return -1. + * Inputs: + * stdin + * Outputs: + * code or -1 is returned. + */ +static code_int +getcode(struct s_zstate *zs) +{ + code_int gcode; + int r_off, bits; + char_type *bp; + + bp = gbuf; + if (clear_flg > 0 || roffset >= size || free_ent > maxcode) { + /* + * If the next entry will be too big for the current gcode + * size, then we must increase the size. This implies reading + * a new buffer full, too. + */ + if (free_ent > maxcode) { + n_bits++; + if (n_bits == maxbits) /* Won't get any bigger now. */ + maxcode = maxmaxcode; + else + maxcode = MAXCODE(n_bits); + } + if (clear_flg > 0) { + maxcode = MAXCODE(n_bits = INIT_BITS); + clear_flg = 0; + } + size = fread(gbuf, 1, n_bits, fp); + if (size <= 0) /* End of file. */ + return (-1); + roffset = 0; + /* Round size down to integral number of codes. */ + size = (size << 3) - (n_bits - 1); + } + r_off = roffset; + bits = n_bits; + + /* Get to the first byte. */ + bp += (r_off >> 3); + r_off &= 7; + + /* Get first part (low order bits). */ + gcode = (*bp++ >> r_off); + bits -= (8 - r_off); + r_off = 8 - r_off; /* Now, roffset into gcode word. */ + + /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ + if (bits >= 8) { + gcode |= *bp++ << r_off; + r_off += 8; + bits -= 8; + } + + /* High order bits. */ + gcode |= (*bp & rmask[bits]) << r_off; + roffset += n_bits; + + return (gcode); +} + +static int +cl_block(struct s_zstate *zs) /* Table clear for block compress. */ +{ + long rat; + + checkpoint = in_count + CHECK_GAP; + + if (in_count > 0x007fffff) { /* Shift will overflow. */ + rat = bytes_out >> 8; + if (rat == 0) /* Don't divide by zero. */ + rat = 0x7fffffff; + else + rat = in_count / rat; + } else + rat = (in_count << 8) / bytes_out; /* 8 fractional bits. */ + if (rat > ratio) + ratio = rat; + else { + ratio = 0; + cl_hash(zs, (count_int) hsize); + free_ent = FIRST; + clear_flg = 1; + if (output(zs, (code_int) CLEAR) == -1) + return (-1); + } + return (0); +} + +static void +cl_hash(struct s_zstate *zs, count_int cl_hsize) /* Reset code table. */ +{ + count_int *htab_p; + long i, m1; + + m1 = -1; + htab_p = htab + cl_hsize; + i = cl_hsize - 16; + do { /* Might use Sys V memset(3) here. */ + *(htab_p - 16) = m1; + *(htab_p - 15) = m1; + *(htab_p - 14) = m1; + *(htab_p - 13) = m1; + *(htab_p - 12) = m1; + *(htab_p - 11) = m1; + *(htab_p - 10) = m1; + *(htab_p - 9) = m1; + *(htab_p - 8) = m1; + *(htab_p - 7) = m1; + *(htab_p - 6) = m1; + *(htab_p - 5) = m1; + *(htab_p - 4) = m1; + *(htab_p - 3) = m1; + *(htab_p - 2) = m1; + *(htab_p - 1) = m1; + htab_p -= 16; + } while ((i -= 16) >= 0); + for (i += 16; i > 0; i--) + *--htab_p = m1; +} + +FILE * +zopen(const char *fname, const char *mode, int bits) +{ + struct s_zstate *zs; + + if ((mode[0] != 'r' && mode[0] != 'w') || mode[1] != '\0' || + bits < 0 || bits > BITS) { + errno = EINVAL; + return (NULL); + } + + if ((zs = calloc(1, sizeof(struct s_zstate))) == NULL) + return (NULL); + + maxbits = bits ? bits : BITS; /* User settable max # bits/code. */ + maxmaxcode = 1L << maxbits; /* Should NEVER generate this code. */ + hsize = HSIZE; /* For dynamic table sizing. */ + free_ent = 0; /* First unused entry. */ + block_compress = BLOCK_MASK; + clear_flg = 0; + ratio = 0; + checkpoint = CHECK_GAP; + in_count = 1; /* Length of input. */ + out_count = 0; /* # of codes output (for debugging). */ + state = S_START; + roffset = 0; + size = 0; + + /* + * Layering compress on top of stdio in order to provide buffering, + * and ensure that reads and write work with the data specified. + */ + if ((fp = fopen(fname, mode)) == NULL) { + free(zs); + return (NULL); + } + switch (*mode) { + case 'r': + zmode = 'r'; + return (funopen(zs, zread, NULL, NULL, zclose)); + case 'w': + zmode = 'w'; + return (funopen(zs, NULL, zwrite, NULL, zclose)); + } + /* NOTREACHED */ + return (NULL); +} diff --git a/files/Sources/files/compress/zopen.h b/files/Sources/files/compress/zopen.h new file mode 100644 index 00000000..8ad5691f --- /dev/null +++ b/files/Sources/files/compress/zopen.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 1996 + * FreeBSD Inc. 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 FreeBSD Inc. 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 [your name] 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. + * + * $FreeBSD: src/usr.bin/compress/zopen.h,v 1.5 2002/03/22 01:19:31 imp Exp $ + */ + +#ifndef _ZOPEN_H_ +#define _ZOPEN_H_ + +FILE *zopen(const char *, const char *, int); + +#endif /* _ZOPEN_H_ */ diff --git a/files/Sources/files/cp/cp.1 b/files/Sources/files/cp/cp.1 new file mode 100644 index 00000000..8c346c7d --- /dev/null +++ b/files/Sources/files/cp/cp.1 @@ -0,0 +1,311 @@ +.\"- +.\" Copyright (c) 1989, 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" 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. +.\" +.\" @(#)cp.1 8.3 (Berkeley) 4/18/94 +.\" $FreeBSD: src/bin/cp/cp.1,v 1.33 2005/02/25 00:40:46 trhodes Exp $ +.\" +.Dd February 23, 2005 +.Dt CP 1 +.Os +.Sh NAME +.Nm cp +.Nd copy files +.Sh SYNOPSIS +.Nm cp +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Op Fl fi | n +.Op Fl apvX +.Ar source_file target_file +.Nm cp +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Op Fl fi | n +.Op Fl apvX +.Ar source_file ... target_directory +.Sh DESCRIPTION +In the first synopsis form, the +.Nm cp +utility copies the contents of the +.Ar source_file +to the +.Ar target_file . +In the second synopsis form, +the contents of each named +.Ar source_file +is copied to the destination +.Ar target_directory . +The names of the files themselves are not changed. +If +.Nm cp +detects an attempt to copy a file to itself, the copy will fail. +.Pp +The following options are available: +.Bl -tag -width flag +.It Fl a +Same as +.Fl pPR +options. Preserves structure and attributes of files +but not directory structure. +.It Fl f +.\"For each existing destination pathname, remove it and +If the destination file cannot be opened, remove it and +create a new file, without prompting for confirmation +regardless of its permissions. +(The +.Fl f +option overrides any previous +.Fl n +option.) +.Pp +The target file is not unlinked before the copy. +Thus, any existing access rights will be retained. +.It Fl H +If the +.Fl R +option is specified, symbolic links on the command line are followed. +(Symbolic links encountered in the tree traversal are not followed.) +.It Fl i +Cause +.Nm cp +to write a prompt to the standard error output before copying a file +that would overwrite an existing file. +If the response from the standard input begins with the character +.Sq Li y +or +.Sq Li Y , +the file copy is attempted. +(The +.Fl i +option overrides any previous +.Fl n +option.) +.It Fl L +If the +.Fl R +option is specified, all symbolic links are followed. +.It Fl n +Do not overwrite an existing file. +(The +.Fl n +option overrides any previous +.Fl f +or +.Fl i +options.) +.It Fl P +If the +.Fl R +option is specified, no symbolic links are followed. +This is the default. +.It Fl p +Cause +.Nm cp +to preserve the following attributes of each source +file in the copy: modification time, access time, +file flags, file mode, user ID, and group ID, as allowed by permissions. +Access Control Lists (ACLs) and Extended Attributes (EAs), +including resource forks, will also be preserved. +.Pp +If the user ID and group ID cannot be preserved, no error message +is displayed and the exit value is not altered. +.Pp +If the source file has its set-user-ID bit on and the user ID cannot +be preserved, the set-user-ID bit is not preserved +in the copy's permissions. +If the source file has its set-group-ID bit on and the group ID cannot +be preserved, the set-group-ID bit is not preserved +in the copy's permissions. +If the source file has both its set-user-ID and set-group-ID bits on, +and either the user ID or group ID cannot be preserved, neither +the set-user-ID nor set-group-ID bits are preserved in the copy's +permissions. +.It Fl R +If +.Ar source_file +designates a directory, +.Nm cp +copies the directory and the entire subtree connected at that point. +If the +.Ar source_file +ends in a +.Pa / , +the contents of the directory are copied rather than the +directory itself. +This option also causes symbolic links to be copied, rather than +indirected through, and for +.Nm cp +to create special files rather than copying them as normal files. +Created directories have the same mode as the corresponding source +directory, unmodified by the process' umask. +.Pp +In +.Fl R +mode, +.Nm cp +will continue copying even if errors are detected. +.Pp +Note that +.Nm cp +copies hard-linked files as separate files. +If you need to preserve hard links, consider using +.Xr tar 1 , +.Xr cpio 1 , +or +.Xr pax 1 +instead. +.It Fl v +Cause +.Nm cp +to be verbose, showing files as they are copied. +.It Fl X +Do not copy Extended Attributes (EAs) or resource forks. +.It Fl c +copy files using clonefile(2) +.El +.Pp +For each destination file that already exists, its contents are +overwritten if permissions allow. +Its mode, user ID, and group +ID are unchanged unless the +.Fl p +option was specified. +.Pp +In the second synopsis form, +.Ar target_directory +must exist unless there is only one named +.Ar source_file +which is a directory and the +.Fl R +flag is specified. +.Pp +If the destination file does not exist, the mode of the source file is +used as modified by the file mode creation mask +.Pf ( Ic umask , +see +.Xr csh 1 ) . +If the source file has its set-user-ID bit on, that bit is removed +unless both the source file and the destination file are owned by the +same user. +If the source file has its set-group-ID bit on, that bit is removed +unless both the source file and the destination file are in the same +group and the user is a member of that group. +If both the set-user-ID and set-group-ID bits are set, all of the above +conditions must be fulfilled or both bits are removed. +.Pp +Appropriate permissions are required for file creation or overwriting. +.Pp +Symbolic links are always followed unless the +.Fl R +flag is set, in which case symbolic links are not followed, by default. +The +.Fl H +or +.Fl L +flags (in conjunction with the +.Fl R +flag) cause symbolic links to be followed as described above. +The +.Fl H , +.Fl L +and +.Fl P +options are ignored unless the +.Fl R +option is specified. +In addition, these options override each other and the +command's actions are determined by the last one specified. +.Pp +If +.Nm cp +receives a +.Dv SIGINFO +(see the +.Cm status +argument for +.Xr stty 1 ) +signal, the current input and output file and the percentage complete +will be written to the standard output. +.Sh EXIT STATUS +.Ex -std +.Sh COMPATIBILITY +Historic versions of the +.Nm cp +utility had a +.Fl r +option. +This implementation supports that option; +however, its use is strongly discouraged, +as it does not correctly copy special files, symbolic links, or fifo's. +.Pp +The +.Fl v +and +.Fl n +options are non-standard and their use in scripts is not recommended. +.Sh LEGACY DESCRIPTION +In legacy mode, +.Fl f +will override +.Fl i . +Also, under the +.Fl f +option, the target file is always unlinked before the copy. +Thus, new access rights will always be set. +.Pp +In +.Fl R +mode, copying will terminate if an error is encountered. +.Pp +For more information about legacy mode, see +.Xr compat 5 . +.Sh SEE ALSO +.Xr mv 1 , +.Xr rcp 1 , +.Xr umask 2 , +.Xr fts 3 , +.Xr compat 5 , +.Xr symlink 7 +.Sh STANDARDS +The +.Nm cp +command is expected to be +.St -p1003.2 +compatible. +.Sh HISTORY +A +.Nm cp +command appeared in +.At v1 . diff --git a/files/Sources/files/cp/cp.c b/files/Sources/files/cp/cp.c new file mode 100644 index 00000000..3e9cce3f --- /dev/null +++ b/files/Sources/files/cp/cp.c @@ -0,0 +1,587 @@ +/*- + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * David Hitz of Auspex Systems Inc. + * + * 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. + */ + +#if 0 +#ifndef lint +static char const copyright[] = +"@(#) Copyright (c) 1988, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)cp.c 8.2 (Berkeley) 4/1/94"; +#endif /* not lint */ +#endif +#include +__FBSDID("$FreeBSD: src/bin/cp/cp.c,v 1.52 2005/09/05 04:36:08 csjp Exp $"); + +/* + * Cp copies source files to target files. + * + * The global PATH_T structure "to" always contains the path to the + * current target file. Since fts(3) does not change directories, + * this path can be either absolute or dot-relative. + * + * The basic algorithm is to initialize "to" and use fts(3) to traverse + * the file hierarchy rooted in the argument list. A trivial case is the + * case of 'cp file1 file2'. The more interesting case is the case of + * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the + * path (relative to the root of the traversal) is appended to dir (stored + * in "to") to form the final target path. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +// #include +// #else /* !__APPLE__ */ +#define COMPAT_MODE(a,b) (1) +#include "ios_error.h" +#endif /* __APPLE__ */ + +#include "extern.h" + +#define STRIP_TRAILING_SLASH(p) { \ + while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \ + *--(p).p_end = 0; \ +} + +static char emptystring[] = ""; + +PATH_T to = { to.p_path, emptystring, "" }; + +__thread int cp_fflag, cp_iflag, cp_nflag, cp_pflag, cp_vflag; +#ifdef __APPLE__ +__thread int Xflag; +#endif /* __APPLE__ */ +static int Rflag, rflag; +__thread int cp_cflag = 0; +volatile __thread sig_atomic_t info; + +enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE }; + +static int copy(char *[], enum op, int); +static void siginfo(int __unused); + +int +cp_main(int argc, char *argv[]) +{ + struct stat to_stat, tmp_stat; + enum op type; + int Hflag, Lflag, Pflag, ch, fts_options, r, have_trailing_slash; + char *target; + + Hflag = Lflag = Pflag = 0; + cp_fflag = cp_iflag = cp_nflag = cp_pflag = cp_vflag = 0; +#ifdef __APPLE__ + Xflag = 0; +#endif /* __APPLE__ */ + Rflag = rflag = cp_cflag = 0; + optind = 1; opterr = 1; optreset = 1; + + while ((ch = getopt(argc, argv, "cHLPRXafinprv")) != -1) + switch (ch) { + case 'c': + cp_cflag = 1; + break; + case 'H': + Hflag = 1; + Lflag = Pflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = Pflag = 0; + break; + case 'P': + Pflag = 1; + Hflag = Lflag = 0; + break; + case 'R': + Rflag = 1; + break; + case 'X': + Xflag = 1; + break; + case 'f': + cp_fflag = 1; + /* Determine if the STD is SUSv3 or Legacy */ + if (COMPAT_MODE("bin/cp", "unix2003")) + cp_nflag = 0; /* reset cp_nflag, but not cp_iflag */ + else + cp_iflag = cp_nflag = 0; /* reset both */ + break; + case 'i': + cp_iflag = 1; + if (COMPAT_MODE("bin/cp", "unix2003")) + cp_nflag = 0; /* reset cp_nflag, but not cp_fflag */ + else + cp_fflag = cp_nflag = 0; + break; + case 'n': + cp_nflag = 1; + cp_fflag = cp_iflag = 0; + break; + case 'p': + cp_pflag = 1; + break; + case 'r': + rflag = 1; + break; + case 'v': + cp_vflag = 1; + break; + case 'a': + cp_pflag = 1; + Pflag = 1; + Rflag = 1; + break; + default: + cp_usage(); + break; + } + argc -= optind; + argv += optind; + + if (argc < 2) + cp_usage(); + + if (cp_cflag && Xflag) { + errx(1, "the -c and -X options may not be specified together"); + } + + fts_options = FTS_NOCHDIR | FTS_PHYSICAL; + if (rflag) { + if (Rflag) { + errx(1, + "cp: the -R and -r options may not be specified together.\n"); + } + if (Hflag || Lflag || Pflag) { + errx(1, + "cp: the -H, -L, and -P options may not be specified with the -r option.\n"); + } + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + if (Rflag) { + if (Hflag) + fts_options |= FTS_COMFOLLOW; + if (Lflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + } else { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL | FTS_COMFOLLOW; + } + (void)signal(SIGINFO, siginfo); + + /* Save the target base in "to". */ + target = argv[--argc]; + if (strlcpy(to.p_path, target, sizeof(to.p_path)) >= sizeof(to.p_path)) { + errx(1, "%s: name too long", target); + } + to.p_end = to.p_path + strlen(to.p_path); + if (to.p_path == to.p_end) { + *to.p_end++ = '.'; + *to.p_end = 0; + } + have_trailing_slash = (to.p_end[-1] == '/'); + if (have_trailing_slash) + STRIP_TRAILING_SLASH(to); + to.target_end = to.p_end; + + /* Set end of argument list for fts(3). */ + argv[argc] = NULL; + + /* + * Cp has two distinct cases: + * + * cp [-R] source target + * cp [-R] source1 ... sourceN directory + * + * In both cases, source can be either a file or a directory. + * + * In (1), the target becomes a copy of the source. That is, if the + * source is a file, the target will be a file, and likewise for + * directories. + * + * In (2), the real target is not directory, but "directory/source". + */ + r = stat(to.p_path, &to_stat); + if (r == -1 && errno != ENOENT) { + err(1, "%s", to.p_path); + } + if (r == -1 || !S_ISDIR(to_stat.st_mode)) { + /* + * Case (1). Target is not a directory. + */ + if (argc > 1) { + cp_usage(); + exit(1); + } + /* + * Need to detect the case: + * cp -R dir foo + * Where dir is a directory and foo does not exist, where + * we want pathname concatenations turned on but not for + * the initial mkdir(). + */ + if (r == -1) { + if (rflag || (Rflag && (Lflag || Hflag))) + stat(*argv, &tmp_stat); + else + lstat(*argv, &tmp_stat); + + if (S_ISDIR(tmp_stat.st_mode) && (Rflag || rflag)) + type = DIR_TO_DNE; + else + type = FILE_TO_FILE; + } else + type = FILE_TO_FILE; + + if (have_trailing_slash && type == FILE_TO_FILE) { + if (r == -1) { + errx(1, "directory %s does not exist", + to.p_path); + } + else { + errx(1, "%s is not a directory", to.p_path); + } + } + } else + /* + * Case (2). Target is a directory. + */ + type = FILE_TO_DIR; + + return (copy(argv, type, fts_options)); +} + +static int +copy(char *argv[], enum op type, int fts_options) +{ + struct stat to_stat; + FTS *ftsp; + FTSENT *curr; + int base = 0, dne, badcp, rval; + size_t nlen; + char *p, *target_mid; + mode_t mask, mode; + + /* + * Keep an inverted copy of the umask, for use in correcting + * permissions on created directories when not using -p. + */ + mask = ~umask(0777); + umask(~mask); + + if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL) { + err(1, "fts_open"); + } + for (badcp = rval = 0; (curr = fts_read(ftsp)) != NULL; badcp = 0) { + switch (curr->fts_info) { + case FTS_NS: + case FTS_DNR: + case FTS_ERR: + warnx("%s: %s", + curr->fts_path, strerror(curr->fts_errno)); + rval = 1; + continue; + case FTS_DC: /* Warn, continue. */ + warnx("%s: directory causes a cycle", curr->fts_path); + rval = 1; + continue; + default: + ; + } +#ifdef __APPLE__ + +#ifdef __clang__ +#pragma clang diagnostic push +/* clang doesn't like fts_name[1], but we know better... */ +#pragma clang diagnostic ignored "-Warray-bounds" +#endif + /* Skip ._ when using copyfile and exists */ + if ((cp_pflag || !Xflag) && (curr->fts_level != FTS_ROOTLEVEL) && + (curr->fts_namelen > 2) && /* ._\0 is not AppleDouble */ + (curr->fts_name[0] == '.') && (curr->fts_name[1] == '_')) { +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + struct stat statbuf; + char path[PATH_MAX]; + char *p = strrchr(curr->fts_path, '/'); + if (p) { + size_t s = p + 2 - curr->fts_path; + if (s > sizeof(path)) s = sizeof(path); + strlcpy(path, curr->fts_path, s); + strlcat(path, curr->fts_name+2, sizeof(path)); + } else { + strlcpy(path, curr->fts_name+2, sizeof(path)); + } + if (!lstat(path, &statbuf)) { + continue; + } + } +#endif /* __APPLE__ */ + /* + * If we are in case (2) or (3) above, we need to append the + * source name to the target name. + */ + if (type != FILE_TO_FILE) { + /* + * Need to remember the roots of traversals to create + * correct pathnames. If there's a directory being + * copied to a non-existent directory, e.g. + * cp -R a/dir noexist + * the resulting path name should be noexist/foo, not + * noexist/dir/foo (where foo is a file in dir), which + * is the case where the target exists. + * + * Also, check for "..". This is for correct path + * concatenation for paths ending in "..", e.g. + * cp -R .. /tmp + * Paths ending in ".." are changed to ".". This is + * tricky, but seems the easiest way to fix the problem. + * + * XXX + * Since the first level MUST be FTS_ROOTLEVEL, base + * is always initialized. + */ + if (curr->fts_level == FTS_ROOTLEVEL) { + if (type != DIR_TO_DNE) { + p = strrchr(curr->fts_path, '/'); + base = (p == NULL) ? 0 : + (int)(p - curr->fts_path + 1); + + if (!strcmp(&curr->fts_path[base], + "..")) + base += 1; + } else + base = curr->fts_pathlen; + } + + p = &curr->fts_path[base]; + nlen = curr->fts_pathlen - base; + target_mid = to.target_end; + if (*p != '/' && target_mid[-1] != '/') + *target_mid++ = '/'; + *target_mid = 0; + if (target_mid - to.p_path + nlen >= PATH_MAX) { + warnx("%s%s: name too long (not copied)", + to.p_path, p); + rval = 1; + continue; + } + (void)strncat(target_mid, p, nlen); + to.p_end = target_mid + nlen; + *to.p_end = 0; + STRIP_TRAILING_SLASH(to); + } + + if (curr->fts_info == FTS_DP) { + /* + * We are nearly finished with this directory. If we + * didn't actually copy it, or otherwise don't need to + * change its attributes, then we are done. + */ + if (!curr->fts_number) + continue; + /* + * If -p is in effect, set all the attributes. + * Otherwise, set the correct permissions, limited + * by the umask. Optimise by avoiding a chmod() + * if possible (which is usually the case if we + * made the directory). Note that mkdir() does not + * honour setuid, setgid and sticky bits, but we + * normally want to preserve them on directories. + */ + if (cp_pflag) { + if (setfile(curr->fts_statp, -1)) + rval = 1; +#ifdef __APPLE__ + /* setfile will fail if writeattr is denied */ + if (copyfile(curr->fts_path, to.p_path, NULL, COPYFILE_ACL)<0) + warn("%s: unable to copy ACL to %s", curr->fts_path, to.p_path); +#else /* !__APPLE__ */ + if (preserve_dir_acls(curr->fts_statp, + curr->fts_accpath, to.p_path) != 0) + rval = 1; +#endif /* __APPLE__ */ + } else { + mode = curr->fts_statp->st_mode; + if ((mode & (S_ISUID | S_ISGID | S_ISTXT)) || + ((mode | S_IRWXU) & mask) != (mode & mask)) + if (chmod(to.p_path, mode & mask) != 0){ + warn("chmod: %s", to.p_path); + rval = 1; + } + } + continue; + } + + /* Not an error but need to remember it happened */ + if (stat(to.p_path, &to_stat) == -1) + dne = 1; + else { + if (to_stat.st_dev == curr->fts_statp->st_dev && + to_stat.st_ino == curr->fts_statp->st_ino) { + warnx("%s and %s are identical (not copied).", + to.p_path, curr->fts_path); + rval = 1; + if (S_ISDIR(curr->fts_statp->st_mode)) + (void)fts_set(ftsp, curr, FTS_SKIP); + continue; + } + if (!S_ISDIR(curr->fts_statp->st_mode) && + S_ISDIR(to_stat.st_mode)) { + warnx("cannot overwrite directory %s with " + "non-directory %s\n", + to.p_path, curr->fts_path); + rval = 1; + continue; + } + dne = 0; + } + + switch (curr->fts_statp->st_mode & S_IFMT) { + case S_IFLNK: + /* Catch special case of a non-dangling symlink */ + if ((fts_options & FTS_LOGICAL) || + ((fts_options & FTS_COMFOLLOW) && + curr->fts_level == 0)) { + if (copy_file(curr, dne)) + badcp = rval = 1; + } else { + if (copy_link(curr, !dne)) + badcp = rval = 1; + } + break; + case S_IFDIR: + if (!Rflag && !rflag) { + warnx("%s is a directory (not copied).", + curr->fts_path); + (void)fts_set(ftsp, curr, FTS_SKIP); + badcp = rval = 1; + break; + } + /* + * If the directory doesn't exist, create the new + * one with the from file mode plus owner RWX bits, + * modified by the umask. Trade-off between being + * able to write the directory (if from directory is + * 555) and not causing a permissions race. If the + * umask blocks owner writes, we fail.. + */ + if (dne) { + if (mkdir(to.p_path, + curr->fts_statp->st_mode | S_IRWXU) < 0) { + if (COMPAT_MODE("bin/cp", "unix2003")) { + warn("%s", to.p_path); + } else { + err(1, "%s", to.p_path); + } + } + } else if (!S_ISDIR(to_stat.st_mode)) { + errno = ENOTDIR; + if (COMPAT_MODE("bin/cp", "unix2003")) { + warn("%s", to.p_path); + } else { + err(1, "%s", to.p_path); + } + } + /* + * Arrange to correct directory attributes later + * (in the post-order phase) if this is a new + * directory, or if the -p flag is in effect. + */ + curr->fts_number = cp_pflag || dne; +#ifdef __APPLE__ + if (!Xflag) { + if (copyfile(curr->fts_path, to.p_path, NULL, COPYFILE_XATTR) < 0) + warn("%s: unable to copy extended attributes to %s", curr->fts_path, to.p_path); + /* ACL and mtime set in postorder traversal */ + } +#endif /* __APPLE__ */ + break; + case S_IFBLK: + case S_IFCHR: + if (Rflag) { + if (copy_special(curr->fts_statp, !dne)) + badcp = rval = 1; + } else { + if (copy_file(curr, dne)) + badcp = rval = 1; + } + break; + case S_IFIFO: + if (Rflag) { + if (copy_fifo(curr->fts_statp, !dne)) + badcp = rval = 1; + } else { + if (copy_file(curr, dne)) + badcp = rval = 1; + } + break; + default: + if (copy_file(curr, dne)) + badcp = rval = 1; + break; + } + if (cp_vflag && !badcp) + (void)fprintf(thread_stdout, "%s -> %s\n", curr->fts_path, to.p_path); + } + fts_close(ftsp); + if (errno) { + err(1, "fts_read"); + } + return (rval); +} + +static void +siginfo(int sig __unused) +{ + + info = 1; +} diff --git a/files/Sources/files/cp/extern.h b/files/Sources/files/cp/extern.h new file mode 100644 index 00000000..4e399de0 --- /dev/null +++ b/files/Sources/files/cp/extern.h @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 1991, 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. + * + * @(#)extern.h 8.2 (Berkeley) 4/1/94 + * $FreeBSD: src/bin/cp/extern.h,v 1.20 2005/09/05 04:36:08 csjp Exp $ + */ + +typedef struct { + char *p_end; /* pointer to NULL at end of path */ + char *target_end; /* pointer to end of target base */ + char p_path[PATH_MAX]; /* pointer to the start of a path */ +} PATH_T; + +extern PATH_T to; +extern __thread int cp_fflag, cp_iflag, cp_nflag, cp_pflag, cp_vflag; +#ifdef __APPLE__ +extern __thread int Xflag; +#endif /* __APPLE__ */ +extern __thread int cp_cflag; +extern volatile __thread sig_atomic_t info; + +__BEGIN_DECLS +int copy_fifo(struct stat *, int); +int copy_file(const FTSENT *, int); +int copy_link(const FTSENT *, int); +int copy_special(struct stat *, int); +int setfile(struct stat *, int); +int preserve_dir_acls(struct stat *, char *, char *); +int preserve_fd_acls(int, int); +void cp_usage(void); +__END_DECLS diff --git a/files/Sources/files/cp/utils.c b/files/Sources/files/cp/utils.c new file mode 100644 index 00000000..090724aa --- /dev/null +++ b/files/Sources/files/cp/utils.c @@ -0,0 +1,534 @@ +/*- + * Copyright (c) 1991, 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 +#if 0 +static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: src/bin/cp/utils.c,v 1.46 2005/09/05 04:36:08 csjp Exp $"); + +#include +#include +#include +#include +#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#include +#include +#include +// #include +#include +#include +// #else +#define COMPAT_MODE(a,b) (1) +#endif /* __APPLE__ */ + +#include "extern.h" +#include "ios_error.h" +#define cp_pct(x,y) (int)(100.0 * (double)(x) / (double)(y)) + +int +copy_file(const FTSENT *entp, int dne) +{ + static char buf[MAXBSIZE]; + struct stat *fs; + int ch, checkch, from_fd, rval, to_fd; + ssize_t rcount; + ssize_t wcount; + size_t wresid; + off_t wtotal; + char *bufp; +#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED + char *p; +#endif + mode_t mode = 0; + struct stat to_stat; + + if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { + warn("%s", entp->fts_path); + return (1); + } + + fs = entp->fts_statp; + + /* + * If the file exists and we're interactive, verify with the user. + * If the file DNE, set the mode to be the from file, minus setuid + * bits, modified by the umask; arguably wrong, but it makes copying + * executables work right and it's been that way forever. (The + * other choice is 666 or'ed with the execute bits on the from file + * modified by the umask.) + */ + if (!dne) { +#define YESNO "(y/n [n]) " + if (cp_nflag) { + if (cp_vflag) + fprintf(thread_stdout, "%s not overwritten\n", to.p_path); + (void)close(from_fd); + return (1); + } else if (cp_iflag) { + (void)fprintf(thread_stderr, "overwrite %s? %s", + to.p_path, YESNO); + fflush(thread_stderr); + checkch = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + if (checkch != 'y' && checkch != 'Y') { + (void)close(from_fd); + (void)fprintf(thread_stderr, "not overwritten\n"); + fflush(thread_stderr); + return (1); + } + } + + if (cp_cflag) { + (void)unlink(to.p_path); + int error = clonefile(entp->fts_path, to.p_path, 0); + if (error) + warn("%s: clonefile failed", to.p_path); + (void)close(from_fd); + return error == 0 ? 0 : 1; + } + + if (COMPAT_MODE("bin/cp", "unix2003")) { + /* first try to overwrite existing destination file name */ + to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); + if (to_fd == -1) { + if (cp_fflag) { + /* Only if it fails remove file and create a new one */ + (void)unlink(to.p_path); + to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, + fs->st_mode & ~(S_ISUID | S_ISGID)); + } + } + } else { + if (cp_fflag) { + /* remove existing destination file name, + * create a new file */ + (void)unlink(to.p_path); + to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, + fs->st_mode & ~(S_ISUID | S_ISGID)); + } else + /* overwrite existing destination file name */ + to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); + } + } else { + + if (cp_cflag) { + int error = clonefile(entp->fts_path, to.p_path, 0); + if (error) + warn("%s: clonefile failed", to.p_path); + (void)close(from_fd); + return error == 0 ? 0 : 1; + } + + to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, + fs->st_mode & ~(S_ISUID | S_ISGID)); + } + + if (to_fd == -1) { + warn("%s", to.p_path); + (void)close(from_fd); + return (1); + } + + rval = 0; + +#ifdef __APPLE__ + if (S_ISREG(fs->st_mode)) { + struct statfs sfs; + + /* + * Pre-allocate blocks for the destination file if it + * resides on Xsan. + */ + if (fstatfs(to_fd, &sfs) == 0 && + strcmp(sfs.f_fstypename, "acfs") == 0) { + fstore_t fst; + + fst.fst_flags = 0; + fst.fst_posmode = F_PEOFPOSMODE; + fst.fst_offset = 0; + fst.fst_length = fs->st_size; + + (void) fcntl(to_fd, F_PREALLOCATE, &fst); + } + } +#endif /* __APPLE__ */ + + if (fstat(to_fd, &to_stat) != -1) { + mode = to_stat.st_mode; + if ((mode & (S_IRWXG|S_IRWXO)) + && fchmod(to_fd, mode & ~(S_IRWXG|S_IRWXO))) { + if (errno != EPERM) /* we have write access but do not own the file */ + warn("%s: fchmod failed", to.p_path); + mode = 0; + } + } else { + warn("%s", to.p_path); + } + /* + * Mmap and write if less than 8M (the limit is so we don't totally + * trash memory on big files. This is really a minor hack, but it + * wins some CPU back. + */ +#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED + if (S_ISREG(fs->st_mode) && fs->st_size > 0 && + fs->st_size <= 8 * 1048576) { + if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ, + MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) { + warn("%s", entp->fts_path); + rval = 1; + } else { + wtotal = 0; + for (bufp = p, wresid = fs->st_size; ; + bufp += wcount, wresid -= (size_t)wcount) { + wcount = write(to_fd, bufp, wresid); + wtotal += wcount; + if (info) { + info = 0; + (void)fprintf(thread_stderr, + "%s -> %s %3d%%\n", + entp->fts_path, to.p_path, + cp_pct(wtotal, fs->st_size)); + + } + if (wcount >= (ssize_t)wresid || wcount <= 0) + break; + } + if (wcount != (ssize_t)wresid) { + warn("%s", to.p_path); + rval = 1; + } + /* Some systems don't unmap on close(2). */ + if (munmap(p, fs->st_size) < 0) { + warn("%s", entp->fts_path); + rval = 1; + } + } + } else +#endif + { + wtotal = 0; + while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { + for (bufp = buf, wresid = rcount; ; + bufp += wcount, wresid -= wcount) { + wcount = write(to_fd, bufp, wresid); + wtotal += wcount; + if (info) { + info = 0; + (void)fprintf(thread_stderr, + "%s -> %s %3d%%\n", + entp->fts_path, to.p_path, + cp_pct(wtotal, fs->st_size)); + + } + if (wcount >= (ssize_t)wresid || wcount <= 0) + break; + } + if (wcount != (ssize_t)wresid) { + warn("%s", to.p_path); + rval = 1; + break; + } + } + if (rcount < 0) { + warn("%s", entp->fts_path); + rval = 1; + } + } + + /* + * Don't remove the target even after an error. The target might + * not be a regular file, or its attributes might be important, + * or its contents might be irreplaceable. It would only be safe + * to remove it if we created it and its length is 0. + */ + if (mode != 0) + if (fchmod(to_fd, mode)) + warn("%s: fchmod failed", to.p_path); +#ifdef __APPLE__ + /* do these before setfile in case copyfile changes mtime */ + if (!Xflag && S_ISREG(fs->st_mode)) { /* skip devices, etc */ + if (fcopyfile(from_fd, to_fd, NULL, COPYFILE_XATTR) < 0) + warn("%s: could not copy extended attributes to %s", entp->fts_path, to.p_path); + } + if (cp_pflag && setfile(fs, to_fd)) + rval = 1; + if (cp_pflag) { + /* If this ACL denies writeattr then setfile will fail... */ + if (fcopyfile(from_fd, to_fd, NULL, COPYFILE_ACL) < 0) + warn("%s: could not copy ACL to %s", entp->fts_path, to.p_path); + } +#else /* !__APPLE__ */ + if (cp_pflag && setfile(fs, to_fd)) + rval = 1; + if (cp_pflag && preserve_fd_acls(from_fd, to_fd) != 0) + rval = 1; +#endif /* __APPLE__ */ + (void)close(from_fd); + if (close(to_fd)) { + warn("%s", to.p_path); + rval = 1; + } + return (rval); +} + +int +copy_link(const FTSENT *p, int exists) +{ + ssize_t len; + char llink[PATH_MAX]; + + if ((len = readlink(p->fts_path, llink, sizeof(llink) - 1)) == -1) { + warn("readlink: %s", p->fts_path); + return (1); + } + llink[len] = '\0'; + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (symlink(llink, to.p_path)) { + warn("symlink: %s", llink); + return (1); + } +#ifdef __APPLE__ + if (!Xflag) + if (copyfile(p->fts_path, to.p_path, NULL, COPYFILE_XATTR | COPYFILE_NOFOLLOW_SRC) <0) + warn("%s: could not copy extended attributes to %s", + p->fts_path, to.p_path, strerror(errno)); +#endif + return (cp_pflag ? setfile(p->fts_statp, -1) : 0); +} + +int +copy_fifo(struct stat *from_stat, int exists) +{ + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (mkfifo(to.p_path, from_stat->st_mode)) { + warn("mkfifo: %s", to.p_path); + return (1); + } + return (cp_pflag ? setfile(from_stat, -1) : 0); +} + +int +copy_special(struct stat *from_stat, int exists) +{ + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { + warn("mknod: %s", to.p_path); + return (1); + } + return (cp_pflag ? setfile(from_stat, -1) : 0); +} + +int +setfile(struct stat *fs, int fd) +{ + static struct timeval tv[2]; + struct stat ts; + int rval, gotstat, islink, fdval; + + rval = 0; + fdval = fd != -1; + islink = !fdval && S_ISLNK(fs->st_mode); + fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO; + + TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); + TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); + if (fdval ? futimes(fd, tv) : (islink ? lutimes(to.p_path, tv) : utimes(to.p_path, tv))) { + warn("%sutimes: %s", fdval ? "f" : (islink ? "l" : ""), to.p_path); + rval = 1; + } + if (fdval ? fstat(fd, &ts) : (islink ? lstat(to.p_path, &ts) : + stat(to.p_path, &ts))) { + gotstat = 0; + } else { + gotstat = 1; + ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO; + } + /* + * Changing the ownership probably won't succeed, unless we're root + * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting + * the mode; current BSD behavior is to remove all setuid bits on + * chown. If chown fails, lose setuid/setgid bits. + */ + if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid) { + if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) : (islink ? + lchown(to.p_path, fs->st_uid, fs->st_gid) : + chown(to.p_path, fs->st_uid, fs->st_gid))) { + if (errno != EPERM) { + warn("%schown: %s", fdval ? "f" : (islink ? "l" : ""), to.p_path); + rval = 1; + } + fs->st_mode &= ~(S_ISUID | S_ISGID); + } + } + + if (!gotstat || fs->st_mode != ts.st_mode) { + if (fdval ? fchmod(fd, fs->st_mode) : (islink ? + lchmod(to.p_path, fs->st_mode) : + chmod(to.p_path, fs->st_mode))) { + warn("%schmod: %s", fdval ? "f" : (islink ? "l" : ""), to.p_path); + rval = 1; + } + } + + if (!gotstat || fs->st_flags != ts.st_flags) { + if (fdval ? fchflags(fd, fs->st_flags) : (islink ? + lchflags(to.p_path, fs->st_flags) : + chflags(to.p_path, fs->st_flags))) { + if (errno != EPERM) { + warn("%schflags: %s", fdval ? "f" : (islink ? "l" : ""), to.p_path); + rval = 1; + } + } + } + return (rval); +} + +#ifndef __APPLE__ +int +preserve_fd_acls(int source_fd, int dest_fd) +{ + struct acl *aclp; + acl_t acl; + + if (fpathconf(source_fd, _PC_ACL_EXTENDED) != 1 || + fpathconf(dest_fd, _PC_ACL_EXTENDED) != 1) + return (0); + acl = acl_get_fd(source_fd); + if (acl == NULL) { + warn("failed to get acl entries while setting %s", to.p_path); + return (1); + } + aclp = &acl->ats_acl; + if (aclp->acl_cnt == 3) + return (0); + if (acl_set_fd(dest_fd, acl) < 0) { + warn("failed to set acl entries for %s", to.p_path); + return (1); + } + return (0); +} + +int +preserve_dir_acls(struct stat *fs, char *source_dir, char *dest_dir) +{ + acl_t (*aclgetf)(const char *, acl_type_t); + int (*aclsetf)(const char *, acl_type_t, acl_t); + struct acl *aclp; + acl_t acl; + + if (pathconf(source_dir, _PC_ACL_EXTENDED) != 1 || + pathconf(dest_dir, _PC_ACL_EXTENDED) != 1) + return (0); + /* + * If the file is a link we will not follow it + */ + if (S_ISLNK(fs->st_mode)) { + aclgetf = acl_get_link_np; + aclsetf = acl_set_link_np; + } else { + aclgetf = acl_get_file; + aclsetf = acl_set_file; + } + /* + * Even if there is no ACL_TYPE_DEFAULT entry here, a zero + * size ACL will be returned. So it is not safe to simply + * check the pointer to see if the default ACL is present. + */ + acl = aclgetf(source_dir, ACL_TYPE_DEFAULT); + if (acl == NULL) { + warn("failed to get default acl entries on %s", + source_dir, strerror(errno)); + return (1); + } + aclp = &acl->ats_acl; + if (aclp->acl_cnt != 0 && aclsetf(dest_dir, + ACL_TYPE_DEFAULT, acl) < 0) { + warn("failed to set default acl entries on %s", + dest_dir, strerror(errno)); + return (1); + } + acl = aclgetf(source_dir, ACL_TYPE_ACCESS); + if (acl == NULL) { + warn("failed to get acl entries on %s", source_dir); + return (1); + } + aclp = &acl->ats_acl; + if (aclsetf(dest_dir, ACL_TYPE_ACCESS, acl) < 0) { + warn("failed to set acl entries on %s", dest_dir); + return (1); + } + return (0); +} +#endif /* !__APPLE__ */ + +void +cp_usage(void) +{ + + if (COMPAT_MODE("bin/cp", "unix2003")) { + (void)fprintf(thread_stderr, "%s\n%s\n", +"usage: cp [-R [-H | -L | -P]] [-fi | -n] [-apvXc] source_file target_file", +" cp [-R [-H | -L | -P]] [-fi | -n] [-apvXc] source_file ... " +"target_directory"); + } else { + (void)fprintf(thread_stderr, "%s\n%s\n", +"usage: cp [-R [-H | -L | -P]] [-f | -i | -n] [-apvXc] source_file target_file", +" cp [-R [-H | -L | -P]] [-f | -i | -n] [-apvXc] source_file ... " +"target_directory"); + } + exit(EX_USAGE); +} diff --git a/files/Sources/files/df/df.1 b/files/Sources/files/df/df.1 new file mode 100644 index 00000000..4560ab95 --- /dev/null +++ b/files/Sources/files/df/df.1 @@ -0,0 +1,217 @@ +.\" Copyright (c) 1989, 1990, 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 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. +.\" +.\" @(#)df.1 8.3 (Berkeley) 5/8/95 +.\" $FreeBSD: src/bin/df/df.1,v 1.18.2.7 2002/04/22 22:17:36 keramida Exp $ +.\" +.Dd May 8, 1995 +.Dt DF 1 +.Os +.Sh NAME +.Nm df +.Nd display free disk space +.Sh SYNOPSIS +.Nm df +.Oo +.Fl b | h | H | k | +.Fl m | g | P +.Oc +.Op Fl ailn +.Op Fl t +.Op Fl T Ar type +.Op Ar file | filesystem ... +.Sh LEGACY SYNOPSIS +.Nm df +.Oo +.Fl b | h | H | k | +.Fl m | P +.Oc +.Op Fl ailn +.Op Fl t Ar type +.Op Fl T Ar type +.Op Ar file | filesystem ... +.Sh DESCRIPTION +The +.Nm df +utility +displays statistics about the amount of free disk space on the specified +.Ar filesystem +or on the filesystem of which +.Ar file +is a part. +Values are displayed in 512-byte per block counts. +If neither a file or a filesystem operand is specified, +statistics for all mounted filesystems are displayed +(subject to the +.Fl t +option below). +.Pp +The following options are available: +.Bl -tag -width Ds +.It Fl a +Show all mount points, including those that were mounted with the MNT_IGNORE +flag. +.It Fl b +Use (the default) 512-byte blocks. +This is only useful as a way to override an +.Ev BLOCKSIZE +specification from the environment. +.It Fl g +Use 1073741824-byte (1-Gbyte) blocks rather than the default. +Note that this overrides the +.Ev BLOCKSIZE +specification from the environment. +.It Fl H +"Human-readable" output. Use unit suffixes: Byte, Kilobyte, Megabyte, +Gigabyte, Terabyte and Petabyte in order to reduce the number of +digits to three or less using base 10 for sizes. +.It Fl h +"Human-readable" output. Use unit suffixes: Byte, Kilobyte, Megabyte, +Gigabyte, Terabyte and Petabyte in order to reduce the number of +digits to three or less using base 2 for sizes. +.It Fl i +Include statistics on the number of free inodes. This option is now the default to conform to +.St -susv3 +Use +.Fl P +to suppress this output. +.It Fl k +Use 1024-byte (1-Kbyte) blocks, rather than the default. +Note that this overrides the +.Ev BLOCKSIZE +specification from the environment. +.It Fl l +Only display information about locally-mounted filesystems. +.It Fl m +Use 1048576-byte (1-Mbyte) blocks rather than the default. Note that +this overrides the +.Ev BLOCKSIZE +specification from the environment. +.It Fl n +Print out the previously obtained statistics from the filesystems. +This option should be used if it is possible that one or more +filesystems are in a state such that they will not be able to provide +statistics without a long delay. +When this option is specified, +.Nm df +will not request new statistics from the filesystems, but will respond +with the possibly stale statistics that were previously obtained. +.It Fl P +Use (the default) 512-byte blocks. +This is only useful as a way to override an +.Ev BLOCKSIZE +specification from the environment. +.It Fl T +Only print out statistics for filesystems of the specified types. +More than one type may be specified in a comma separated list. +The list of filesystem types can be prefixed with +.Dq no +to specify the filesystem types for which action should +.Em not +be taken. +For example, the +.Nm df +command: +.Bd -literal -offset indent +df -T nonfs,mfs +.Ed +.Pp +lists all filesystems except those of type +.Tn NFS +and +.Tn MFS . +The +.Xr lsvfs 1 +command can be used to find out the types of filesystems +that are available on the system. +.It Fl t +If used with no arguments, +this option is a no-op +(Mac OS X already prints the total allocated-space figures). +If used with an argument, it acts like +.Fl T , +but this usage is deprecated and should not be relied upon. +.El +.Sh ENVIRONMENT +.Bl -tag -width BLOCKSIZE +.It Ev BLOCKSIZE +If the environment variable +.Ev BLOCKSIZE +is set, the block counts will be displayed in units of that size block. +.El +.Sh BUGS +The +.Fl n +and +.Fl t +flags are ignored if a file or filesystem is specified. +.Sh LEGACY DESCRIPTION +The "capacity" percentage is normally rounded up to the next higher integer. +In legacy mode, it is rounded down to the next lower integer. +.Pp +When the +.Fl P +option and the +.Fl k +option are used together, +sizes are reported in 1024-blocks. +In legacy mode, when the +.Fl P +option and +.Fl k +option are used together, +the last option specified dictates the reported block size. +.Pp +The +.Fl t +option is normally a no-op +(Mac OS X already prints the total allocated-space figures). +In legacy mode, it is equivalent to +.Fl T . +.Pp +For more information about legacy mode, see +.Xr compat 5 . +.Sh SEE ALSO +.Xr lsvfs 1 , +.Xr quota 1 , +.Xr fstatfs 2 , +.Xr getfsstat 2 , +.Xr statfs 2 , +.Xr getmntinfo 3 , +.Xr compat 5 , +.Xr fstab 5 , +.Xr mount 8 , +.Xr quot 8 +.Sh HISTORY +A +.Nm df +command appeared in +.At v1 . diff --git a/files/Sources/files/df/df.c b/files/Sources/files/df/df.c new file mode 100644 index 00000000..649c1b73 --- /dev/null +++ b/files/Sources/files/df/df.c @@ -0,0 +1,650 @@ +/* + * Copyright (c) 1980, 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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 +#ifndef lint +__used static const char copyright[] = +"@(#) Copyright (c) 1980, 1990, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)df.c 8.9 (Berkeley) 5/8/95"; +#else +__used static const char rcsid[] = + "$FreeBSD: src/bin/df/df.c,v 1.23.2.9 2002/07/01 00:14:24 iedowse Exp $"; +#endif +#endif /* not lint */ + +#ifdef __APPLE__ +#define MNT_IGNORE 0 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libutil.h" +#include "ios_error.h" + +#ifdef __APPLE__ +// #include "get_compat.h" +// #else +#define COMPAT_MODE(func, mode) 1 +#endif + +#define UNITS_SI 1 +#define UNITS_2 2 + +#define KILO_SZ(n) (n) +#define MEGA_SZ(n) ((n) * (n)) +#define GIGA_SZ(n) ((n) * (n) * (n)) +#define TERA_SZ(n) ((n) * (n) * (n) * (n)) +#define PETA_SZ(n) ((n) * (n) * (n) * (n) * (n)) + +#define KILO_2_SZ (KILO_SZ(1024ULL)) +#define MEGA_2_SZ (MEGA_SZ(1024ULL)) +#define GIGA_2_SZ (GIGA_SZ(1024ULL)) +#define TERA_2_SZ (TERA_SZ(1024ULL)) +#define PETA_2_SZ (PETA_SZ(1024ULL)) + +#define KILO_SI_SZ (KILO_SZ(1000ULL)) +#define MEGA_SI_SZ (MEGA_SZ(1000ULL)) +#define GIGA_SI_SZ (GIGA_SZ(1000ULL)) +#define TERA_SI_SZ (TERA_SZ(1000ULL)) +#define PETA_SI_SZ (PETA_SZ(1000ULL)) + +/* Maximum widths of various fields. */ +struct maxwidths { + int mntfrom; + int total; + int used; + int avail; + int iused; + int ifree; +}; + +static unsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ}; +static unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ}; +static unsigned long long *valp; + +typedef enum { NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX } unit_t; + +static unit_t unitp [] = { NONE, KILO, MEGA, GIGA, TERA, PETA }; + +static int bread(off_t, void *, int); +int checkvfsname(const char *, char **); +static char *getmntpt(char *); +static int int64width(int64_t); +static char *makenetvfslist(void); +char **makevfslist(const char *); +static void prthuman(struct statfs *, uint64_t); +static void prthumanval(int64_t); +static void prtstat(struct statfs *, struct maxwidths *); +static long regetmntinfo(struct statfs **, long, char **); +static unit_t unit_adjust(double *); +static void update_maxwidths(struct maxwidths *, struct statfs *); +static void usage(void); + +static int aflag = 0, hflag, iflag, nflag; +static int headerlen, timesthrough; + +static __inline int imax(int a, int b) +{ + return (a > b ? a : b); +} + +int +df_main(int argc, char *argv[]) +{ + struct stat stbuf; + struct statfs statfsbuf, *mntbuf; + struct maxwidths maxwidths; + char *mntpt, **vfslist; + long mntsize; + int ch, i, rv, tflag = 0, kludge_tflag = 0; + int kflag = 0; + // always init the flags: + aflag = hflag = iflag = nflag = 0; + headerlen = 0; timesthrough = 0; + optind = 1; opterr = 1; optreset = 1; + + const char *options = "abgHhiklmnPt:T:"; + if (COMPAT_MODE("bin/df", "unix2003")) { + /* Unix2003 requires -t be "include total capacity". which df + already does, but it conflicts with the old -t so we need to + *not* expect a string after -t (we provide -T in both cases + to cover the old use of -t) */ + options = "abgHhiklmnPtT:"; + // iflag = 1; + iflag = 0; + } + + vfslist = NULL; + while ((ch = getopt(argc, argv, options)) != -1) + switch (ch) { + case 'a': + aflag = 1; + break; + case 'b': + /* FALLTHROUGH */ + case 'P': + if (COMPAT_MODE("bin/df", "unix2003")) { + if (!kflag) { + /* -k overrides -P */ + putenv("BLOCKSIZE=512"); + } + iflag = 0; + } else { + putenv("BLOCKSIZE=512"); + } + hflag = 0; + break; + case 'g': + putenv("BLOCKSIZE=1g"); + hflag = 0; + break; + case 'H': + hflag = UNITS_SI; + valp = vals_si; + break; + case 'h': + hflag = UNITS_2; + valp = vals_base2; + break; + case 'i': + iflag = 1; + break; + case 'k': + if (COMPAT_MODE("bin/df", "unix2003")) { + putenv("BLOCKSIZE=1024"); + } else { + putenv("BLOCKSIZE=1k"); + } + kflag = 1; + hflag = 0; + break; + case 'l': + if (tflag) { + errx(1, "-l and -T are mutually exclusive."); + } + if (vfslist != NULL) + break; + vfslist = makevfslist(makenetvfslist()); + break; + case 'm': + putenv("BLOCKSIZE=1m"); + hflag = 0; + break; + case 'n': + nflag = 1; + break; + case 't': + /* Unix2003 uses -t for something we do by default */ + if (COMPAT_MODE("bin/df", "unix2003")) { + kludge_tflag = 1; + break; + } + case 'T': + if (vfslist != NULL) { + if (tflag) { + errx(1, "only one -%c option may be specified", ch); + } + else { + errx(1, "-l and -%c are mutually exclusive.", ch); + } + } + tflag++; + vfslist = makevfslist(optarg); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* If we are in unix2003 mode, have seen a -t but no -T and the first + non switch arg isn't a file, let's pretend they used -T on it. + This makes the Lexmark printer installer happy (PR-3918471) */ + if (tflag == 0 && kludge_tflag && *argv && stat(*argv, &stbuf) < 0 + && errno == ENOENT) { + vfslist = makevfslist(*argv++); + } + + mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); + bzero(&maxwidths, sizeof(maxwidths)); + for (i = 0; i < mntsize; i++) + update_maxwidths(&maxwidths, &mntbuf[i]); + + rv = 0; + if (!*argv) { + mntsize = regetmntinfo(&mntbuf, mntsize, vfslist); + bzero(&maxwidths, sizeof(maxwidths)); + for (i = 0; i < mntsize; i++) + update_maxwidths(&maxwidths, &mntbuf[i]); + for (i = 0; i < mntsize; i++) { + if (aflag || (mntbuf[i].f_flags & MNT_IGNORE) == 0) + prtstat(&mntbuf[i], &maxwidths); + } + exit(rv); + } + + for (; *argv; argv++) { + if (stat(*argv, &stbuf) < 0) { + if ((mntpt = getmntpt(*argv)) == 0) { + warn("%s", *argv); + rv = 1; + continue; + } + } else if (S_ISCHR(stbuf.st_mode) || S_ISBLK(stbuf.st_mode)) { + warnx("%s: Raw devices not supported", *argv); + rv = 1; + continue; + } else + mntpt = *argv; + /* + * Statfs does not take a `wait' flag, so we cannot + * implement nflag here. + */ + if (statfs(mntpt, &statfsbuf) < 0) { + warn("%s", mntpt); + rv = 1; + continue; + } + /* Check to make sure the arguments we've been + * given are satisfied. Return an error if we + * have been asked to list a mount point that does + * not match the other args we've been given (-l, -t, etc.) + */ + if (checkvfsname(statfsbuf.f_fstypename, vfslist)) { + rv++; + continue; + } + + if (argc == 1) { + bzero(&maxwidths, sizeof(maxwidths)); + update_maxwidths(&maxwidths, &statfsbuf); + } + prtstat(&statfsbuf, &maxwidths); + } + return (rv); +} + +char * +getmntpt(char *name) +{ + long mntsize, i; + struct statfs *mntbuf; + + mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); + for (i = 0; i < mntsize; i++) { + if (!strcmp(mntbuf[i].f_mntfromname, name)) + return (mntbuf[i].f_mntonname); + } + return (0); +} + +/* + * Make a pass over the filesystem info in ``mntbuf'' filtering out + * filesystem types not in vfslist and possibly re-stating to get + * current (not cached) info. Returns the new count of valid statfs bufs. + */ +long +regetmntinfo(struct statfs **mntbufp, long mntsize, char **vfslist) +{ + int i, j; + struct statfs *mntbuf; + + if (vfslist == NULL) + return (nflag ? mntsize : getmntinfo(mntbufp, MNT_WAIT)); + + mntbuf = *mntbufp; + for (j = 0, i = 0; i < mntsize; i++) { + if (checkvfsname(mntbuf[i].f_fstypename, vfslist)) + continue; + if (!nflag) + (void)statfs(mntbuf[i].f_mntonname,&mntbuf[j]); + else if (i != j) + mntbuf[j] = mntbuf[i]; + j++; + } + return (j); +} + +/* + * Output in "human-readable" format. Uses 3 digits max and puts + * unit suffixes at the end. Makes output compact and easy to read, + * especially on huge disks. + * + */ +unit_t +unit_adjust(double *val) +{ + double abval; + unit_t unit; + unsigned int unit_sz; + + abval = fabs(*val); + + unit_sz = abval ? ilogb(abval) / 10 : 0; + + if (unit_sz >= UNIT_MAX) { + unit = NONE; + } else { + unit = unitp[unit_sz]; + *val /= (double)valp[unit_sz]; + } + + return (unit); +} + +void +prthuman(struct statfs *sfsp, uint64_t used) +{ + int64_t value; + + value = sfsp->f_blocks; + value *= sfsp->f_bsize; + prthumanval(value); + value = used; + value *= sfsp->f_bsize; + prthumanval(value); + value = sfsp->f_bavail; + value *= sfsp->f_bsize; + prthumanval(value); +} + +void +prthumanval(int64_t bytes) +{ + char buf[6]; + int flags; + + flags = HN_B | HN_NOSPACE | HN_DECIMAL; + if (hflag == UNITS_SI) + flags |= HN_DIVISOR_1000; + + humanize_number(buf, sizeof(buf) - (bytes < 0 ? 0 : 1), + bytes, "", HN_AUTOSCALE, flags); + + if (hflag == UNITS_SI) + (void)fprintf(thread_stdout, " %6s", buf); + else + (void)fprintf(thread_stdout, "%6si", buf); + +} + +/* + * Convert statfs returned filesystem size into BLOCKSIZE units. + * Attempts to avoid overflow for large filesystems. + */ +static intmax_t fsbtoblk(int64_t num, uint64_t fsbs, u_long bs, char *fs) +{ + if (num < 0) { + warnx("negative filesystem block count/size from fs %s", fs); + return 0; + } else if ((fsbs != 0) && (fsbs < bs)) { + return (num / (intmax_t) (bs / fsbs)); + } else { + return (num * (intmax_t) (fsbs / bs)); + } +} + +/* + * Print out status about a filesystem. + */ +void +prtstat(struct statfs *sfsp, struct maxwidths *mwp) +{ + static long blocksize; + // static int headerlen, timesthrough; + static const char *header; + uint64_t used, availblks, inodes; + char * avail_str; + + if (++timesthrough == 1) { + mwp->mntfrom = imax(mwp->mntfrom, (int)strlen("Filesystem")); + if (hflag) { + header = " Size"; + mwp->total = mwp->used = mwp->avail = (int)strlen(header); + } else { + header = getbsize(&headerlen, &blocksize); + mwp->total = imax(mwp->total, headerlen); + } + mwp->used = imax(mwp->used, (int)strlen("Used")); + if (COMPAT_MODE("bin/df", "unix2003") && !hflag) { + avail_str = "Available"; + } else { + avail_str = "Avail"; + } + mwp->avail = imax(mwp->avail, (int)strlen(avail_str)); + + (void)fprintf(thread_stdout, "%-*s %*s %*s %*s Capacity", mwp->mntfrom, + "Filesystem", mwp->total, header, mwp->used, "Used", + mwp->avail, avail_str); + if (iflag) { + mwp->iused = imax(mwp->iused, (int)strlen(" iused")); + mwp->ifree = imax(mwp->ifree, (int)strlen("ifree")); + (void)fprintf(thread_stdout, " %*s %*s %%iused", mwp->iused - 2, + "iused", mwp->ifree, "ifree"); + } + (void)fprintf(thread_stdout, " Mounted on\n"); + } + + (void)fprintf(thread_stdout, "%-*s", mwp->mntfrom, sfsp->f_mntfromname); + if (sfsp->f_blocks > sfsp->f_bfree) + used = sfsp->f_blocks - sfsp->f_bfree; + else + used = 0; + availblks = sfsp->f_bavail + used; + if (hflag) { + prthuman(sfsp, used); + } else { + (void)fprintf(thread_stdout, " %*jd %*jd %*jd", mwp->total, + fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize, sfsp->f_mntonname), + mwp->used, fsbtoblk(used, sfsp->f_bsize, blocksize, sfsp->f_mntonname), + mwp->avail, fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, blocksize, sfsp->f_mntonname)); + } + if (COMPAT_MODE("bin/df", "unix2003")) { + /* Standard says percentage must be rounded UP to next + integer value, not truncated */ + double value; + if (availblks == 0) + value = 100.0; + else { + value = (double)used / (double)availblks * 100.0; + if ((value-(int)value) > 0.0) value = value + 1.0; + } + (void)fprintf(thread_stdout, " %5.0f%%", trunc(value)); + } else { + (void)fprintf(thread_stdout, " %5.0f%%", + availblks == 0 ? 100.0 : (double)used / (double)availblks * 100.0); + } + if (iflag) { + inodes = sfsp->f_files; + used = inodes - sfsp->f_ffree; + (void)fprintf(thread_stdout, " %*llu %*llu %4.0f%% ", mwp->iused, used, + mwp->ifree, sfsp->f_ffree, inodes == 0 ? 100.0 : + (double)used / (double)inodes * 100.0); + } else + (void)fprintf(thread_stdout, " "); + (void)fprintf(thread_stdout, " %s\n", sfsp->f_mntonname); +} + +/* + * Update the maximum field-width information in `mwp' based on + * the filesystem specified by `sfsp'. + */ +void +update_maxwidths(struct maxwidths *mwp, struct statfs *sfsp) +{ + static long blocksize; + int dummy; + + if (blocksize == 0) + getbsize(&dummy, &blocksize); + + mwp->mntfrom = imax(mwp->mntfrom, (int)strlen(sfsp->f_mntfromname)); + mwp->total = imax(mwp->total, int64width(fsbtoblk(sfsp->f_blocks, + sfsp->f_bsize, blocksize, sfsp->f_mntonname))); + if (sfsp->f_blocks >= sfsp->f_bfree) + mwp->used = imax(mwp->used, int64width(fsbtoblk(sfsp->f_blocks - + sfsp->f_bfree, sfsp->f_bsize, blocksize, sfsp->f_mntonname))); + mwp->avail = imax(mwp->avail, int64width(fsbtoblk(sfsp->f_bavail, + sfsp->f_bsize, blocksize, sfsp->f_mntonname))); + mwp->iused = imax(mwp->iused, int64width(sfsp->f_files - sfsp->f_ffree)); + mwp->ifree = imax(mwp->ifree, int64width(sfsp->f_ffree)); +} + +/* Return the width in characters of the specified long. */ +int +int64width(int64_t val) +{ + int len; + + len = 0; + /* Negative or zero values require one extra digit. */ + if (val <= 0) { + val = -val; + len++; + } + while (val > 0) { + len++; + val /= 10; + } + + return (len); +} + +void +usage(void) +{ + + char *t_flag = COMPAT_MODE("bin/df", "unix2003") ? "[-t]" : "[-t type]"; + (void)fprintf(thread_stderr, + "usage: df [-b | -H | -h | -k | -m | -g | -P] [-ailn] [-T type] %s [filesystem ...]\n", t_flag); + exit(EX_USAGE); +} + +char * +makenetvfslist(void) +{ + char *str, *strptr, **listptr; +#ifndef __APPLE__ + int mib[3], maxvfsconf, cnt=0, i; + size_t miblen; + struct ovfsconf *ptr; +#else + int mib[4], maxvfsconf, cnt=0, i; + size_t miblen; + struct vfsconf vfc; +#endif + + mib[0] = CTL_VFS; mib[1] = VFS_GENERIC; mib[2] = VFS_MAXTYPENUM; + miblen=sizeof(maxvfsconf); + if (sysctl(mib, 3, + &maxvfsconf, &miblen, NULL, 0)) { + warn("sysctl failed"); + return (NULL); + } + + if ((listptr = malloc(sizeof(char*) * maxvfsconf)) == NULL) { + warnx("malloc failed"); + return (NULL); + } + +#ifndef __APPLE__ + for (ptr = getvfsent(); ptr; ptr = getvfsent()) + if (ptr->vfc_flags & VFCF_NETWORK) { + listptr[cnt++] = strdup(ptr->vfc_name); + if (listptr[cnt-1] == NULL) { + warnx("malloc failed"); + return (NULL); + } + } +#else + miblen = sizeof (struct vfsconf); + mib[2] = VFS_CONF; + for (i = 0; i < maxvfsconf; i++) { + mib[3] = i; + if (sysctl(mib, 4, &vfc, &miblen, NULL, 0) == 0) { + if (!(vfc.vfc_flags & MNT_LOCAL)) { + listptr[cnt++] = strdup(vfc.vfc_name); + if (listptr[cnt-1] == NULL) { + free(listptr); + warnx("malloc failed"); + return (NULL); + } + } + } + } +#endif + + if (cnt == 0 || + (str = malloc(sizeof(char) * (32 * cnt + cnt + 2))) == NULL) { + if (cnt > 0) + warnx("malloc failed"); + free(listptr); + return (NULL); + } + + *str = 'n'; *(str + 1) = 'o'; + for (i = 0, strptr = str + 2; i < cnt; i++, strptr++) { + strncpy(strptr, listptr[i], 32); + strptr += strlen(listptr[i]); + *strptr = ','; + free(listptr[i]); + } + *(--strptr) = '\0'; + + free(listptr); + return (str); +} diff --git a/files/Sources/files/df/vfslist.c b/files/Sources/files/df/vfslist.c new file mode 100644 index 00000000..e21eb32d --- /dev/null +++ b/files/Sources/files/df/vfslist.c @@ -0,0 +1,106 @@ +/*- + * Copyright (c) 1995 + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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 +#ifndef lint +#if 0 +static char sccsid[] = "@(#)vfslist.c 8.1 (Berkeley) 5/8/95"; +#endif +__used static const char rcsid[] = + "$FreeBSD: src/sbin/mount/vfslist.c,v 1.4 1999/08/28 00:13:27 peter Exp $"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include "ios_error.h" + +#ifndef __APPLE__ +#include "extern.h" +#endif + +static int skipvfs; + +int checkvfsname(const char *, const char **); +const char **makevfslist(const char *); + +int +checkvfsname(vfsname, vfslist) + const char *vfsname; + const char **vfslist; +{ + + if (vfslist == NULL) + return (0); + while (*vfslist != NULL) { + if (strcmp(vfsname, *vfslist) == 0) + return (skipvfs); + ++vfslist; + } + return (!skipvfs); +} + +const char ** +makevfslist(fslist) + const char *fslist; +{ + const char **av; + int i; + const char *cnextcp; + char *nextcp; + + if (fslist == NULL) + return (NULL); + if (fslist[0] == 'n' && fslist[1] == 'o') { + fslist += 2; + skipvfs = 1; + } + for (i = 0, cnextcp = fslist; *cnextcp; cnextcp++) + if (*cnextcp == ',') + i++; + if ((av = malloc((size_t)(i + 2) * sizeof(char *))) == NULL) { + warnx("malloc failed"); + return (NULL); + } + nextcp = strdup(fslist); + i = 0; + av[i++] = nextcp; + while ((nextcp = strchr(nextcp, ',')) != NULL) { + *nextcp++ = '\0'; + av[i++] = nextcp; + } + av[i++] = NULL; + return (av); +} diff --git a/files/Sources/files/du/du.1 b/files/Sources/files/du/du.1 new file mode 100644 index 00000000..1bc6b2b6 --- /dev/null +++ b/files/Sources/files/du/du.1 @@ -0,0 +1,172 @@ +.\" Copyright (c) 1990, 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 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. +.\" +.\" @(#)du.1 8.2 (Berkeley) 4/1/94 +.\" $FreeBSD: src/usr.bin/du/du.1,v 1.30 2005/05/21 09:55:05 ru Exp $ +.\" +.Dd June 2, 2004 +.Dt DU 1 +.Os +.Sh NAME +.Nm du +.Nd display disk usage statistics +.Sh SYNOPSIS +.Nm du +.Op Fl H | L | P +.Op Fl a | s | d Ar depth +.Op Fl c +.Op Fl h | k | m | g +.Op Fl x +.Op Fl I Ar mask +.Op Ar +.Sh DESCRIPTION +The +.Nm du +utility displays the file system block usage for each file argument +and for each directory in the file hierarchy rooted in each directory +argument. +If no file is specified, the block usage of the hierarchy rooted in +the current directory is displayed. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl a +Display an entry for each file in a file hierarchy. +.It Fl c +Display a grand total. +.It Fl d Ar depth +Display an entry for all files and directories +.Ar depth +directories deep. +.It Fl H +Symbolic links on the command line are followed, symbolic links in file +hierarchies are not followed. +.It Fl h +"Human-readable" output. +Use unit suffixes: Byte, Kilobyte, Megabyte, +Gigabyte, Terabyte and Petabyte. +.It Fl I Ar mask +Ignore files and directories matching the specified +.Ar mask . +.It Fl g +Display block counts in 1073741824-byte (1-Gbyte) blocks. +.It Fl k +Display block counts in 1024-byte (1-Kbyte) blocks. +.It Fl L +Symbolic links on the command line and in file hierarchies are followed. +.It Fl m +Display block counts in 1048576-byte (1-Mbyte) blocks. +.It Fl P +No symbolic links are followed. +This is the default. +.It Fl r +Generate messages about directories that cannot be read, files +that cannot be opened, and so on. +This is the default case. +This option exists solely for conformance with +.St -xpg4 . +.It Fl s +Display an entry for each specified file. +(Equivalent to +.Fl d Li 0 ) +.It Fl x +File system mount points are not traversed. +.El +.Pp +The +.Nm du +utility counts the storage used by symbolic links and not the files they +reference unless the +.Fl H +or +.Fl L +option is specified. +If either the +.Fl H +or +.Fl L +options are specified, storage used by any symbolic links which are +followed is not counted or displayed. +If more than one of the +.Fl H , +.Fl L , +and +.Fl P +options is specified, the last one given is used. +.Pp +Files having multiple hard links are counted (and displayed) a single +time per +.Nm du +execution. +Directories having multiple hard links (typically Time Machine backups) are +counted a single time per +.Nm du +execution. +.Sh ENVIRONMENT +.Bl -tag -width BLOCKSIZE +.It Ev BLOCKSIZE +If the environment variable +.Ev BLOCKSIZE +is set, and the +.Fl k +option is not specified, the block counts will be displayed in units of that +size block. +If +.Ev BLOCKSIZE +is not set, and the +.Fl k +option is not specified, the block counts will be displayed in 512-byte blocks. +.El +.Sh LEGACY DESCRIPTION +In legacy mode, only one of the +.Fl H , +.Fl L , +or +.Fl P +options may be specified. +.Pp +The command will detect and report a SYMLOOP error +(loop involving symbolic links). +In legacy mode, this is not the case. +.Pp +For more information about legacy mode, see +.Xr compat 5 . +.Sh SEE ALSO +.Xr df 1 , +.Xr fts 3 , +.Xr compat 5 , +.Xr symlink 7 , +.Xr quot 8 +.Sh HISTORY +A +.Nm du +command appeared in +.At v1 . diff --git a/files/Sources/files/du/du.c b/files/Sources/files/du/du.c new file mode 100644 index 00000000..8ecdd7a7 --- /dev/null +++ b/files/Sources/files/du/du.c @@ -0,0 +1,751 @@ +/* + * 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 + * Chris Newcomb. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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 +#ifndef lint +__used static const char copyright[] = +"@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static const char sccsid[] = "@(#)du.c 8.5 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: src/usr.bin/du/du.c,v 1.38 2005/04/09 14:31:40 stefanf Exp $"); + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ios_error.h" + +#ifdef __APPLE__ +// #include +// #else +#define COMPAT_MODE(func, mode) (1) +#endif + +#define KILO_SZ(n) (n) +#define MEGA_SZ(n) ((n) * (n)) +#define GIGA_SZ(n) ((n) * (n) * (n)) +#define TERA_SZ(n) ((n) * (n) * (n) * (n)) +#define PETA_SZ(n) ((n) * (n) * (n) * (n) * (n)) + +#define KILO_2_SZ (KILO_SZ(1024ULL)) +#define MEGA_2_SZ (MEGA_SZ(1024ULL)) +#define GIGA_2_SZ (GIGA_SZ(1024ULL)) +#define TERA_2_SZ (TERA_SZ(1024ULL)) +#define PETA_2_SZ (PETA_SZ(1024ULL)) + +#define KILO_SI_SZ (KILO_SZ(1000ULL)) +#define MEGA_SI_SZ (MEGA_SZ(1000ULL)) +#define GIGA_SI_SZ (GIGA_SZ(1000ULL)) +#define TERA_SI_SZ (TERA_SZ(1000ULL)) +#define PETA_SI_SZ (PETA_SZ(1000ULL)) + +#define TWO_TB (2LL * 1024LL * 1024LL * 1024LL * 1024LL) + +static unsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ}; +static unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ}; +static unsigned long long *valp; + +typedef enum { NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX } unit_t; + +static int unitp [] = { NONE, KILO, MEGA, GIGA, TERA, PETA }; + +SLIST_HEAD(ignhead, ignentry) ignores; +struct ignentry { + char *mask; + SLIST_ENTRY(ignentry) next; +}; + +static int linkchk(FTSENT *); +static int dirlinkchk(FTSENT *); +static void usage(void); +static void prthumanval(double); +static unit_t unit_adjust(double *); +static void ignoreadd(const char *); +static void ignoreclean(void); +static int ignorep(FTSENT *); + +int +du_main(int argc, char *argv[]) +{ + FTS *fts; + FTSENT *p; + off_t savednumber = 0; + long blocksize; + int ftsoptions; + int listall; + int depth; + int Hflag, Lflag, Pflag, aflag, sflag, dflag, cflag, hflag, ch, notused, rval; + char **save; + static char dot[] = "."; + off_t *ftsnum, *ftsparnum; + + setlocale(LC_ALL, ""); + + Hflag = Lflag = Pflag = aflag = sflag = dflag = cflag = hflag = 0; + optind = 1; opterr = 1; optreset = 1; + + save = argv; + ftsoptions = FTS_NOCHDIR; + depth = INT_MAX; + SLIST_INIT(&ignores); + + while ((ch = getopt(argc, argv, "HI:LPasd:cghkmrx")) != -1) + switch (ch) { + case 'H': + Lflag = Pflag = 0; + Hflag = 1; + break; + case 'I': + ignoreadd(optarg); + break; + case 'L': + Hflag = Pflag = 0; + Lflag = 1; + break; + case 'P': + Hflag = Lflag = 0; + Pflag = 1; + break; + case 'a': + aflag = 1; + break; + case 's': + sflag = 1; + break; + case 'd': + dflag = 1; + errno = 0; + depth = atoi(optarg); + if (errno == ERANGE || depth < 0) { + warnx("invalid argument to option d: %s", optarg); + usage(); + } + break; + case 'c': + cflag = 1; + break; + case 'h': + putenv("BLOCKSIZE=512"); + hflag = 1; + valp = vals_base2; + break; + case 'k': + hflag = 0; + putenv("BLOCKSIZE=1024"); + break; + case 'm': + hflag = 0; + putenv("BLOCKSIZE=1048576"); + break; + case 'g': + hflag = 0; + putenv("BLOCKSIZE=1g"); + break; + case 'r': /* Compatibility. */ + break; + case 'x': + ftsoptions |= FTS_XDEV; + break; + case '?': + default: + usage(); + } + +// argc -= optind; + argv += optind; + + /* + * XXX + * Because of the way that fts(3) works, logical walks will not count + * the blocks actually used by symbolic links. We rationalize this by + * noting that users computing logical sizes are likely to do logical + * copies, so not counting the links is correct. The real reason is + * that we'd have to re-implement the kernel's symbolic link traversing + * algorithm to get this right. If, for example, you have relative + * symbolic links referencing other relative symbolic links, it gets + * very nasty, very fast. The bottom line is that it's documented in + * the man page, so it's a feature. + */ + + if (Hflag + Lflag + Pflag > 1) + usage(); + + if (Hflag + Lflag + Pflag == 0) + Pflag = 1; /* -P (physical) is default */ + + if (Hflag) + ftsoptions |= FTS_COMFOLLOW; + + if (Lflag) + ftsoptions |= FTS_LOGICAL; + + if (Pflag) + ftsoptions |= FTS_PHYSICAL; + + listall = 0; + + if (aflag) { + if (sflag || dflag) + usage(); + listall = 1; + } else if (sflag) { + if (dflag) + usage(); + depth = 0; + } + + if (!*argv) { + argv = save; + argv[0] = dot; + argv[1] = NULL; + } + + (void) getbsize(¬used, &blocksize); + blocksize /= 512; + + rval = 0; + + if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL) { + err(1, "fts_open"); + } + + while ((p = fts_read(fts)) != NULL) { + switch (p->fts_info) { + case FTS_D: + if (ignorep(p) || dirlinkchk(p)) + fts_set(fts, p, FTS_SKIP); + break; + case FTS_DP: + if (ignorep(p)) + break; + + ftsparnum = (off_t *)&p->fts_parent->fts_number; + ftsnum = (off_t *)&p->fts_number; + if (p->fts_statp->st_size < TWO_TB) { + ftsparnum[0] += ftsnum[0] += p->fts_statp->st_blocks; + } else { + ftsparnum[0] += ftsnum[0] += howmany(p->fts_statp->st_size, 512LL); + } + + if (p->fts_level <= depth) { + if (hflag) { + (void) prthumanval(howmany(*ftsnum, blocksize)); + (void) fprintf(thread_stdout, "\t%s\n", p->fts_path); + } else { + (void) fprintf(thread_stdout, "%jd\t%s\n", + (intmax_t)howmany(*ftsnum, blocksize), + p->fts_path); + } + } + break; + case FTS_DC: /* Ignore. */ + if (COMPAT_MODE("bin/du", "unix2003")) { + errx(1, "Can't follow symlink cycle from %s to %s", p->fts_path, p->fts_cycle->fts_path); + } + break; + case FTS_DNR: /* Warn, continue. */ + case FTS_ERR: + case FTS_NS: + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + break; + case FTS_SLNONE: + if (COMPAT_MODE("bin/du", "unix2003")) { + struct stat sb; + int rc = stat(p->fts_path, &sb); + if (rc < 0 && errno == ELOOP) { + errx(1, "Too many symlinks at %s", p->fts_path); + } + } + default: + if (ignorep(p)) + break; + + if (p->fts_statp->st_nlink > 1 && linkchk(p)) + break; + + if (listall || p->fts_level == 0) { + if (hflag) { + if (p->fts_statp->st_size < TWO_TB) { + (void) prthumanval(howmany(p->fts_statp->st_blocks, + blocksize)); + } else { + (void) prthumanval(howmany(howmany(p->fts_statp->st_size, 512LL), + blocksize)); + } + (void) fprintf(thread_stdout, "\t%s\n", p->fts_path); + } else { + if (p->fts_statp->st_size < TWO_TB) { + (void) fprintf(thread_stdout, "%jd\t%s\n", + (intmax_t)howmany(p->fts_statp->st_blocks, blocksize), + p->fts_path); + } else { + (void) fprintf(thread_stdout, "%jd\t%s\n", + (intmax_t)howmany(howmany(p->fts_statp->st_size, 512LL), blocksize), + p->fts_path); + } + } + } + + ftsparnum = (off_t *)&p->fts_parent->fts_number; + if (p->fts_statp->st_size < TWO_TB) { + ftsparnum[0] += p->fts_statp->st_blocks; + } else { + ftsparnum[0] += p->fts_statp->st_size / 512LL; + } + } + savednumber = ((off_t *)&p->fts_parent->fts_number)[0]; + } + + if (errno) { + err(1, "fts_read"); + } + + if (cflag) { + if (hflag) { + (void) prthumanval(howmany(savednumber, blocksize)); + (void) fprintf(thread_stdout, "\ttotal\n"); + } else { + (void) fprintf(thread_stdout, "%jd\ttotal\n", (intmax_t)howmany(savednumber, blocksize)); + } + } + + ignoreclean(); + exit(rval); +} + +static int +linkchk(FTSENT *p) +{ + struct links_entry { + struct links_entry *next; + struct links_entry *previous; + int links; + dev_t dev; + ino_t ino; + }; + static const size_t links_hash_initial_size = 8192; + static struct links_entry **buckets; + static struct links_entry *free_list; + static size_t number_buckets; + static unsigned long number_entries; + static char stop_allocating; + struct links_entry *le, **new_buckets; + struct stat *st; + size_t i, new_size; + int hash; + + st = p->fts_statp; + + /* If necessary, initialize the hash table. */ + if (buckets == NULL) { + number_buckets = links_hash_initial_size; + buckets = malloc(number_buckets * sizeof(buckets[0])); + if (buckets == NULL) { + errx(1, "No memory for hardlink detection"); + } + for (i = 0; i < number_buckets; i++) + buckets[i] = NULL; + } + + /* If the hash table is getting too full, enlarge it. */ + if (number_entries > number_buckets * 10 && !stop_allocating) { + new_size = number_buckets * 2; + new_buckets = malloc(new_size * sizeof(struct links_entry *)); + + /* Try releasing the free list to see if that helps. */ + if (new_buckets == NULL && free_list != NULL) { + while (free_list != NULL) { + le = free_list; + free_list = le->next; + free(le); + } + new_buckets = malloc(new_size * sizeof(new_buckets[0])); + } + + if (new_buckets == NULL) { + stop_allocating = 1; + warnx("No more memory for tracking hard links"); + } else { + memset(new_buckets, 0, + new_size * sizeof(struct links_entry *)); + for (i = 0; i < number_buckets; i++) { + while (buckets[i] != NULL) { + /* Remove entry from old bucket. */ + le = buckets[i]; + buckets[i] = le->next; + + /* Add entry to new bucket. */ + hash = (le->dev ^ le->ino) % new_size; + + if (new_buckets[hash] != NULL) + new_buckets[hash]->previous = + le; + le->next = new_buckets[hash]; + le->previous = NULL; + new_buckets[hash] = le; + } + } + free(buckets); + buckets = new_buckets; + number_buckets = new_size; + } + } + + /* Try to locate this entry in the hash table. */ + hash = ( st->st_dev ^ st->st_ino ) % number_buckets; + for (le = buckets[hash]; le != NULL; le = le->next) { + if (le->dev == st->st_dev && le->ino == st->st_ino) { + /* + * Save memory by releasing an entry when we've seen + * all of it's links. + */ + if (--le->links <= 0) { + if (le->previous != NULL) + le->previous->next = le->next; + if (le->next != NULL) + le->next->previous = le->previous; + if (buckets[hash] == le) + buckets[hash] = le->next; + number_entries--; + /* Recycle this node through the free list */ + if (stop_allocating) { + free(le); + } else { + le->next = free_list; + free_list = le; + } + } + return (1); + } + } + + if (stop_allocating) + return (0); + + /* Add this entry to the links cache. */ + if (free_list != NULL) { + /* Pull a node from the free list if we can. */ + le = free_list; + free_list = le->next; + } else + /* Malloc one if we have to. */ + le = malloc(sizeof(struct links_entry)); + if (le == NULL) { + stop_allocating = 1; + warnx("No more memory for tracking hard links"); + return (0); + } + le->dev = st->st_dev; + le->ino = st->st_ino; + le->links = st->st_nlink - 1; + number_entries++; + le->next = buckets[hash]; + le->previous = NULL; + if (buckets[hash] != NULL) + buckets[hash]->previous = le; + buckets[hash] = le; + return (0); +} + +static int +dirlinkchk(FTSENT *p) +{ + struct links_entry { + struct links_entry *next; + struct links_entry *previous; + int links; + dev_t dev; + ino_t ino; + }; + static const size_t links_hash_initial_size = 8192; + static struct links_entry **buckets; + static struct links_entry *free_list; + static size_t number_buckets; + static unsigned long number_entries; + static char stop_allocating; + struct links_entry *le, **new_buckets; + struct stat *st; + size_t i, new_size; + int hash; + struct attrbuf { + int size; + int linkcount; + } buf; + struct attrlist attrList; + + memset(&attrList, 0, sizeof(attrList)); + attrList.bitmapcount = ATTR_BIT_MAP_COUNT; + attrList.dirattr = ATTR_DIR_LINKCOUNT; + if (-1 == getattrlist(p->fts_path, &attrList, &buf, sizeof(buf), 0)) + return 0; + if (buf.linkcount == 1) + return 0; + st = p->fts_statp; + + /* If necessary, initialize the hash table. */ + if (buckets == NULL) { + number_buckets = links_hash_initial_size; + buckets = malloc(number_buckets * sizeof(buckets[0])); + if (buckets == NULL) { + errx(1, "No memory for directory hardlink detection"); + } + for (i = 0; i < number_buckets; i++) + buckets[i] = NULL; + } + + /* If the hash table is getting too full, enlarge it. */ + if (number_entries > number_buckets * 10 && !stop_allocating) { + new_size = number_buckets * 2; + new_buckets = malloc(new_size * sizeof(struct links_entry *)); + + /* Try releasing the free list to see if that helps. */ + if (new_buckets == NULL && free_list != NULL) { + while (free_list != NULL) { + le = free_list; + free_list = le->next; + free(le); + } + new_buckets = malloc(new_size * sizeof(new_buckets[0])); + } + + if (new_buckets == NULL) { + stop_allocating = 1; + warnx("No more memory for tracking directory hard links"); + } else { + memset(new_buckets, 0, + new_size * sizeof(struct links_entry *)); + for (i = 0; i < number_buckets; i++) { + while (buckets[i] != NULL) { + /* Remove entry from old bucket. */ + le = buckets[i]; + buckets[i] = le->next; + + /* Add entry to new bucket. */ + hash = (le->dev ^ le->ino) % new_size; + + if (new_buckets[hash] != NULL) + new_buckets[hash]->previous = + le; + le->next = new_buckets[hash]; + le->previous = NULL; + new_buckets[hash] = le; + } + } + free(buckets); + buckets = new_buckets; + number_buckets = new_size; + } + } + + /* Try to locate this entry in the hash table. */ + hash = ( st->st_dev ^ st->st_ino ) % number_buckets; + for (le = buckets[hash]; le != NULL; le = le->next) { + if (le->dev == st->st_dev && le->ino == st->st_ino) { + /* + * Save memory by releasing an entry when we've seen + * all of it's links. + */ + if (--le->links <= 0) { + if (le->previous != NULL) + le->previous->next = le->next; + if (le->next != NULL) + le->next->previous = le->previous; + if (buckets[hash] == le) + buckets[hash] = le->next; + number_entries--; + /* Recycle this node through the free list */ + if (stop_allocating) { + free(le); + } else { + le->next = free_list; + free_list = le; + } + } + return (1); + } + } + + if (stop_allocating) + return (0); + /* Add this entry to the links cache. */ + if (free_list != NULL) { + /* Pull a node from the free list if we can. */ + le = free_list; + free_list = le->next; + } else + /* Malloc one if we have to. */ + le = malloc(sizeof(struct links_entry)); + if (le == NULL) { + stop_allocating = 1; + warnx("No more memory for tracking hard links"); + return (0); + } + le->dev = st->st_dev; + le->ino = st->st_ino; + le->links = buf.linkcount - 1; + number_entries++; + le->next = buckets[hash]; + le->previous = NULL; + if (buckets[hash] != NULL) + buckets[hash]->previous = le; + buckets[hash] = le; + return (0); +} + +/* + * Output in "human-readable" format. Uses 3 digits max and puts + * unit suffixes at the end. Makes output compact and easy to read, + * especially on huge disks. + * + */ +unit_t +unit_adjust(double *val) +{ + double abval; + unit_t unit; + unsigned int unit_sz; + + abval = fabs(*val); + + unit_sz = abval ? ilogb(abval) / 10 : 0; + + if (unit_sz >= UNIT_MAX) { + unit = NONE; + } else { + unit = unitp[unit_sz]; + *val /= (double)valp[unit_sz]; + } + + return (unit); +} + +void +prthumanval(double bytes) +{ + unit_t unit; + + bytes *= 512; + unit = unit_adjust(&bytes); + + if (bytes == 0) + (void)fprintf(thread_stdout, " 0B"); + else if (bytes > 10) + (void)fprintf(thread_stdout, "%3.0f%c", bytes, "BKMGTPE"[unit]); + else + (void)fprintf(thread_stdout, "%3.1f%c", bytes, "BKMGTPE"[unit]); +} + +static void +usage(void) +{ + (void)fprintf(thread_stderr, + "usage: du [-H | -L | -P] [-a | -s | -d depth] [-c] [-h | -k | -m | -g] [-x] [-I mask] [file ...]\n"); + exit(EX_USAGE); +} + +void +ignoreadd(const char *mask) +{ + struct ignentry *ign; + + ign = calloc(1, sizeof(*ign)); + if (ign == NULL) { + errx(1, "cannot allocate memory"); + } + ign->mask = strdup(mask); + if (ign->mask == NULL) { + errx(1, "cannot allocate memory"); + } + SLIST_INSERT_HEAD(&ignores, ign, next); +} + +void +ignoreclean(void) +{ + struct ignentry *ign; + + while (!SLIST_EMPTY(&ignores)) { + ign = SLIST_FIRST(&ignores); + SLIST_REMOVE_HEAD(&ignores, next); + free(ign->mask); + free(ign); + } +} + +int +ignorep(FTSENT *ent) +{ + struct ignentry *ign; + +#ifdef __APPLE__ + if (S_ISDIR(ent->fts_statp->st_mode) && !strcmp("fd", ent->fts_name)) { + struct statfs sfsb; + int rc = statfs(ent->fts_accpath, &sfsb); + if (rc >= 0 && !strcmp("devfs", sfsb.f_fstypename)) { + /* Don't cd into /dev/fd/N since one of those is likely to be + the cwd as of the start of du which causes all manner of + unpleasant surprises */ + return 1; + } + } +#endif /* __APPLE__ */ + SLIST_FOREACH(ign, &ignores, next) + if (fnmatch(ign->mask, ent->fts_name, 0) != FNM_NOMATCH) + return 1; + return 0; +} diff --git a/files/Sources/files/gzip/futimens.c b/files/Sources/files/gzip/futimens.c new file mode 100644 index 00000000..08345c33 --- /dev/null +++ b/files/Sources/files/gzip/futimens.c @@ -0,0 +1,100 @@ +/*- + * Copyright (c) 2015 Jilles Tjoelker + * 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 + +#ifndef UTIME_NOW +#define UTIME_NOW -1 +#endif + +#ifndef UTIME_OMIT +#define UTIME_OMIT -2 +#endif +int futimens(int fd, const struct timespec times[2]); + +int +futimens(int fd, const struct timespec times[2]) +{ + struct timeval now, tv[2], *tvp; + struct stat sb; + + if (times == NULL || (times[0].tv_nsec == UTIME_NOW && + times[1].tv_nsec == UTIME_NOW)) + tvp = NULL; + else if (times[0].tv_nsec == UTIME_OMIT && + times[1].tv_nsec == UTIME_OMIT) + return (0); + else { + if ((times[0].tv_nsec < 0 || times[0].tv_nsec > 999999999) && + times[0].tv_nsec != UTIME_NOW && + times[0].tv_nsec != UTIME_OMIT) { + errno = EINVAL; + return (-1); + } + if ((times[1].tv_nsec < 0 || times[1].tv_nsec > 999999999) && + times[1].tv_nsec != UTIME_NOW && + times[1].tv_nsec != UTIME_OMIT) { + errno = EINVAL; + return (-1); + } + tv[0].tv_sec = times[0].tv_sec; + tv[0].tv_usec = times[0].tv_nsec / 1000; + tv[1].tv_sec = times[1].tv_sec; + tv[1].tv_usec = times[1].tv_nsec / 1000; + tvp = tv; + if (times[0].tv_nsec == UTIME_OMIT || + times[1].tv_nsec == UTIME_OMIT) { + if (fstat(fd, &sb) == -1) + return (-1); + if (times[0].tv_nsec == UTIME_OMIT) { + tv[0].tv_sec = sb.st_atimespec.tv_sec; + tv[0].tv_usec = sb.st_atimespec.tv_nsec / 1000; + } + if (times[1].tv_nsec == UTIME_OMIT) { + tv[1].tv_sec = sb.st_mtimespec.tv_sec; + tv[1].tv_usec = sb.st_mtimespec.tv_nsec / 1000; + } + } + if (times[0].tv_nsec == UTIME_NOW || + times[1].tv_nsec == UTIME_NOW) { + if (gettimeofday(&now, NULL) == -1) + return (-1); + if (times[0].tv_nsec == UTIME_NOW) + tv[0] = now; + if (times[1].tv_nsec == UTIME_NOW) + tv[1] = now; + } + } + return (futimes(fd, tvp)); +} diff --git a/files/Sources/files/gzip/gzip.1 b/files/Sources/files/gzip/gzip.1 new file mode 100644 index 00000000..98e0ea25 --- /dev/null +++ b/files/Sources/files/gzip/gzip.1 @@ -0,0 +1,234 @@ +.\" $NetBSD: gzip.1,v 1.26 2015/10/27 07:36:18 mrg Exp $ +.\" +.\" Copyright (c) 1997, 2003, 2004 Matthew R. Green +.\" 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 ``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 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. +.\" +.\" $FreeBSD: head/usr.bin/gzip/gzip.1 290073 2015-10-27 21:26:05Z delphij $ +.Dd October 26, 2015 +.Dt GZIP 1 +.Os +.Sh NAME +.Nm gzip +.Nd compression/decompression tool using Lempel-Ziv coding (LZ77) +.Sh SYNOPSIS +.Nm +.Op Fl cdfhkLlNnqrtVv +.Op Fl S Ar suffix +.Ar file +.Oo +.Ar file Oo ... +.Oc +.Oc +.Nm gunzip +.Op Fl cfhkLNqrtVv +.Op Fl S Ar suffix +.Ar file +.Oo +.Ar file Oo ... +.Oc +.Oc +.Nm zcat +.Op Fl fhV +.Ar file +.Oo +.Ar file Oo ... +.Oc +.Oc +.Sh DESCRIPTION +The +.Nm +program compresses and decompresses files using Lempel-Ziv coding +(LZ77). +If no +.Ar files +are specified, +.Nm +will compress from standard input, or decompress to standard output. +When in compression mode, each +.Ar file +will be replaced with another file with the suffix, set by the +.Fl S Ar suffix +option, added, if possible. +.Pp +In decompression mode, each +.Ar file +will be checked for existence, as will the file with the suffix +added. +Each +.Ar file +argument must contain a separate complete archive; +when multiple +.Ar files +are indicated, each is decompressed in turn. +.Pp +In the case of +.Nm gzcat +the resulting data is then concatenated in the manner of +.Xr cat 1 . +.Pp +If invoked as +.Nm gunzip +then the +.Fl d +option is enabled. +If invoked as +.Nm zcat +or +.Nm gzcat +then both the +.Fl c +and +.Fl d +options are enabled. +.Pp +This version of +.Nm +is also capable of decompressing files compressed using +.Xr compress 1 , +.Xr bzip2 1 , +or +.Xr xz 1 . +.Sh OPTIONS +The following options are available: +.Bl -tag -width XXrXXXrecursiveX +.It Fl 1 , -fast +.It Fl 2 , 3 , 4 , 5 , 6 , 7 , 8 +.It Fl 9 , -best +These options change the compression level used, with the +.Fl 1 +option being the fastest, with less compression, and the +.Fl 9 +option being the slowest, with optimal compression. +The default compression level is 6. +.It Fl c , -stdout , -to-stdout +This option specifies that output will go to the standard output +stream, leaving files intact. +.It Fl d , -decompress , -uncompress +This option selects decompression rather than compression. +.It Fl f , -force +This option turns on force mode. +This allows files with multiple links, symbolic links to regular files, +overwriting of pre-existing files, reading from or writing to a terminal, +and when combined with the +.Fl c +option, allowing non-compressed data to pass through unchanged. +.It Fl h , -help +This option prints a usage summary and exits. +.It Fl k , -keep +Keep (do not delete) input files during compression +or decompression. +.It Fl L , -license +This option prints +.Nm +license. +.It Fl l , -list +This option displays information about the file's compressed and +uncompressed size, ratio, uncompressed name. +With the +.Fl v +option, it also displays the compression method, CRC, date and time +embedded in the file. +.It Fl N , -name +This option causes the stored filename in the input file to be used +as the output file. +.It Fl n , -no-name +This option stops the filename and timestamp from being stored in +the output file. +.It Fl q , -quiet +With this option, no warnings or errors are printed. +.It Fl r , -recursive +This option is used to +.Nm +the files in a directory tree individually, using the +.Xr fts 3 +library. +.It Fl S Ar suffix , Fl -suffix Ar suffix +This option changes the default suffix from .gz to +.Ar suffix . +.It Fl t , -test +This option will test compressed files for integrity. +.It Fl V , -version +This option prints the version of the +.Nm +program. +.It Fl v , -verbose +This option turns on verbose mode, which prints the compression +ratio for each file compressed. +.El +.Sh ENVIRONMENT +If the environment variable +.Ev GZIP +is set, it is parsed as a white-space separated list of options +handled before any options on the command line. +Options on the command line will override anything in +.Ev GZIP . +.Sh EXIT STATUS +The +.Nm +utility exits 0 on success, +1 on errors, +and 2 if a warning occurs. +.Sh SEE ALSO +.Xr bzip2 1 , +.Xr compress 1 , +.Xr xz 1 , +.Xr fts 3 , +.Xr zlib 3 +.Sh HISTORY +The +.Nm +program was originally written by Jean-loup Gailly, licensed under +the GNU Public Licence. +Matthew R. Green wrote a simple front end for +.Nx 1.3 +distribution media, based on the freely re-distributable zlib library. +It was enhanced to be mostly feature-compatible with the original +GNU +.Nm +program for +.Nx 2.0 . +.Pp +This implementation of +.Nm +was ported based on the +.Nx +.Nm , +and first appeared in +.Fx 7.0 . +.Sh AUTHORS +.An -nosplit +This implementation of +.Nm +was written by +.An Matthew R. Green Aq Mt mrg@eterna.com.au +with unpack support written by +.An Xin LI Aq Mt delphij@FreeBSD.org . +.Sh BUGS +According to RFC 1952, the recorded file size is stored in a 32-bit +integer, therefore, it cannot represent files larger than 4GB. +This limitation also applies to +.Fl l +option of +.Nm +utility. diff --git a/files/Sources/files/gzip/gzip.c b/files/Sources/files/gzip/gzip.c new file mode 100644 index 00000000..9c9d7905 --- /dev/null +++ b/files/Sources/files/gzip/gzip.c @@ -0,0 +1,2279 @@ +/* $NetBSD: gzip.c,v 1.109 2015/10/27 07:36:18 mrg Exp $ */ + +/*- + * Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green + * 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 ``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 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 +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1997, 1998, 2003, 2004, 2006\ + Matthew R. Green. All rights reserved."); +__FBSDID("$FreeBSD: head/usr.bin/gzip/gzip.c 290073 2015-10-27 21:26:05Z delphij $"); +#endif /* not lint */ + +/* + * gzip.c -- GPL free gzip using zlib. + * + * RFC 1950 covers the zlib format + * RFC 1951 covers the deflate format + * RFC 1952 covers the gzip format + * + * TODO: + * - use mmap where possible + * - make bzip2/compress -v/-t/-l support work as well as possible + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#include +// #include +#define COMPAT_MODE(func, mode) 1 +int futimens(int fd, const struct timespec times[2]); +#endif /* __APPLE__ */ +#include // for booleans +#include "ios_error.h" +// iOS: Until someone install the header file (the library exists) +#define NO_XZ_SUPPORT + +/* what type of file are we dealing with */ +enum filetype { + FT_GZIP, +#ifndef NO_BZIP2_SUPPORT + FT_BZIP2, +#endif +#ifndef NO_COMPRESS_SUPPORT + FT_Z, +#endif +#ifndef NO_PACK_SUPPORT + FT_PACK, +#endif +#ifndef NO_XZ_SUPPORT + FT_XZ, +#endif + FT_LAST, + FT_UNKNOWN +}; + +#ifndef NO_BZIP2_SUPPORT +#include + +#define BZ2_SUFFIX ".bz2" +#define BZIP2_MAGIC "\102\132\150" +#endif + +#ifndef NO_COMPRESS_SUPPORT +#define Z_SUFFIX ".Z" +#define Z_MAGIC "\037\235" +#endif + +#ifndef NO_PACK_SUPPORT +#define PACK_MAGIC "\037\036" +#endif + +#ifndef NO_XZ_SUPPORT +#include +#define XZ_SUFFIX ".xz" +#define XZ_MAGIC "\3757zXZ" +#endif + +#define GZ_SUFFIX ".gz" + +#define BUFLEN (64 * 1024) + +#define GZIP_MAGIC0 0x1F +#define GZIP_MAGIC1 0x8B +#define GZIP_OMAGIC1 0x9E + +#define GZIP_TIMESTAMP (off_t)4 +#define GZIP_ORIGNAME (off_t)10 + +#define HEAD_CRC 0x02 +#define EXTRA_FIELD 0x04 +#define ORIG_NAME 0x08 +#define COMMENT 0x10 + +#define OS_CODE 3 /* Unix */ + +typedef struct { + const char *zipped; + int ziplen; + const char *normal; /* for unzip - must not be longer than zipped */ +} suffixes_t; +static suffixes_t suffixes[] = { +#define SUFFIX(Z, N) {Z, sizeof Z - 1, N} + SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S .xxx */ +#ifndef SMALL + SUFFIX(GZ_SUFFIX, ""), + SUFFIX(".z", ""), + SUFFIX("-gz", ""), + SUFFIX("-z", ""), + SUFFIX("_z", ""), + SUFFIX(".taz", ".tar"), + SUFFIX(".tgz", ".tar"), +#ifndef NO_BZIP2_SUPPORT + SUFFIX(BZ2_SUFFIX, ""), + SUFFIX(".tbz", ".tar"), + SUFFIX(".tbz2", ".tar"), +#endif +#ifndef NO_COMPRESS_SUPPORT + SUFFIX(Z_SUFFIX, ""), +#endif +#ifndef NO_XZ_SUPPORT + SUFFIX(XZ_SUFFIX, ""), +#endif + SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S "" */ +#endif /* SMALL */ +#undef SUFFIX +}; +#define NUM_SUFFIXES (sizeof suffixes / sizeof suffixes[0]) +#define SUFFIX_MAXLEN 30 + +#ifdef __APPLE__ +static const char gzip_version[] = "Apple gzip "; // GZIP_APPLE_VERSION; +#else +static const char gzip_version[] = "FreeBSD gzip 20150413"; +#endif + +#ifndef SMALL +static const char gzip_copyright[] = \ +" Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n" +" All rights reserved.\n" +"\n" +" Redistribution and use in source and binary forms, with or without\n" +" modification, are permitted provided that the following conditions\n" +" are met:\n" +" 1. Redistributions of source code must retain the above copyright\n" +" notice, this list of conditions and the following disclaimer.\n" +" 2. Redistributions in binary form must reproduce the above copyright\n" +" notice, this list of conditions and the following disclaimer in the\n" +" documentation and/or other materials provided with the distribution.\n" +"\n" +" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n" +" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n" +" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n" +" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n" +" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n" +" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n" +" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n" +" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n" +" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n" +" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n" +" SUCH DAMAGE."; +#endif + +static int cflag; /* stdout mode */ +static int dflag; /* decompress mode */ +static int lflag; /* list mode */ +static int numflag = 6; /* gzip -1..-9 value */ + +#ifndef SMALL +static int fflag; /* force mode */ +static int kflag; /* don't delete input files */ +static int nflag; /* don't save name/timestamp */ +static int Nflag; /* don't restore name/timestamp */ +static int qflag; /* quiet mode */ +static int rflag; /* recursive mode */ +static int tflag; /* test */ +static int vflag; /* verbose mode */ +static const char *remove_file = NULL; /* file to be removed upon SIGINT */ +#else +#define qflag 0 +#define tflag 0 +#endif + +static int exit_value = 0; /* exit value */ + +static char *infile; /* name of file coming in */ + +#ifdef __APPLE__ +static bool zcat; +#endif + +static void maybe_err(const char *fmt, ...) __printflike(1, 2) ; // __dead2; +#if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) || \ + !defined(NO_XZ_SUPPORT) +static void maybe_errx(const char *fmt, ...) __printflike(1, 2) ; // __dead2; +#endif +static void maybe_warn(const char *fmt, ...) __printflike(1, 2); +static void maybe_warnx(const char *fmt, ...) __printflike(1, 2); +static enum filetype file_gettype(u_char *); +#ifdef SMALL +#define gz_compress(if, of, sz, fn, tm) gz_compress(if, of, sz) +#endif +static off_t gz_compress(int, int, off_t *, const char *, uint32_t); +static off_t gz_uncompress(int, int, char *, size_t, off_t *, const char *); +static off_t file_compress(char *, char *, size_t); +static off_t file_uncompress(char *, char *, size_t); +static void handle_pathname(char *); +static void handle_file(char *, struct stat *); +static void handle_stdin(void); +static void handle_stdout(void); +static void print_ratio(off_t, off_t, FILE *); +static void print_list(int fd, off_t, const char *, time_t); +static void usage(void) ; // __dead2; +static void display_version(void) ; // __dead2; +#ifndef SMALL +static void display_license(void); +static void sigint_handler(int); +#endif +static const suffixes_t *check_suffix(char *, int); +static ssize_t read_retry(int, void *, size_t); + +#ifdef SMALL +#define unlink_input(f, sb) unlink(f) +#else +static off_t cat_fd(unsigned char *, size_t, off_t *, int fd); +static void prepend_gzip(char *, int *, char ***); +static void handle_dir(char *); +static void print_verbage(const char *, const char *, off_t, off_t); +static void print_test(const char *, int); +static void copymodes(int fd, const struct stat *, const char *file); +static int check_outfile(const char *outfile); +#endif + +#ifndef NO_BZIP2_SUPPORT +static off_t unbzip2(int, int, char *, size_t, off_t *); +#endif + +#ifndef NO_COMPRESS_SUPPORT +static FILE *zdopen(int); +static off_t zuncompress(FILE *, FILE *, char *, size_t, off_t *); +#endif + +#ifndef NO_PACK_SUPPORT +static off_t unpack(int, int, char *, size_t, off_t *); +#endif + +#ifndef NO_XZ_SUPPORT +static off_t unxz(int, int, char *, size_t, off_t *); +#endif + +#ifdef SMALL +#define getopt_long(a,b,c,d,e) getopt(a,b,c) +#else +static const struct option longopts[] = { + { "stdout", no_argument, 0, 'c' }, + { "to-stdout", no_argument, 0, 'c' }, + { "decompress", no_argument, 0, 'd' }, + { "uncompress", no_argument, 0, 'd' }, + { "force", no_argument, 0, 'f' }, + { "help", no_argument, 0, 'h' }, + { "keep", no_argument, 0, 'k' }, + { "list", no_argument, 0, 'l' }, + { "no-name", no_argument, 0, 'n' }, + { "name", no_argument, 0, 'N' }, + { "quiet", no_argument, 0, 'q' }, + { "recursive", no_argument, 0, 'r' }, + { "suffix", required_argument, 0, 'S' }, + { "test", no_argument, 0, 't' }, + { "verbose", no_argument, 0, 'v' }, + { "version", no_argument, 0, 'V' }, + { "fast", no_argument, 0, '1' }, + { "best", no_argument, 0, '9' }, + { "ascii", no_argument, 0, 'a' }, + { "license", no_argument, 0, 'L' }, + { NULL, no_argument, 0, 0 }, +}; +#endif + +int +gzip_main(int argc, char **argv) +{ + const char *progname = argv[0]; // getprogname(); // getprogname returns Host application +#ifndef SMALL + char *gzip; + int len; +#endif + int ch; + // Initialize all flags: + optind = 1; opterr = 1; optreset = 1; + cflag = dflag = lflag = 0; + numflag = 6; +#ifndef SMALL + fflag = kflag = nflag = Nflag = qflag = rflag = tflag = vflag = 0; +#endif + exit_value = 0; /* exit value */ +#ifdef __APPLE__ + zcat = false; +#endif + +#ifndef SMALL + if ((gzip = getenv("GZIP")) != NULL) + prepend_gzip(gzip, &argc, &argv); + signal(SIGINT, sigint_handler); +#endif + + /* + * XXX + * handle being called `gunzip', `zcat' and `gzcat' + */ + if (strcmp(progname, "gunzip") == 0) + dflag = 1; + else if (strcmp(progname, "zcat") == 0 || + strcmp(progname, "gzcat") == 0) + dflag = cflag = 1; + +#ifdef __APPLE__ + if (strcmp(progname, "zcat") == 0) { + zcat = true; + } +#endif + +#ifdef SMALL +#define OPT_LIST "123456789cdhlV" +#else +#define OPT_LIST "123456789acdfhklLNnqrS:tVv" +#endif + + while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) { + switch (ch) { + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + numflag = ch - '0'; + break; + case 'c': + cflag = 1; + break; + case 'd': + dflag = 1; + break; + case 'l': + lflag = 1; + dflag = 1; + break; + case 'V': + display_version(); + /* NOTREACHED */ +#ifndef SMALL + case 'a': + fprintf(thread_stderr, "%s: option --ascii ignored on this system\n", progname); + break; + case 'f': + fflag = 1; + break; + case 'k': + kflag = 1; + break; + case 'L': + display_license(); + /* NOT REACHED */ + case 'N': + nflag = 0; + Nflag = 1; + break; + case 'n': + nflag = 1; + Nflag = 0; + break; + case 'q': + qflag = 1; + break; + case 'r': + rflag = 1; + break; + case 'S': + len = strlen(optarg); + if (len != 0) { + if (len > SUFFIX_MAXLEN) { + errx(1, "incorrect suffix: '%s': too long", optarg); + } + suffixes[0].zipped = optarg; + suffixes[0].ziplen = len; + } else { + suffixes[NUM_SUFFIXES - 1].zipped = ""; + suffixes[NUM_SUFFIXES - 1].ziplen = 0; + } + break; + case 't': + cflag = 1; + tflag = 1; + dflag = 1; + break; + case 'v': + vflag = 1; + break; +#endif + default: + usage(); + /* NOTREACHED */ + } + } + argv += optind; + argc -= optind; + + if (argc == 0) { + if (dflag) /* stdin mode */ + handle_stdin(); + else /* stdout mode */ + handle_stdout(); + } else { + do { + handle_pathname(argv[0]); + } while (*++argv); + } +#ifndef SMALL + if (qflag == 0 && lflag && argc > 1) + print_list(-1, 0, "(totals)", 0); +#endif + exit(exit_value); +} + +/* maybe print a warning */ +void +maybe_warn(const char *fmt, ...) +{ + va_list ap; + + if (qflag == 0) { + va_start(ap, fmt); + vwarn(fmt, ap); + va_end(ap); + } + if (exit_value == 0) + exit_value = 1; +} + +/* ... without an errno. */ +void +maybe_warnx(const char *fmt, ...) +{ + va_list ap; + + if (qflag == 0) { + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); + } + if (exit_value == 0) + exit_value = 1; +} + +/* maybe print an error */ +void +maybe_err(const char *fmt, ...) +{ + va_list ap; + + if (qflag == 0) { + va_start(ap, fmt); + vwarn(fmt, ap); + va_end(ap); + } + exit(2); +} + +#if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) || \ + !defined(NO_XZ_SUPPORT) +/* ... without an errno. */ +void +maybe_errx(const char *fmt, ...) +{ + va_list ap; + + if (qflag == 0) { + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); + } + exit(2); +} +#endif + +#ifndef SMALL +/* split up $GZIP and prepend it to the argument list */ +static void +prepend_gzip(char *gzip, int *argc, char ***argv) +{ + char *s, **nargv, **ac; + int nenvarg = 0, i; + + /* scan how many arguments there are */ + for (s = gzip;;) { + while (*s == ' ' || *s == '\t') + s++; + if (*s == 0) + goto count_done; + nenvarg++; + while (*s != ' ' && *s != '\t') + if (*s++ == 0) + goto count_done; + } +count_done: + /* punt early */ + if (nenvarg == 0) + return; + + *argc += nenvarg; + ac = *argv; + + nargv = (char **)malloc((*argc + 1) * sizeof(char *)); + if (nargv == NULL) + maybe_err("malloc"); + + /* stash this away */ + *argv = nargv; + + /* copy the program name first */ + i = 0; + nargv[i++] = *(ac++); + + s = gzip; + for (;;) { + /* Skip whitespaces. */ + while (*s == ' ' || *s == '\t') + s++; + if (*s == 0) { + goto copy_done; + } + nargv[i++] = s; + /* Find the end of this argument. */ + while (*s != ' ' && *s != '\t') + if (*s++ == 0) + /* Argument followed by NUL. */ + goto copy_done; + /* copy any unterminated args */ + nargv[i-1] = strndup(nargv[i-1], s-nargv[i-1]); + if (nargv[i-1] == NULL) + maybe_err("strndup"); + s++; + } +copy_done: + + /* copy the original arguments and a NULL */ + while (*ac) + nargv[i++] = *(ac++); + nargv[i] = NULL; +} +#endif + +/* compress input to output. Return bytes read, -1 on error */ +static off_t +gz_compress(int in, int out, off_t *gsizep, const char *origname, uint32_t mtime) +{ + z_stream z; + char *outbufp, *inbufp; + off_t in_tot = 0, out_tot = 0; + ssize_t in_size; + int i, error; + uLong crc; +#ifdef SMALL + static char header[] = { GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, 0, + 0, 0, 0, 0, + 0, OS_CODE }; +#endif + + outbufp = malloc(BUFLEN); + inbufp = malloc(BUFLEN); + if (outbufp == NULL || inbufp == NULL) { + maybe_err("malloc failed"); + goto out; + } + + memset(&z, 0, sizeof z); + z.zalloc = Z_NULL; + z.zfree = Z_NULL; + z.opaque = 0; + +#ifdef SMALL + memcpy(outbufp, header, sizeof header); + i = sizeof header; +#else + if (nflag != 0) { + mtime = 0; + origname = ""; + } + + i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c%c%c%s", + GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, + *origname ? ORIG_NAME : 0, + mtime & 0xff, + (mtime >> 8) & 0xff, + (mtime >> 16) & 0xff, + (mtime >> 24) & 0xff, + numflag == 1 ? 4 : numflag == 9 ? 2 : 0, + OS_CODE, origname); + if (i >= BUFLEN) + /* this need PATH_MAX > BUFLEN ... */ + maybe_err("snprintf"); + if (*origname) + i++; +#endif + + z.next_out = (unsigned char *)outbufp + i; + z.avail_out = BUFLEN - i; + + error = deflateInit2(&z, numflag, Z_DEFLATED, + (-MAX_WBITS), 8, Z_DEFAULT_STRATEGY); + if (error != Z_OK) { + maybe_warnx("deflateInit2 failed"); + in_tot = -1; + goto out; + } + + crc = crc32(0L, Z_NULL, 0); + for (;;) { + if (z.avail_out == 0) { + if (write(out, outbufp, BUFLEN) != BUFLEN) { + maybe_warn("write"); + out_tot = -1; + goto out; + } + + out_tot += BUFLEN; + z.next_out = (unsigned char *)outbufp; + z.avail_out = BUFLEN; + } + + if (z.avail_in == 0) { + in_size = read(in, inbufp, BUFLEN); + if (in_size < 0) { + maybe_warn("read"); + in_tot = -1; + goto out; + } + if (in_size == 0) + break; + + crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size); + in_tot += in_size; + z.next_in = (unsigned char *)inbufp; + z.avail_in = in_size; + } + + error = deflate(&z, Z_NO_FLUSH); + if (error != Z_OK && error != Z_STREAM_END) { + maybe_warnx("deflate failed"); + in_tot = -1; + goto out; + } + } + + /* clean up */ + for (;;) { + size_t len; + ssize_t w; + + error = deflate(&z, Z_FINISH); + if (error != Z_OK && error != Z_STREAM_END) { + maybe_warnx("deflate failed"); + in_tot = -1; + goto out; + } + + len = (char *)z.next_out - outbufp; + + w = write(out, outbufp, len); + if (w == -1 || (size_t)w != len) { + maybe_warn("write"); + out_tot = -1; + goto out; + } + out_tot += len; + z.next_out = (unsigned char *)outbufp; + z.avail_out = BUFLEN; + + if (error == Z_STREAM_END) + break; + } + + if (deflateEnd(&z) != Z_OK) { + maybe_warnx("deflateEnd failed"); + in_tot = -1; + goto out; + } + + i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c", + (int)crc & 0xff, + (int)(crc >> 8) & 0xff, + (int)(crc >> 16) & 0xff, + (int)(crc >> 24) & 0xff, + (int)in_tot & 0xff, + (int)(in_tot >> 8) & 0xff, + (int)(in_tot >> 16) & 0xff, + (int)(in_tot >> 24) & 0xff); + if (i != 8) + maybe_err("snprintf"); + if (write(out, outbufp, i) != i) { + maybe_warn("write"); + in_tot = -1; + } else + out_tot += i; + +out: + if (inbufp != NULL) + free(inbufp); + if (outbufp != NULL) + free(outbufp); + if (gsizep) + *gsizep = out_tot; + return in_tot; +} + +/* + * uncompress input to output then close the input. return the + * uncompressed size written, and put the compressed sized read + * into `*gsizep'. + */ +static off_t +gz_uncompress(int in, int out, char *pre, size_t prelen, off_t *gsizep, + const char *filename) +{ + z_stream z; + char *outbufp, *inbufp; + off_t out_tot = -1, in_tot = 0; + uint32_t out_sub_tot = 0; + enum { + GZSTATE_MAGIC0, + GZSTATE_MAGIC1, + GZSTATE_METHOD, + GZSTATE_FLAGS, + GZSTATE_SKIPPING, + GZSTATE_EXTRA, + GZSTATE_EXTRA2, + GZSTATE_EXTRA3, + GZSTATE_ORIGNAME, + GZSTATE_COMMENT, + GZSTATE_HEAD_CRC1, + GZSTATE_HEAD_CRC2, + GZSTATE_INIT, + GZSTATE_READ, + GZSTATE_CRC, + GZSTATE_LEN, + } state = GZSTATE_MAGIC0; + int flags = 0, skip_count = 0; + int error = Z_STREAM_ERROR, done_reading = 0; + uLong crc = 0; + ssize_t wr; + int needmore = 0; + +#define ADVANCE() { z.next_in++; z.avail_in--; } + + if ((outbufp = malloc(BUFLEN)) == NULL) { + maybe_err("malloc failed"); + goto out2; + } + if ((inbufp = malloc(BUFLEN)) == NULL) { + maybe_err("malloc failed"); + goto out1; + } + + memset(&z, 0, sizeof z); + z.avail_in = prelen; + z.next_in = (unsigned char *)pre; + z.avail_out = BUFLEN; + z.next_out = (unsigned char *)outbufp; + z.zalloc = NULL; + z.zfree = NULL; + z.opaque = 0; + + in_tot = prelen; + out_tot = 0; + + for (;;) { + if ((z.avail_in == 0 || needmore) && done_reading == 0) { + ssize_t in_size; + + if (z.avail_in > 0) { + memmove(inbufp, z.next_in, z.avail_in); + } + z.next_in = (unsigned char *)inbufp; + in_size = read(in, z.next_in + z.avail_in, + BUFLEN - z.avail_in); + + if (in_size == -1) { + maybe_warn("failed to read stdin"); + goto stop_and_fail; + } else if (in_size == 0) { + done_reading = 1; + } + + z.avail_in += in_size; + needmore = 0; + + in_tot += in_size; + } + if (z.avail_in == 0) { + if (done_reading && state != GZSTATE_MAGIC0) { + maybe_warnx("%s: unexpected end of file", + filename); + goto stop_and_fail; + } + goto stop; + } + switch (state) { + case GZSTATE_MAGIC0: + if (*z.next_in != GZIP_MAGIC0) { + if (in_tot > 0) { + maybe_warnx("%s: trailing garbage " + "ignored", filename); + exit_value = 2; + goto stop; + } + maybe_warnx("input not gziped (MAGIC0)"); + goto stop_and_fail; + } + ADVANCE(); + state++; + out_sub_tot = 0; + crc = crc32(0L, Z_NULL, 0); + break; + + case GZSTATE_MAGIC1: + if (*z.next_in != GZIP_MAGIC1 && + *z.next_in != GZIP_OMAGIC1) { + maybe_warnx("input not gziped (MAGIC1)"); + goto stop_and_fail; + } + ADVANCE(); + state++; + break; + + case GZSTATE_METHOD: + if (*z.next_in != Z_DEFLATED) { + maybe_warnx("unknown compression method"); + goto stop_and_fail; + } + ADVANCE(); + state++; + break; + + case GZSTATE_FLAGS: + flags = *z.next_in; + ADVANCE(); + skip_count = 6; + state++; + break; + + case GZSTATE_SKIPPING: + if (skip_count > 0) { + skip_count--; + ADVANCE(); + } else + state++; + break; + + case GZSTATE_EXTRA: + if ((flags & EXTRA_FIELD) == 0) { + state = GZSTATE_ORIGNAME; + break; + } + skip_count = *z.next_in; + ADVANCE(); + state++; + break; + + case GZSTATE_EXTRA2: + skip_count |= ((*z.next_in) << 8); + ADVANCE(); + state++; + break; + + case GZSTATE_EXTRA3: + if (skip_count > 0) { + skip_count--; + ADVANCE(); + } else + state++; + break; + + case GZSTATE_ORIGNAME: + if ((flags & ORIG_NAME) == 0) { + state++; + break; + } + if (*z.next_in == 0) + state++; + ADVANCE(); + break; + + case GZSTATE_COMMENT: + if ((flags & COMMENT) == 0) { + state++; + break; + } + if (*z.next_in == 0) + state++; + ADVANCE(); + break; + + case GZSTATE_HEAD_CRC1: + if (flags & HEAD_CRC) + skip_count = 2; + else + skip_count = 0; + state++; + break; + + case GZSTATE_HEAD_CRC2: + if (skip_count > 0) { + skip_count--; + ADVANCE(); + } else + state++; + break; + + case GZSTATE_INIT: + if (inflateInit2(&z, -MAX_WBITS) != Z_OK) { + maybe_warnx("failed to inflateInit"); + goto stop_and_fail; + } + state++; + break; + + case GZSTATE_READ: + error = inflate(&z, Z_FINISH); + switch (error) { + /* Z_BUF_ERROR goes with Z_FINISH... */ + case Z_BUF_ERROR: + if (z.avail_out > 0 && !done_reading) + continue; + + case Z_STREAM_END: + case Z_OK: + break; + + case Z_NEED_DICT: + maybe_warnx("Z_NEED_DICT error"); + goto stop_and_fail; + case Z_DATA_ERROR: + maybe_warnx("data stream error"); + goto stop_and_fail; + case Z_STREAM_ERROR: + maybe_warnx("internal stream error"); + goto stop_and_fail; + case Z_MEM_ERROR: + maybe_warnx("memory allocation error"); + goto stop_and_fail; + + default: + maybe_warn("unknown error from inflate(): %d", + error); + } + wr = BUFLEN - z.avail_out; + + if (wr != 0) { + crc = crc32(crc, (const Bytef *)outbufp, (unsigned)wr); + if ( +#ifndef SMALL + /* don't write anything with -t */ + tflag == 0 && +#endif + write(out, outbufp, wr) != wr) { + maybe_warn("error writing to output"); + goto stop_and_fail; + } + + out_tot += wr; + out_sub_tot += wr; + } + + if (error == Z_STREAM_END) { + inflateEnd(&z); + state++; + } + + z.next_out = (unsigned char *)outbufp; + z.avail_out = BUFLEN; + + break; + case GZSTATE_CRC: + { + uLong origcrc; + + if (z.avail_in < 4) { + if (!done_reading) { + needmore = 1; + continue; + } + maybe_warnx("truncated input"); + goto stop_and_fail; + } + origcrc = ((unsigned)z.next_in[0] & 0xff) | + ((unsigned)z.next_in[1] & 0xff) << 8 | + ((unsigned)z.next_in[2] & 0xff) << 16 | + ((unsigned)z.next_in[3] & 0xff) << 24; + if (origcrc != crc) { + maybe_warnx("invalid compressed" + " data--crc error"); + goto stop_and_fail; + } + } + + z.avail_in -= 4; + z.next_in += 4; + + if (!z.avail_in && done_reading) { + goto stop; + } + state++; + break; + case GZSTATE_LEN: + { + uLong origlen; + + if (z.avail_in < 4) { + if (!done_reading) { + needmore = 1; + continue; + } + maybe_warnx("truncated input"); + goto stop_and_fail; + } + origlen = ((unsigned)z.next_in[0] & 0xff) | + ((unsigned)z.next_in[1] & 0xff) << 8 | + ((unsigned)z.next_in[2] & 0xff) << 16 | + ((unsigned)z.next_in[3] & 0xff) << 24; + + if (origlen != out_sub_tot) { + maybe_warnx("invalid compressed" + " data--length error"); + goto stop_and_fail; + } + } + + z.avail_in -= 4; + z.next_in += 4; + + if (error < 0) { + maybe_warnx("decompression error"); + goto stop_and_fail; + } + state = GZSTATE_MAGIC0; + break; + } + continue; +stop_and_fail: + out_tot = -1; +stop: + break; + } + if (state > GZSTATE_INIT) + inflateEnd(&z); + + free(inbufp); +out1: + free(outbufp); +out2: + if (gsizep) + *gsizep = in_tot; + return (out_tot); +} + +#ifndef SMALL +/* + * set the owner, mode, flags & utimes using the given file descriptor. + * file is only used in possible warning messages. + */ +static void +copymodes(int fd, const struct stat *sbp, const char *file) +{ + struct timespec times[2]; + struct stat sb; + + /* + * If we have no info on the input, give this file some + * default values and return.. + */ + if (sbp == NULL) { + mode_t mask = umask(022); + + (void)fchmod(fd, DEFFILEMODE & ~mask); + (void)umask(mask); + return; + } + sb = *sbp; + + /* if the chown fails, remove set-id bits as-per compress(1) */ + if (fchown(fd, sb.st_uid, sb.st_gid) < 0) { + if (errno != EPERM) + maybe_warn("couldn't fchown: %s", file); + sb.st_mode &= ~(S_ISUID|S_ISGID); + } + + /* we only allow set-id and the 9 normal permission bits */ + sb.st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO; + if (fchmod(fd, sb.st_mode) < 0) + maybe_warn("couldn't fchmod: %s", file); + +#ifdef __APPLE__ + times[0] = sb.st_atimespec; + times[1] = sb.st_mtimespec; +#else + times[0] = sb.st_atim; + times[1] = sb.st_mtim; +#endif + if (futimens(fd, times) < 0) + maybe_warn("couldn't futimens: %s", file); + + /* only try flags if they exist already */ + if (sb.st_flags != 0 && fchflags(fd, sb.st_flags) < 0) + maybe_warn("couldn't fchflags: %s", file); +} +#endif + +/* what sort of file is this? */ +static enum filetype +file_gettype(u_char *buf) +{ + + if (buf[0] == GZIP_MAGIC0 && + (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1)) + return FT_GZIP; + else +#ifndef NO_BZIP2_SUPPORT + if (memcmp(buf, BZIP2_MAGIC, 3) == 0 && + buf[3] >= '0' && buf[3] <= '9') + return FT_BZIP2; + else +#endif +#ifndef NO_COMPRESS_SUPPORT + if (memcmp(buf, Z_MAGIC, 2) == 0) + return FT_Z; + else +#endif +#ifndef NO_PACK_SUPPORT + if (memcmp(buf, PACK_MAGIC, 2) == 0) + return FT_PACK; + else +#endif +#ifndef NO_XZ_SUPPORT + if (memcmp(buf, XZ_MAGIC, 4) == 0) /* XXX: We only have 4 bytes */ + return FT_XZ; + else +#endif + return FT_UNKNOWN; +} + +#ifndef SMALL +/* check the outfile is OK. */ +static int +check_outfile(const char *outfile) +{ + struct stat sb; + int ok = 1; + + if (lflag == 0 && stat(outfile, &sb) == 0) { + if (fflag) + unlink(outfile); +// else if (isatty(fileno(thread_stdin))) { + else if (ios_isatty(STDIN_FILENO)) { + char ans[10] = { 'n', '\0' }; /* default */ + + fprintf(thread_stderr, "%s already exists -- do you wish to " + "overwrite (y or n)? " , outfile); + fflush(thread_stderr); + (void)fgets(ans, sizeof(ans) - 1, thread_stdin); + if (ans[0] != 'y' && ans[0] != 'Y') { + fprintf(thread_stderr, "\tnot overwriting\n"); + ok = 0; + } else + unlink(outfile); + } else { + maybe_warnx("%s already exists -- skipping", outfile); + ok = 0; + } + } + return ok; +} + +static void +unlink_input(const char *file, const struct stat *sb) +{ + struct stat nsb; + + if (kflag) + return; + bzero(&nsb, sizeof(nsb)); + if (stat(file, &nsb) != 0) + /* Must be gone already */ + return; + if (nsb.st_dev != sb->st_dev || nsb.st_ino != sb->st_ino) + /* Definitely a different file */ + return; + unlink(file); +} + +static void +sigint_handler(int signo __unused) +{ + + if (remove_file != NULL) + unlink(remove_file); + _exit(2); +} +#endif + +static const suffixes_t * +check_suffix(char *file, int xlate) +{ + const suffixes_t *s; + int len = strlen(file); + char *sp; + + for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) { + /* if it doesn't fit in "a.suf", don't bother */ + if (s->ziplen >= len) + continue; + sp = file + len - s->ziplen; + if (strcmp(s->zipped, sp) != 0) + continue; + if (xlate) + strcpy(sp, s->normal); + return s; + } + return NULL; +} + +#ifdef __APPLE__ +static void +clear_type_and_creator(int fd) +{ + struct attrlist alist; + struct { + u_int32_t length; + char info[32]; + } abuf; + + memset(&alist, 0, sizeof(alist)); + alist.bitmapcount = ATTR_BIT_MAP_COUNT; + alist.commonattr = ATTR_CMN_FNDRINFO; + + if (!fgetattrlist(fd, &alist, &abuf, sizeof(abuf), 0) && abuf.length == sizeof(abuf)) { + memset(abuf.info, 0, 8); + fsetattrlist(fd, &alist, abuf.info, sizeof(abuf.info), 0); + } +} +#endif /* __APPLE__ */ + +/* + * compress the given file: create a corresponding .gz file and remove the + * original. + */ +static off_t +file_compress(char *file, char *outfile, size_t outsize) +{ + int in; + int out; + off_t size, insize; +#ifndef SMALL + struct stat isb, osb; + const suffixes_t *suff; +#endif + + in = open(file, O_RDONLY); + if (in == -1) { + maybe_warn("can't open %s", file); + return (-1); + } + +#ifndef SMALL + bzero(&isb, sizeof(isb)); + if (fstat(in, &isb) != 0) { + maybe_warn("couldn't stat: %s", file); + close(in); + return (-1); + } +#endif + + if (cflag == 0) { +#ifndef SMALL + if (isb.st_nlink > 1 && fflag == 0) { + maybe_warnx("%s has %d other link%s -- skipping", + file, isb.st_nlink - 1, + (isb.st_nlink - 1) == 1 ? "" : "s"); + close(in); + return (-1); + } + + if (fflag == 0 && (suff = check_suffix(file, 0)) && + suff->zipped[0] != 0) { + maybe_warnx("%s already has %s suffix -- unchanged", + file, suff->zipped); + close(in); + return (-1); + } +#endif + + /* Add (usually) .gz to filename */ + if ((size_t)snprintf(outfile, outsize, "%s%s", + file, suffixes[0].zipped) >= outsize) + memcpy(outfile + outsize - suffixes[0].ziplen - 1, + suffixes[0].zipped, suffixes[0].ziplen + 1); + +#ifndef SMALL + if (check_outfile(outfile) == 0) { + close(in); + return (-1); + } +#endif + } + + if (cflag == 0) { + out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600); + if (out == -1) { + maybe_warn("could not create output: %s", outfile); + fclose(thread_stdin); + return (-1); + } +#ifndef SMALL + remove_file = outfile; +#endif + } else + out = fileno(thread_stdout); + + insize = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime); + +#ifndef __APPLE__ + (void)close(in); +#endif /* !__APPLE__ */ + + /* + * If there was an error, insize will be -1. + * If we compressed to stdout, just return the size. + * Otherwise stat the file and check it is the correct size. + * We only blow away the file if we can stat the output and it + * has the expected size. + */ + if (cflag != 0) + return (insize == -1 ? -1 : size); + +#ifndef SMALL + if (fstat(out, &osb) != 0) { + maybe_warn("couldn't stat: %s", outfile); + goto bad_outfile; + } + + if (osb.st_size != size) { + maybe_warnx("output file: %s wrong size (%ju != %ju), deleting", + outfile, (uintmax_t)osb.st_size, (uintmax_t)size); + goto bad_outfile; + } + +#ifdef __APPLE__ + fcopyfile(in, out, 0, COPYFILE_ACL | COPYFILE_XATTR); + clear_type_and_creator(out); +#endif /* __APPLE__ */ + copymodes(out, &isb, outfile); + remove_file = NULL; +#endif +#ifdef __APPLE__ + (void)close(in); +#endif /* __APPLE__ */ + if (close(out) == -1) + maybe_warn("couldn't close output"); + + /* output is good, ok to delete input */ + unlink_input(file, &isb); + return (size); + +#ifndef SMALL + bad_outfile: + if (close(out) == -1) + maybe_warn("couldn't close output"); + + maybe_warnx("leaving original %s", file); + unlink(outfile); + return (size); +#endif +} + +/* uncompress the given file and remove the original */ +static off_t +file_uncompress(char *file, char *outfile, size_t outsize) +{ + struct stat isb, osb; + off_t size; + ssize_t rbytes; + unsigned char header1[4]; + enum filetype method; + int fd, ofd, zfd = -1; +#ifndef SMALL + ssize_t rv; + time_t timestamp = 0; + char name[PATH_MAX + 1]; +#endif + + /* gather the old name info */ + + fd = open(file, O_RDONLY); + if (fd < 0) { + maybe_warn("can't open %s", file); + goto lose; + } + + strlcpy(outfile, file, outsize); + if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) { + maybe_warnx("%s: unknown suffix -- ignored", file); + goto lose; + } + + rbytes = read(fd, header1, sizeof header1); + if (rbytes != sizeof header1) { + /* we don't want to fail here. */ +#ifndef SMALL + if (fflag) + goto lose; +#endif + if (rbytes == -1) + maybe_warn("can't read %s", file); + else + goto unexpected_EOF; + goto lose; + } + + method = file_gettype(header1); +#ifndef SMALL + if (fflag == 0 && method == FT_UNKNOWN) { + maybe_warnx("%s: not in gzip format", file); + goto lose; + } + +#endif + +#ifndef SMALL + if (method == FT_GZIP && Nflag) { + unsigned char ts[4]; /* timestamp */ + + rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP); + if (rv >= 0 && rv < (ssize_t)(sizeof ts)) + goto unexpected_EOF; + if (rv == -1) { + if (!fflag) + maybe_warn("can't read %s", file); + goto lose; + } + timestamp = ts[3] << 24 | ts[2] << 16 | ts[1] << 8 | ts[0]; + + if (header1[3] & ORIG_NAME) { + rbytes = pread(fd, name, sizeof(name) - 1, GZIP_ORIGNAME); + if (rbytes < 0) { + maybe_warn("can't read %s", file); + goto lose; + } + if (name[0] != '\0') { + char *dp, *nf; + + /* Make sure that name is NUL-terminated */ + name[rbytes] = '\0'; + + /* strip saved directory name */ + nf = strrchr(name, '/'); + if (nf == NULL) + nf = name; + else + nf++; + + /* preserve original directory name */ + dp = strrchr(file, '/'); + if (dp == NULL) + dp = file; + else + dp++; + snprintf(outfile, outsize, "%.*s%.*s", + (int) (dp - file), + file, (int) rbytes, nf); + } + } + } +#endif + lseek(fd, 0, SEEK_SET); + bzero(&isb, sizeof(isb)); + if (cflag == 0 || lflag) { + if (fstat(fd, &isb) != 0) + goto lose; +#ifndef SMALL + if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) { + maybe_warnx("%s has %d other links -- skipping", + file, isb.st_nlink - 1); + goto lose; + } + if (nflag == 0 && timestamp) + isb.st_mtime = timestamp; + if (check_outfile(outfile) == 0) + goto lose; +#endif + } + + if (cflag == 0 && lflag == 0) { + zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600); + if (zfd == fileno(thread_stdout)) { + /* We won't close STDOUT_FILENO later... */ + zfd = dup(zfd); + close(fileno(thread_stdout)); + } + if (zfd == -1) { + maybe_warn("can't open %s", outfile); + goto lose; + } +#ifndef SMALL + remove_file = outfile; +#endif + } else + zfd = fileno(thread_stdout); + + switch (method) { +#ifndef NO_BZIP2_SUPPORT + case FT_BZIP2: + /* XXX */ + if (lflag) { + maybe_warnx("no -l with bzip2 files"); + goto lose; + } + + size = unbzip2(fd, zfd, NULL, 0, NULL); + break; +#endif + +#ifndef NO_COMPRESS_SUPPORT + case FT_Z: { + FILE *in, *out; + + /* XXX */ + if (lflag) { + maybe_warnx("no -l with Lempel-Ziv files"); + goto lose; + } + + if ((in = zdopen(fd)) == NULL) { + maybe_warn("zdopen for read: %s", file); + goto lose; + } + + out = fdopen(dup(zfd), "w"); + if (out == NULL) { + maybe_warn("fdopen for write: %s", outfile); + fclose(in); + goto lose; + } + + size = zuncompress(in, out, NULL, 0, NULL); + /* need to fclose() if ferror() is true... */ + if (ferror(in) | fclose(in)) { + maybe_warn("failed infile fclose"); + unlink(outfile); + (void)fclose(out); + } + if (fclose(out) != 0) { + maybe_warn("failed outfile fclose"); + unlink(outfile); + goto lose; + } + break; + } +#endif + +#ifndef NO_PACK_SUPPORT + case FT_PACK: + if (lflag) { + maybe_warnx("no -l with packed files"); + goto lose; + } + + size = unpack(fd, zfd, NULL, 0, NULL); + break; +#endif + +#ifndef NO_XZ_SUPPORT + case FT_XZ: + if (lflag) { + maybe_warnx("no -l with xz files"); + goto lose; + } + + size = unxz(fd, zfd, NULL, 0, NULL); + break; +#endif + +#ifndef SMALL + case FT_UNKNOWN: + if (lflag) { + maybe_warnx("no -l for unknown filetypes"); + goto lose; + } + size = cat_fd(NULL, 0, NULL, fd); + break; +#endif + default: + if (lflag) { + print_list(fd, isb.st_size, outfile, isb.st_mtime); + close(fd); + return -1; /* XXX */ + } + + size = gz_uncompress(fd, zfd, NULL, 0, NULL, file); + break; + } + + if (close(fd) != 0) + maybe_warn("couldn't close input"); + if (zfd != fileno(thread_stdout) && close(zfd) != 0) + maybe_warn("couldn't close output"); + + if (size == -1) { + if (cflag == 0) + unlink(outfile); + maybe_warnx("%s: uncompress failed", file); + return -1; + } + + /* if testing, or we uncompressed to stdout, this is all we need */ +#ifndef SMALL + if (tflag) + return size; +#endif + /* if we are uncompressing to stdin, don't remove the file. */ + if (cflag) + return size; + + /* + * if we create a file... + */ + /* + * if we can't stat the file don't remove the file. + */ + + ofd = open(outfile, O_RDWR, 0); + if (ofd == -1) { + maybe_warn("couldn't open (leaving original): %s", + outfile); + return -1; + } + if (fstat(ofd, &osb) != 0) { + maybe_warn("couldn't stat (leaving original): %s", + outfile); + close(ofd); + return -1; + } + if (osb.st_size != size) { + maybe_warnx("stat gave different size: %ju != %ju (leaving original)", + (uintmax_t)size, (uintmax_t)osb.st_size); + close(ofd); + unlink(outfile); + return -1; + } +#ifndef SMALL + copymodes(ofd, &isb, outfile); + remove_file = NULL; +#endif + close(ofd); + unlink_input(file, &isb); + return size; + + unexpected_EOF: + maybe_warnx("%s: unexpected end of file", file); + lose: + if (fd != -1) + close(fd); + if (zfd != -1 && zfd != fileno(thread_stdout)) + close(fd); + return -1; +} + +#ifndef SMALL +static off_t +cat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd) +{ + char buf[BUFLEN]; + off_t in_tot; + ssize_t w; + + in_tot = count; + // w = write(fileno(thread_stdout), prepend, count); + w = fwrite(prepend, 1, count, thread_stdout); + if (w == -1 || (size_t)w != count) { + maybe_warn("write to stdout"); + return -1; + } + for (;;) { + ssize_t rv; + + rv = read(fd, buf, sizeof buf); + if (rv == 0) + break; + if (rv < 0) { + maybe_warn("read from fd %d", fd); + break; + } + +// if (write(fileno(thread_stdout), buf, rv) != rv) { + if (fwrite(buf, 1, rv, thread_stdout) != rv) { + maybe_warn("write to stdout"); + break; + } + in_tot += rv; + } + + if (gsizep) + *gsizep = in_tot; + return (in_tot); +} +#endif + +static void +handle_stdin(void) +{ + unsigned char header1[4]; + off_t usize, gsize; + enum filetype method; + ssize_t bytes_read; +#ifndef NO_COMPRESS_SUPPORT + FILE *in; +#endif + +#ifndef SMALL +// if (fflag == 0 && lflag == 0 && isatty(fileno(thread_stdin))) { + if (fflag == 0 && lflag == 0 && (ios_isatty(STDIN_FILENO))) { + maybe_warnx("standard input is a terminal -- ignoring"); + return; + } +#endif + + if (lflag) { + struct stat isb; + + /* XXX could read the whole file, etc. */ + if (fstat(fileno(thread_stdin), &isb) < 0) { + maybe_warn("fstat"); + return; + } + print_list(fileno(thread_stdin), isb.st_size, "stdout", isb.st_mtime); + return; + } + + bytes_read = read_retry(fileno(thread_stdin), header1, sizeof header1); + if (bytes_read == -1) { + maybe_warn("can't read stdin"); + return; + } else if (bytes_read != sizeof(header1)) { + maybe_warnx("(stdin): unexpected end of file"); + return; + } + + method = file_gettype(header1); + switch (method) { + default: +#ifndef SMALL + if (fflag == 0) { + maybe_warnx("unknown compression format"); + return; + } + usize = cat_fd(header1, sizeof header1, &gsize, fileno(thread_stdin)); + break; +#endif + case FT_GZIP: + usize = gz_uncompress(fileno(thread_stdin), fileno(thread_stdout), + (char *)header1, sizeof header1, &gsize, "(stdin)"); + break; +#ifndef NO_BZIP2_SUPPORT + case FT_BZIP2: + usize = unbzip2(fileno(thread_stdin), fileno(thread_stdout), + (char *)header1, sizeof header1, &gsize); + break; +#endif +#ifndef NO_COMPRESS_SUPPORT + case FT_Z: + if ((in = zdopen(fileno(thread_stdin))) == NULL) { + maybe_warnx("zopen of stdin"); + return; + } + + usize = zuncompress(in, thread_stdout, (char *)header1, + sizeof header1, &gsize); + fclose(in); + break; +#endif +#ifndef NO_PACK_SUPPORT + case FT_PACK: + usize = unpack(fileno(thread_stdin), fileno(thread_stdout), + (char *)header1, sizeof header1, &gsize); + break; +#endif +#ifndef NO_XZ_SUPPORT + case FT_XZ: + usize = unxz(fileno(thread_stdin), fileno(thread_stdout), + (char *)header1, sizeof header1, &gsize); + break; +#endif + } + +#ifndef SMALL + if (vflag && !tflag && usize != -1 && gsize != -1) + print_verbage(NULL, NULL, usize, gsize); + if (vflag && tflag) + print_test("(stdin)", usize != -1); +#endif + +} + +static void +handle_stdout(void) +{ + off_t gsize, usize; + struct stat sb; + time_t systime; + uint32_t mtime; + int ret; + +#ifndef SMALL +// if (fflag == 0 && isatty(fileno(thread_stdout))) { + if (fflag == 0 && (ios_isatty(STDOUT_FILENO))) { + maybe_warnx("standard output is a terminal -- ignoring"); + return; + } +#endif + /* If stdin is a file use its mtime, otherwise use current time */ + ret = fstat(fileno(thread_stdin), &sb); + +#ifndef SMALL + if (ret < 0) { + maybe_warn("Can't stat stdin"); + return; + } +#endif + + if (S_ISREG(sb.st_mode)) + mtime = (uint32_t)sb.st_mtime; + else { + systime = time(NULL); +#ifndef SMALL + if (systime == -1) { + maybe_warn("time"); + return; + } +#endif + mtime = (uint32_t)systime; + } + + usize = gz_compress(fileno(thread_stdin), fileno(thread_stdout), &gsize, "", mtime); +#ifndef SMALL + if (vflag && !tflag && usize != -1 && gsize != -1) + print_verbage(NULL, NULL, usize, gsize); +#endif +} + +/* do what is asked for, for the path name */ +static void +handle_pathname(char *path) +{ + char *opath = path, *s = NULL; + ssize_t len; + int slen; + struct stat sb; + + /* check for stdout/stdin */ + if (path[0] == '-' && path[1] == '\0') { + if (dflag) + handle_stdin(); + else + handle_stdout(); + return; + } + +#ifdef __APPLE__ + if (zcat && COMPAT_MODE("bin/zcat", "Unix2003")) { + char *suffix = strrchr(path, '.'); + if (suffix == NULL || strcmp(suffix, Z_SUFFIX) != 0) { + len = strlen(path); + slen = sizeof(Z_SUFFIX) - 1; + s = malloc(len + slen + 1); + memcpy(s, path, len); + memcpy(s + len, Z_SUFFIX, slen + 1); + path = s; + } + } +#endif + +retry: + if (stat(path, &sb) != 0 || (fflag == 0 && cflag == 0 && + lstat(path, &sb) != 0)) { + /* lets try .gz if we're decompressing */ + if (dflag && s == NULL && errno == ENOENT) { + len = strlen(path); + slen = suffixes[0].ziplen; + s = malloc(len + slen + 1); + if (s == NULL) + maybe_err("malloc"); + memcpy(s, path, len); + memcpy(s + len, suffixes[0].zipped, slen + 1); + path = s; + goto retry; + } +#ifdef __APPLE__ + /* Include actual path for clarity. */ + maybe_warn("can't stat: %s (%s)", opath, path); +#else + maybe_warn("can't stat: %s", opath); +#endif + goto out; + } + + if (S_ISDIR(sb.st_mode)) { +#ifndef SMALL + if (rflag) + handle_dir(path); + else +#endif + maybe_warnx("%s is a directory", path); + goto out; + } + + if (S_ISREG(sb.st_mode)) + handle_file(path, &sb); + else + maybe_warnx("%s is not a regular file", path); + +out: + if (s) + free(s); +} + +/* compress/decompress a file */ +static void +handle_file(char *file, struct stat *sbp) +{ + off_t usize, gsize; + char outfile[PATH_MAX]; + + infile = file; + if (dflag) { + usize = file_uncompress(file, outfile, sizeof(outfile)); +#ifndef SMALL + if (vflag && tflag) + print_test(file, usize != -1); +#endif + if (usize == -1) + return; + gsize = sbp->st_size; + } else { + gsize = file_compress(file, outfile, sizeof(outfile)); + if (gsize == -1) + return; + usize = sbp->st_size; + } + + +#ifndef SMALL + if (vflag && !tflag) + print_verbage(file, (cflag) ? NULL : outfile, usize, gsize); +#endif +} + +#ifndef SMALL +/* this is used with -r to recursively descend directories */ +static void +handle_dir(char *dir) +{ + char *path_argv[2]; + FTS *fts; + FTSENT *entry; + + path_argv[0] = dir; + path_argv[1] = 0; + fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL); + if (fts == NULL) { + warn("couldn't fts_open %s", dir); + return; + } + + while ((entry = fts_read(fts))) { + switch(entry->fts_info) { + case FTS_D: + case FTS_DP: + continue; + + case FTS_DNR: + case FTS_ERR: + case FTS_NS: + maybe_warn("%s", entry->fts_path); + continue; + case FTS_F: + handle_file(entry->fts_path, entry->fts_statp); + } + } + (void)fts_close(fts); +} +#endif + +/* print a ratio - size reduction as a fraction of uncompressed size */ +static void +print_ratio(off_t in, off_t out, FILE *where) +{ + int percent10; /* 10 * percent */ + off_t diff; + char buff[8]; + int len; + + diff = in - out/2; + if (diff <= 0) + /* + * Output is more than double size of input! print -99.9% + * Quite possibly we've failed to get the original size. + */ + percent10 = -999; + else { + /* + * We only need 12 bits of result from the final division, + * so reduce the values until a 32bit division will suffice. + */ + while (in > 0x100000) { + diff >>= 1; + in >>= 1; + } + if (in != 0) + percent10 = ((u_int)diff * 2000) / (u_int)in - 1000; + else + percent10 = 0; + } + + len = snprintf(buff, sizeof buff, "%2.2d.", percent10); + /* Move the '.' to before the last digit */ + buff[len - 1] = buff[len - 2]; + buff[len - 2] = '.'; + fprintf(where, "%5s%%", buff); +} + +#ifndef SMALL +/* print compression statistics, and the new name (if there is one!) */ +static void +print_verbage(const char *file, const char *nfile, off_t usize, off_t gsize) +{ + if (file) + fprintf(thread_stderr, "%s:%s ", file, + strlen(file) < 7 ? "\t\t" : "\t"); + print_ratio(usize, gsize, thread_stderr); + if (nfile) + fprintf(thread_stderr, " -- replaced with %s", nfile); + fprintf(thread_stderr, "\n"); + fflush(thread_stderr); +} + +/* print test results */ +static void +print_test(const char *file, int ok) +{ + + if (exit_value == 0 && ok == 0) + exit_value = 1; + fprintf(thread_stderr, "%s:%s %s\n", file, + strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK"); + fflush(thread_stderr); +} +#endif + +/* print a file's info ala --list */ +/* eg: + compressed uncompressed ratio uncompressed_name + 354841 1679360 78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar +*/ +static void +print_list(int fd, off_t out, const char *outfile, time_t ts) +{ + static int first = 1; +#ifndef SMALL + static off_t in_tot, out_tot; + uint32_t crc = 0; +#endif + off_t in = 0, rv; + + if (first) { +#ifndef SMALL + if (vflag) + fprintf(thread_stdout, "method crc date time "); +#endif + if (qflag == 0) + fprintf(thread_stdout, " compressed uncompressed " + "ratio uncompressed_name\n"); + } + first = 0; + + /* print totals? */ +#ifndef SMALL + if (fd == -1) { + in = in_tot; + out = out_tot; + } else +#endif + { + /* read the last 4 bytes - this is the uncompressed size */ + rv = lseek(fd, (off_t)(-8), SEEK_END); + if (rv != -1) { + unsigned char buf[8]; + uint32_t usize; + + rv = read(fd, (char *)buf, sizeof(buf)); + if (rv == -1) + maybe_warn("read of uncompressed size"); + else if (rv != sizeof(buf)) + maybe_warnx("read of uncompressed size"); + + else { + usize = buf[4] | buf[5] << 8 | + buf[6] << 16 | buf[7] << 24; + in = (off_t)usize; +#ifndef SMALL + crc = buf[0] | buf[1] << 8 | + buf[2] << 16 | buf[3] << 24; +#endif + } + } + } + +#ifndef SMALL + if (vflag && fd == -1) + fprintf(thread_stdout, " "); + else if (vflag) { + char *date = ctime(&ts); + + /* skip the day, 1/100th second, and year */ + date += 4; + date[12] = 0; + fprintf(thread_stdout, "%5s %08x %11s ", "defla"/*XXX*/, crc, date); + } + in_tot += in; + out_tot += out; +#else + (void)&ts; /* XXX */ +#endif + fprintf(thread_stdout, "%12llu %12llu ", (unsigned long long)out, (unsigned long long)in); + print_ratio(in, out, thread_stdout); + fprintf(thread_stdout, " %s\n", outfile); +} + +/* display the usage of NetBSD gzip */ +static void +usage(void) +{ + + fprintf(thread_stderr, "%s\n", gzip_version); + fprintf(thread_stderr, +#ifdef SMALL + "usage: %s [-" OPT_LIST "] [ [ ...]]\n", +#else + "usage: %s [-123456789acdfhklLNnqrtVv] [-S .suffix] [ [ ...]]\n" + " -1 --fast fastest (worst) compression\n" + " -2 .. -8 set compression level\n" + " -9 --best best (slowest) compression\n" + " -c --stdout write to stdout, keep original files\n" + " --to-stdout\n" + " -d --decompress uncompress files\n" + " --uncompress\n" + " -f --force force overwriting & compress links\n" + " -h --help display this help\n" + " -k --keep don't delete input files during operation\n" + " -l --list list compressed file contents\n" + " -N --name save or restore original file name and time stamp\n" + " -n --no-name don't save original file name or time stamp\n" + " -q --quiet output no warnings\n" + " -r --recursive recursively compress files in directories\n" + " -S .suf use suffix .suf instead of .gz\n" + " --suffix .suf\n" + " -t --test test compressed file\n" + " -V --version display program version\n" + " -v --verbose print extra statistics\n", +#endif + ios_progname()); + exit(0); +} + +#ifndef SMALL +/* display the license information of FreeBSD gzip */ +static void +display_license(void) +{ + +#ifdef __APPLE__ + fprintf(thread_stderr, "%s (based on FreeBSD gzip 20150113)\n", gzip_version); +#else + fprintf(thread_stderr, "%s (based on NetBSD gzip 20150113)\n", gzip_version); +#endif + fprintf(thread_stderr, "%s\n", gzip_copyright); + exit(0); +} +#endif + +/* display the version of NetBSD gzip */ +static void +display_version(void) +{ + + fprintf(thread_stderr, "%s\n", gzip_version); + exit(0); +} + +#ifndef NO_BZIP2_SUPPORT +#include "unbzip2.c" +#endif +#ifndef NO_COMPRESS_SUPPORT +#include "zuncompress.c" +#endif +#ifndef NO_PACK_SUPPORT +#include "unpack.c" +#endif +#ifndef NO_XZ_SUPPORT +#include "unxz.c" +#endif + +static ssize_t +read_retry(int fd, void *buf, size_t sz) +{ + char *cp = buf; + size_t left = MIN(sz, (size_t) SSIZE_MAX); + + while (left > 0) { + ssize_t ret; + + ret = read(fd, cp, left); + if (ret == -1) { + return ret; + } else if (ret == 0) { + break; /* EOF */ + } + cp += ret; + left -= ret; + } + + return sz - left; +} diff --git a/files/Sources/files/gzip/unbzip2.c b/files/Sources/files/gzip/unbzip2.c new file mode 100644 index 00000000..4192e9bc --- /dev/null +++ b/files/Sources/files/gzip/unbzip2.c @@ -0,0 +1,141 @@ +/* $NetBSD: unbzip2.c,v 1.13 2009/12/05 03:23:37 mrg Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Simon Burge. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + * + * $FreeBSD: src/usr.bin/gzip/unbzip2.c,v 1.5 2010/04/07 22:54:53 delphij Exp $ + */ + +/* This file is #included by gzip.c */ + +static off_t +unbzip2(int in, int out, char *pre, size_t prelen, off_t *bytes_in) +{ + int ret, end_of_file, cold = 0; + off_t bytes_out = 0; + bz_stream bzs; + static char *inbuf, *outbuf; + + if (inbuf == NULL) + inbuf = malloc(BUFLEN); + if (outbuf == NULL) + outbuf = malloc(BUFLEN); + if (inbuf == NULL || outbuf == NULL) + maybe_err("malloc"); + + bzs.bzalloc = NULL; + bzs.bzfree = NULL; + bzs.opaque = NULL; + + end_of_file = 0; + ret = BZ2_bzDecompressInit(&bzs, 0, 0); + if (ret != BZ_OK) + maybe_errx("bzip2 init"); + + /* Prepend. */ + bzs.avail_in = prelen; + bzs.next_in = pre; + + if (bytes_in) + *bytes_in = prelen; + + while (ret == BZ_OK) { + if (bzs.avail_in == 0 && !end_of_file) { + ssize_t n; + + n = read(in, inbuf, BUFLEN); + if (n < 0) + maybe_err("read"); + if (n == 0) + end_of_file = 1; + bzs.next_in = inbuf; + bzs.avail_in = n; + if (bytes_in) + *bytes_in += n; + } + + bzs.next_out = outbuf; + bzs.avail_out = BUFLEN; + ret = BZ2_bzDecompress(&bzs); + + switch (ret) { + case BZ_STREAM_END: + case BZ_OK: + if (ret == BZ_OK && end_of_file) { + /* + * If we hit this after a stream end, consider + * it as the end of the whole file and don't + * bail out. + */ + if (cold == 1) + ret = BZ_STREAM_END; + else + maybe_errx("truncated file"); + } + cold = 0; + if (!tflag && bzs.avail_out != BUFLEN) { + ssize_t n; + + n = write(out, outbuf, BUFLEN - bzs.avail_out); + if (n < 0) + maybe_err("write"); + bytes_out += n; + } + if (ret == BZ_STREAM_END && !end_of_file) { + if (BZ2_bzDecompressEnd(&bzs) != BZ_OK || + BZ2_bzDecompressInit(&bzs, 0, 0) != BZ_OK) + maybe_errx("bzip2 re-init"); + cold = 1; + ret = BZ_OK; + } + break; + + case BZ_DATA_ERROR: + maybe_warnx("bzip2 data integrity error"); + break; + + case BZ_DATA_ERROR_MAGIC: + maybe_warnx("bzip2 magic number error"); + break; + + case BZ_MEM_ERROR: + maybe_warnx("bzip2 out of memory"); + break; + + default: + maybe_warnx("unknown bzip2 error: %d", ret); + break; + } + } + + if (ret != BZ_STREAM_END || BZ2_bzDecompressEnd(&bzs) != BZ_OK) + return (-1); + + return (bytes_out); +} + diff --git a/files/Sources/files/gzip/unpack.c b/files/Sources/files/gzip/unpack.c new file mode 100644 index 00000000..dbad5882 --- /dev/null +++ b/files/Sources/files/gzip/unpack.c @@ -0,0 +1,329 @@ +/*- + * Copyright (c) 2009 Xin LI + * 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. + * + * $FreeBSD: src/usr.bin/gzip/unpack.c,v 1.3 2010/10/16 15:24:04 bcr Exp $ + */ + +/* This file is #included by gzip.c */ + +/* + * pack(1) file format: + * + * The first 7 bytes is the header: + * 00, 01 - Signature (US, RS), we already validated it earlier. + * 02..05 - Uncompressed size + * 06 - Level for the huffman tree (<=24) + * + * pack(1) will then store symbols (leaf) nodes count in each huffman + * tree levels, each level would consume 1 byte (See [1]). + * + * After the symbol count table, there is the symbol table, storing + * symbols represented by corresponding leaf node. EOB is not being + * explicitly transmitted (not necessary anyway) in the symbol table. + * + * Compressed data goes after the symbol table. + * + * NOTES + * + * [1] If we count EOB into the symbols, that would mean that we will + * have at most 256 symbols in the huffman tree. pack(1) rejects empty + * file and files that just repeats one character, which means that we + * will have at least 2 symbols. Therefore, pack(1) would reduce the + * last level symbol count by 2 which makes it a number in + * range [0..254], so all levels' symbol count would fit into 1 byte. + */ + +#define PACK_HEADER_LENGTH 7 +#define HTREE_MAXLEVEL 24 + +/* + * unpack descriptor + * + * Represent the huffman tree in a similar way that pack(1) would + * store in a packed file. We store all symbols in a linear table, + * and store pointers to each level's first symbol. In addition to + * that, maintain two counts for each level: inner nodes count and + * leaf nodes count. + */ +typedef struct { + int symbol_size; /* Size of the symbol table */ + int treelevels; /* Levels for the huffman tree */ + + int *symbolsin; /* Table of leaf symbols count in + each level */ + int *inodesin; /* Table of internal nodes count in + each level */ + + char *symbol; /* The symbol table */ + char *symbol_eob; /* Pointer to the EOB symbol */ + char **tree; /* Decoding huffman tree (pointers to + first symbol of each tree level */ + + off_t uncompressed_size; /* Uncompressed size */ + FILE *fpIn; /* Input stream */ + FILE *fpOut; /* Output stream */ +} unpack_descriptor_t; + +/* + * Release resource allocated to an unpack descriptor. + * + * Caller is responsible to make sure that all of these pointers are + * initialized (in our case, they all point to valid memory block). + * We don't zero out pointers here because nobody else would ever + * reference the memory block without scrubbing them. + */ +static void +unpack_descriptor_fini(unpack_descriptor_t *unpackd) +{ + + free(unpackd->symbolsin); + free(unpackd->inodesin); + free(unpackd->symbol); + free(unpackd->tree); + + fclose(unpackd->fpIn); + fclose(unpackd->fpOut); +} + +/* + * Recursively fill the internal node count table + */ +static void +unpackd_fill_inodesin(const unpack_descriptor_t *unpackd, int level) +{ + + /* + * The internal nodes would be 1/2 of total internal nodes and + * leaf nodes in the next level. For the last level there + * would be no internal node by definition. + */ + if (level < unpackd->treelevels) { + unpackd_fill_inodesin(unpackd, level + 1); + unpackd->inodesin[level] = (unpackd->inodesin[level + 1] + + unpackd->symbolsin[level + 1]) / 2; + } else + unpackd->inodesin[level] = 0; +} + +/* + * Update counter for accepted bytes + */ +static void +accepted_bytes(off_t *bytes_in, off_t newbytes) +{ + + if (bytes_in != NULL) + (*bytes_in) += newbytes; +} + +/* + * Read file header and construct the tree. Also, prepare the buffered I/O + * for decode routine. + * + * Return value is uncompressed size. + */ +static void +unpack_parse_header(int in, int out, char *pre, size_t prelen, off_t *bytes_in, + unpack_descriptor_t *unpackd) +{ + unsigned char hdr[PACK_HEADER_LENGTH]; /* buffer for header */ + ssize_t bytesread; /* Bytes read from the file */ + int i, j, thisbyte; + + /* Prepend the header buffer if we already read some data */ + if (prelen != 0) + memcpy(hdr, pre, prelen); + + /* Read in and fill the rest bytes of header */ + bytesread = read(in, hdr + prelen, PACK_HEADER_LENGTH - prelen); + if (bytesread < 0) + maybe_err("Error reading pack header"); + + accepted_bytes(bytes_in, PACK_HEADER_LENGTH); + + /* Obtain uncompressed length (bytes 2,3,4,5)*/ + unpackd->uncompressed_size = 0; + for (i = 2; i <= 5; i++) { + unpackd->uncompressed_size <<= 8; + unpackd->uncompressed_size |= hdr[i]; + } + + /* Get the levels of the tree */ + unpackd->treelevels = hdr[6]; + if (unpackd->treelevels > HTREE_MAXLEVEL || unpackd->treelevels < 1) + maybe_errx("Huffman tree has insane levels"); + + /* Let libc take care for buffering from now on */ + if ((unpackd->fpIn = fdopen(in, "r")) == NULL) + maybe_err("Can not fdopen() input stream"); + if ((unpackd->fpOut = fdopen(out, "w")) == NULL) + maybe_err("Can not fdopen() output stream"); + + /* Allocate for the tables of bounds and the tree itself */ + unpackd->inodesin = + calloc(unpackd->treelevels, sizeof(*(unpackd->inodesin))); + unpackd->symbolsin = + calloc(unpackd->treelevels, sizeof(*(unpackd->symbolsin))); + unpackd->tree = + calloc(unpackd->treelevels, (sizeof (*(unpackd->tree)))); + if (unpackd->inodesin == NULL || unpackd->symbolsin == NULL || + unpackd->tree == NULL) + maybe_err("calloc"); + + /* We count from 0 so adjust to match array upper bound */ + unpackd->treelevels--; + + /* Read the levels symbol count table and calculate total */ + unpackd->symbol_size = 1; /* EOB */ + for (i = 0; i <= unpackd->treelevels; i++) { + if ((thisbyte = fgetc(unpackd->fpIn)) == EOF) + maybe_err("File appears to be truncated"); + unpackd->symbolsin[i] = (unsigned char)thisbyte; + unpackd->symbol_size += unpackd->symbolsin[i]; + } + accepted_bytes(bytes_in, unpackd->treelevels); + if (unpackd->symbol_size > 256) + maybe_errx("Bad symbol table"); + + /* Allocate for the symbol table, point symbol_eob at the beginning */ + unpackd->symbol_eob = unpackd->symbol = calloc(1, unpackd->symbol_size); + if (unpackd->symbol == NULL) + maybe_err("calloc"); + + /* + * Read in the symbol table, which contain [2, 256] symbols. + * In order to fit the count in one byte, pack(1) would offset + * it by reducing 2 from the actual number from the last level. + * + * We adjust the last level's symbol count by 1 here, because + * the EOB symbol is not being transmitted explicitly. Another + * adjustment would be done later afterward. + */ + unpackd->symbolsin[unpackd->treelevels]++; + for (i = 0; i <= unpackd->treelevels; i++) { + unpackd->tree[i] = unpackd->symbol_eob; + for (j = 0; j < unpackd->symbolsin[i]; j++) { + if ((thisbyte = fgetc(unpackd->fpIn)) == EOF) + maybe_errx("Symbol table truncated"); + *unpackd->symbol_eob++ = (char)thisbyte; + } + accepted_bytes(bytes_in, unpackd->symbolsin[i]); + } + + /* Now, take account for the EOB symbol as well */ + unpackd->symbolsin[unpackd->treelevels]++; + + /* + * The symbolsin table has been constructed now. + * Calculate the internal nodes count table based on it. + */ + unpackd_fill_inodesin(unpackd, 0); +} + +/* + * Decode huffman stream, based on the huffman tree. + */ +static void +unpack_decode(const unpack_descriptor_t *unpackd, off_t *bytes_in) +{ + int thislevel, thiscode, thisbyte, inlevelindex; + int i; + off_t bytes_out = 0; + const char *thissymbol; /* The symbol pointer decoded from stream */ + + /* + * Decode huffman. Fetch every bytes from the file, get it + * into 'thiscode' bit-by-bit, then output the symbol we got + * when one has been found. + * + * Assumption: sizeof(int) > ((max tree levels + 1) / 8). + * bad things could happen if not. + */ + thislevel = 0; + thiscode = thisbyte = 0; + + while ((thisbyte = fgetc(unpackd->fpIn)) != EOF) { + accepted_bytes(bytes_in, 1); + + /* + * Split one bit from thisbyte, from highest to lowest, + * feed the bit into thiscode, until we got a symbol from + * the tree. + */ + for (i = 7; i >= 0; i--) { + thiscode = (thiscode << 1) | ((thisbyte >> i) & 1); + + /* Did we got a symbol? (referencing leaf node) */ + if (thiscode >= unpackd->inodesin[thislevel]) { + inlevelindex = + thiscode - unpackd->inodesin[thislevel]; + if (inlevelindex > unpackd->symbolsin[thislevel]) + maybe_errx("File corrupt"); + + thissymbol = + &(unpackd->tree[thislevel][inlevelindex]); + if ((thissymbol == unpackd->symbol_eob) && + (bytes_out == unpackd->uncompressed_size)) + goto finished; + + fputc((*thissymbol), unpackd->fpOut); + bytes_out++; + + /* Prepare for next input */ + thislevel = 0; thiscode = 0; + } else { + thislevel++; + if (thislevel > unpackd->treelevels) + maybe_errx("File corrupt"); + } + } + } + +finished: + if (bytes_out != unpackd->uncompressed_size) + maybe_errx("Premature EOF"); +} + +/* Handler for pack(1)'ed file */ +static off_t +unpack(int in, int out, char *pre, size_t prelen, off_t *bytes_in) +{ + unpack_descriptor_t unpackd; + + in = dup(in); + if (in == -1) + maybe_err("dup"); + out = dup(out); + if (out == -1) + maybe_err("dup"); + + unpack_parse_header(in, out, pre, prelen, bytes_in, &unpackd); + unpack_decode(&unpackd, bytes_in); + unpack_descriptor_fini(&unpackd); + + /* If we reached here, the unpack was successful */ + return (unpackd.uncompressed_size); +} + diff --git a/files/Sources/files/gzip/zuncompress.c b/files/Sources/files/gzip/zuncompress.c new file mode 100644 index 00000000..06df5976 --- /dev/null +++ b/files/Sources/files/gzip/zuncompress.c @@ -0,0 +1,396 @@ +/* $NetBSD: zuncompress.c,v 1.11 2011/08/16 13:55:02 joerg Exp $ */ + +/*- + * Copyright (c) 1985, 1986, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis and James A. Woods, derived from original + * work by Spencer Thomas and Joseph Orost. + * + * 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. + * 3. 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. + * + * from: NetBSD: zopen.c,v 1.8 2003/08/07 11:13:29 agc Exp + * $FreeBSD: src/usr.bin/gzip/zuncompress.c,v 1.7 2012/10/19 14:49:42 ed Exp $ + */ + +/* This file is #included by gzip.c */ + +static int zread(void *, char *, int); + +#define tab_prefixof(i) (zs->zs_codetab[i]) +#define tab_suffixof(i) ((char_type *)(zs->zs_htab))[i] +#define de_stack ((char_type *)&tab_suffixof(1 << BITS)) + +#define BITS 16 /* Default bits. */ +#define HSIZE 69001 /* 95% occupancy */ /* XXX may not need HSIZE */ +#define BIT_MASK 0x1f /* Defines for third byte of header. */ +#define BLOCK_MASK 0x80 +#define CHECK_GAP 10000 /* Ratio check interval. */ +#define BUFSIZE (64 * 1024) + +/* + * Masks 0x40 and 0x20 are free. I think 0x20 should mean that there is + * a fourth header byte (for expansion). + */ +#define INIT_BITS 9 /* Initial number of bits/code. */ + +/* + * the next two codes should not be changed lightly, as they must not + * lie within the contiguous general code space. + */ +#define FIRST 257 /* First free entry. */ +#define CLEAR 256 /* Table clear output code. */ + + +#define MAXCODE(n_bits) ((1 << (n_bits)) - 1) + +typedef long code_int; +typedef long count_int; +typedef u_char char_type; + +static char_type magic_header[] = + {'\037', '\235'}; /* 1F 9D */ + +static char_type rmask[9] = + {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; + +static off_t total_compressed_bytes; +static size_t compressed_prelen; +static char *compressed_pre; + +struct s_zstate { + FILE *zs_fp; /* File stream for I/O */ + char zs_mode; /* r or w */ + enum { + S_START, S_MIDDLE, S_EOF + } zs_state; /* State of computation */ + int zs_n_bits; /* Number of bits/code. */ + int zs_maxbits; /* User settable max # bits/code. */ + code_int zs_maxcode; /* Maximum code, given n_bits. */ + code_int zs_maxmaxcode; /* Should NEVER generate this code. */ + count_int zs_htab [HSIZE]; + u_short zs_codetab [HSIZE]; + code_int zs_hsize; /* For dynamic table sizing. */ + code_int zs_free_ent; /* First unused entry. */ + /* + * Block compression parameters -- after all codes are used up, + * and compression rate changes, start over. + */ + int zs_block_compress; + int zs_clear_flg; + long zs_ratio; + count_int zs_checkpoint; + int zs_offset; + long zs_in_count; /* Length of input. */ + long zs_bytes_out; /* Length of compressed output. */ + long zs_out_count; /* # of codes output (for debugging). */ + char_type zs_buf[BITS]; + union { + struct { + long zs_fcode; + code_int zs_ent; + code_int zs_hsize_reg; + int zs_hshift; + } w; /* Write parameters */ + struct { + char_type *zs_stackp; + int zs_finchar; + code_int zs_code, zs_oldcode, zs_incode; + int zs_roffset, zs_size; + char_type zs_gbuf[BITS]; + } r; /* Read parameters */ + } u; +}; + +static code_int getcode(struct s_zstate *zs); + +static off_t +zuncompress(FILE *in, FILE *out, char *pre, size_t prelen, + off_t *compressed_bytes) +{ + off_t bin, bout = 0; + char *buf; + + buf = malloc(BUFSIZE); + if (buf == NULL) + return -1; + + /* XXX */ + compressed_prelen = prelen; + if (prelen != 0) + compressed_pre = pre; + else + compressed_pre = NULL; + + while ((bin = fread(buf, 1, BUFSIZE, in)) != 0) { + if (tflag == 0 && (off_t)fwrite(buf, 1, bin, out) != bin) { + free(buf); + return -1; + } + bout += bin; + } + + if (compressed_bytes) + *compressed_bytes = total_compressed_bytes; + + free(buf); + return bout; +} + +static int +zclose(void *zs) +{ + free(zs); + /* We leave the caller to close the fd passed to zdopen() */ + return 0; +} + +FILE * +zdopen(int fd) +{ + struct s_zstate *zs; + + if ((zs = calloc(1, sizeof(struct s_zstate))) == NULL) + return (NULL); + + zs->zs_state = S_START; + + /* XXX we can get rid of some of these */ + zs->zs_hsize = HSIZE; /* For dynamic table sizing. */ + zs->zs_free_ent = 0; /* First unused entry. */ + zs->zs_block_compress = BLOCK_MASK; + zs->zs_clear_flg = 0; /* XXX we calloc()'d this structure why = 0? */ + zs->zs_ratio = 0; + zs->zs_checkpoint = CHECK_GAP; + zs->zs_in_count = 1; /* Length of input. */ + zs->zs_out_count = 0; /* # of codes output (for debugging). */ + zs->u.r.zs_roffset = 0; + zs->u.r.zs_size = 0; + + /* + * Layering compress on top of stdio in order to provide buffering, + * and ensure that reads and write work with the data specified. + */ + if ((zs->zs_fp = fdopen(fd, "r")) == NULL) { + free(zs); + return NULL; + } + + return funopen(zs, zread, NULL, NULL, zclose); +} + +/* + * Decompress read. This routine adapts to the codes in the file building + * the "string" table on-the-fly; requiring no table to be stored in the + * compressed file. The tables used herein are shared with those of the + * compress() routine. See the definitions above. + */ +static int +zread(void *cookie, char *rbp, int num) +{ + u_int count, i; + struct s_zstate *zs; + u_char *bp, header[3]; + + if (num == 0) + return (0); + + zs = cookie; + count = num; + bp = (u_char *)rbp; + switch (zs->zs_state) { + case S_START: + zs->zs_state = S_MIDDLE; + break; + case S_MIDDLE: + goto middle; + case S_EOF: + goto eof; + } + + /* Check the magic number */ + for (i = 0; i < 3 && compressed_prelen; i++, compressed_prelen--) + header[i] = *compressed_pre++; + + if (fread(header + i, 1, sizeof(header) - i, zs->zs_fp) != + sizeof(header) - i || + memcmp(header, magic_header, sizeof(magic_header)) != 0) { + errno = EFTYPE; + return (-1); + } + total_compressed_bytes = 0; + zs->zs_maxbits = header[2]; /* Set -b from file. */ + zs->zs_block_compress = zs->zs_maxbits & BLOCK_MASK; + zs->zs_maxbits &= BIT_MASK; + zs->zs_maxmaxcode = 1L << zs->zs_maxbits; + if (zs->zs_maxbits > BITS || zs->zs_maxbits < 12) { + errno = EFTYPE; + return (-1); + } + /* As above, initialize the first 256 entries in the table. */ + zs->zs_maxcode = MAXCODE(zs->zs_n_bits = INIT_BITS); + for (zs->u.r.zs_code = 255; zs->u.r.zs_code >= 0; zs->u.r.zs_code--) { + tab_prefixof(zs->u.r.zs_code) = 0; + tab_suffixof(zs->u.r.zs_code) = (char_type) zs->u.r.zs_code; + } + zs->zs_free_ent = zs->zs_block_compress ? FIRST : 256; + + zs->u.r.zs_oldcode = -1; + zs->u.r.zs_stackp = de_stack; + + while ((zs->u.r.zs_code = getcode(zs)) > -1) { + + if ((zs->u.r.zs_code == CLEAR) && zs->zs_block_compress) { + for (zs->u.r.zs_code = 255; zs->u.r.zs_code >= 0; + zs->u.r.zs_code--) + tab_prefixof(zs->u.r.zs_code) = 0; + zs->zs_clear_flg = 1; + zs->zs_free_ent = FIRST; + zs->u.r.zs_oldcode = -1; + continue; + } + zs->u.r.zs_incode = zs->u.r.zs_code; + + /* Special case for KwKwK string. */ + if (zs->u.r.zs_code >= zs->zs_free_ent) { + if (zs->u.r.zs_code > zs->zs_free_ent || + zs->u.r.zs_oldcode == -1) { + /* Bad stream. */ + errno = EINVAL; + return (-1); + } + *zs->u.r.zs_stackp++ = zs->u.r.zs_finchar; + zs->u.r.zs_code = zs->u.r.zs_oldcode; + } + /* + * The above condition ensures that code < free_ent. + * The construction of tab_prefixof in turn guarantees that + * each iteration decreases code and therefore stack usage is + * bound by 1 << BITS - 256. + */ + + /* Generate output characters in reverse order. */ + while (zs->u.r.zs_code >= 256) { + *zs->u.r.zs_stackp++ = tab_suffixof(zs->u.r.zs_code); + zs->u.r.zs_code = tab_prefixof(zs->u.r.zs_code); + } + *zs->u.r.zs_stackp++ = zs->u.r.zs_finchar = tab_suffixof(zs->u.r.zs_code); + + /* And put them out in forward order. */ +middle: do { + if (count-- == 0) + return (num); + *bp++ = *--zs->u.r.zs_stackp; + } while (zs->u.r.zs_stackp > de_stack); + + /* Generate the new entry. */ + if ((zs->u.r.zs_code = zs->zs_free_ent) < zs->zs_maxmaxcode && + zs->u.r.zs_oldcode != -1) { + tab_prefixof(zs->u.r.zs_code) = (u_short) zs->u.r.zs_oldcode; + tab_suffixof(zs->u.r.zs_code) = zs->u.r.zs_finchar; + zs->zs_free_ent = zs->u.r.zs_code + 1; + } + + /* Remember previous code. */ + zs->u.r.zs_oldcode = zs->u.r.zs_incode; + } + zs->zs_state = S_EOF; +eof: return (num - count); +} + +/*- + * Read one code from the standard input. If EOF, return -1. + * Inputs: + * stdin + * Outputs: + * code or -1 is returned. + */ +static code_int +getcode(struct s_zstate *zs) +{ + code_int gcode; + int r_off, bits, i; + char_type *bp; + + bp = zs->u.r.zs_gbuf; + if (zs->zs_clear_flg > 0 || zs->u.r.zs_roffset >= zs->u.r.zs_size || + zs->zs_free_ent > zs->zs_maxcode) { + /* + * If the next entry will be too big for the current gcode + * size, then we must increase the size. This implies reading + * a new buffer full, too. + */ + if (zs->zs_free_ent > zs->zs_maxcode) { + zs->zs_n_bits++; + if (zs->zs_n_bits == zs->zs_maxbits) /* Won't get any bigger now. */ + zs->zs_maxcode = zs->zs_maxmaxcode; + else + zs->zs_maxcode = MAXCODE(zs->zs_n_bits); + } + if (zs->zs_clear_flg > 0) { + zs->zs_maxcode = MAXCODE(zs->zs_n_bits = INIT_BITS); + zs->zs_clear_flg = 0; + } + /* XXX */ + for (i = 0; i < zs->zs_n_bits && compressed_prelen; i++, compressed_prelen--) + zs->u.r.zs_gbuf[i] = *compressed_pre++; + zs->u.r.zs_size = fread(zs->u.r.zs_gbuf + i, 1, zs->zs_n_bits - i, zs->zs_fp); + zs->u.r.zs_size += i; + if (zs->u.r.zs_size <= 0) /* End of file. */ + return (-1); + zs->u.r.zs_roffset = 0; + + total_compressed_bytes += zs->u.r.zs_size; + + /* Round size down to integral number of codes. */ + zs->u.r.zs_size = (zs->u.r.zs_size << 3) - (zs->zs_n_bits - 1); + } + r_off = zs->u.r.zs_roffset; + bits = zs->zs_n_bits; + + /* Get to the first byte. */ + bp += (r_off >> 3); + r_off &= 7; + + /* Get first part (low order bits). */ + gcode = (*bp++ >> r_off); + bits -= (8 - r_off); + r_off = 8 - r_off; /* Now, roffset into gcode word. */ + + /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ + if (bits >= 8) { + gcode |= *bp++ << r_off; + r_off += 8; + bits -= 8; + } + + /* High order bits. */ + gcode |= (*bp & rmask[bits]) << r_off; + zs->u.r.zs_roffset += zs->zs_n_bits; + + return (gcode); +} + diff --git a/files/Sources/files/less/INSTALL b/files/Sources/files/less/INSTALL new file mode 100644 index 00000000..c2ab230e --- /dev/null +++ b/files/Sources/files/less/INSTALL @@ -0,0 +1,186 @@ + This file describes how to build and install less using +the "configure" script. This only works on Unix systems. +To install on other systems, read the README file. + + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + diff --git a/files/Sources/files/less/LICENSE b/files/Sources/files/less/LICENSE new file mode 100644 index 00000000..832ca1b3 --- /dev/null +++ b/files/Sources/files/less/LICENSE @@ -0,0 +1,27 @@ + Less License + ------------ + +Less +Copyright (C) 1984-2016 Mark Nudelman + +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 in the documentation and/or other materials provided with + the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + diff --git a/files/Sources/files/less/Makefile.aut b/files/Sources/files/less/Makefile.aut new file mode 100644 index 00000000..9cc04cea --- /dev/null +++ b/files/Sources/files/less/Makefile.aut @@ -0,0 +1,143 @@ +# Makefile for authoring less. + +EMAIL = bug-less@gnu.org +HOMEPAGE = http://www.greenwoodsoftware.com/less +SHELL = /bin/sh +RCS = rcs +NROFF = nroff -man + +srcdir = . + +SRC = \ + main.c screen.c brac.c ch.c charset.c cmdbuf.c \ + command.c cvt.c decode.c edit.c filename.c forwback.c \ + help.c ifile.c input.c jump.c line.c linenum.c \ + lsystem.c mark.c optfunc.c option.c opttbl.c os.c \ + output.c pattern.c position.c prompt.c search.c signal.c \ + tags.c ttyin.c version.c +DISTFILES_W = \ + defines.ds Makefile.dsb Makefile.dsg Makefile.dsu \ + defines.o2 Makefile.o2e \ + defines.o9 Makefile.o9c Makefile.o9u \ + defines.wn Makefile.wnm Makefile.wnb \ + configure +UNICODE_FILES = \ + compose.uni ubin.uni wide.uni +DISTFILES = \ + ${SRC} regexp.c regexp.h \ + COPYING INSTALL LICENSE Makefile.in Makefile.aut NEWS README \ + configure.ac lesskey.c lessecho.c scrsize.c \ + charset.h cmd.h funcs.h lglob.h less.h lesskey.h option.h \ + pckeys.h pattern.h position.h \ + install.sh defines.h.in mkinstalldirs \ + less.nro less.man lesskey.nro lesskey.man lessecho.nro lessecho.man \ + less.hlp \ + mkfuncs.awk mkhelp.c \ + mkutable $(UNICODE_FILES) \ + ${DISTFILES_W} + +all: help.c funcs.h $(UNICODE_FILES) ${srcdir}/configure + +release: .FORCE + ${MAKE} -f Makefile.aut tagall + ${MAKE} -f Makefile.aut all + ${MAKE} -f Makefile.aut clean + ${MAKE} -f Makefile.aut dist + +.FORCE: + +help.c: less.hlp mkhelp + -mv -f ${srcdir}/help.c ${srcdir}/help.c.old + rm -rf help.c + ./mkhelp < less.hlp > help.c + if cmp -s help.c help.c.old; then mv -f help.c.old help.c; fi + +mkhelp: mkhelp.c + ${CC} -o mkhelp mkhelp.c + +${srcdir}/configure: ${srcdir}/configure.ac \ + ${srcdir}/Makefile.in + cd ${srcdir}; autoheader; autoconf + +funcs.h: ${SRC:%=${srcdir}/%} + -mv -f ${srcdir}/funcs.h ${srcdir}/funcs.h.old + awk -f ${srcdir}/mkfuncs.awk ${SRC:%=${srcdir}/%} >${srcdir}/funcs.h + if cmp -s funcs.h funcs.h.old; then mv -f funcs.h.old funcs.h; fi + +lint: + lint -I. ${CPPFLAGS} ${SRC} + +clean: + rm -f Makefile config.status config.log config.cache defines.h stamp-h \ + README NEWS \ + less.nro less.man lesskey.nro lesskey.man lessecho.nro lessecho.man + +distclean: clean +realclean: clean + +REPLACE_VERSION = \ + @REL=`sed -e '/char version/!d' -e 's/[^0-9.]*\([0-9.]*\).*/\1/' -e q ${srcdir}/version.c`; \ + DT=`date '+%d %h %Y'`; \ + echo "Stuffing version number $$REL into $@"; \ + rm -f $@; \ + sed \ + -e "s;@@VERSION@@;$$REL;" \ + -e "s;@@DATE@@;$$DT;" \ + -e "s;@@EMAIL@@;${EMAIL};" \ + -e "s;@@HOMEPAGE@@;${HOMEPAGE};" >$@ + +${srcdir}/README: ${srcdir}/README.VER ${srcdir}/version.c + ${REPLACE_VERSION} ${srcdir}/README.VER +${srcdir}/NEWS: ${srcdir}/NEWS.VER ${srcdir}/version.c + ${REPLACE_VERSION} ${srcdir}/NEWS.VER +${srcdir}/less.nro: ${srcdir}/less.nro.VER ${srcdir}/version.c + ${REPLACE_VERSION} ${srcdir}/less.nro.VER +${srcdir}/lesskey.nro: ${srcdir}/lesskey.nro.VER ${srcdir}/version.c + ${REPLACE_VERSION} ${srcdir}/lesskey.nro.VER +${srcdir}/lessecho.nro: ${srcdir}/lessecho.nro.VER ${srcdir}/version.c + ${REPLACE_VERSION} ${srcdir}/lessecho.nro.VER +${srcdir}/less.hlp: ${srcdir}/less.hlp.VER ${srcdir}/version.c + ${REPLACE_VERSION} ${srcdir}/less.hlp.VER + +${srcdir}/less.man: ${srcdir}/less.nro + ${NROFF} ${srcdir}/less.nro >${srcdir}/less.man +${srcdir}/lesskey.man: ${srcdir}/lesskey.nro + ${NROFF} ${srcdir}/lesskey.nro >${srcdir}/lesskey.man +${srcdir}/lessecho.man: ${srcdir}/lessecho.nro + ${NROFF} ${srcdir}/lessecho.nro >${srcdir}/lessecho.man + +compose.uni: unicode/UnicodeData.txt + ./mkutable -f2 Mn Me -- unicode/UnicodeData.txt > $@ +ubin.uni: unicode/UnicodeData.txt + ./mkutable -f2 Cc Cf Cs Co Zl Zp -- unicode/UnicodeData.txt > $@ +wide.uni: unicode/EastAsianWidth.txt + ./mkutable -f1 W F -- unicode/EastAsianWidth.txt > $@ + +distfiles: ${DISTFILES} + +dist: ${DISTFILES} + if [ ! -d ${srcdir}/release ]; then mkdir ${srcdir}/release; fi + @cd ${srcdir}; \ + REL=`sed -e '/char version/!d' -e 's/[^0-9.]*\([0-9.]*\).*/less-\1/' -e q version.c`; \ + rm -rf release/$$REL; mkdir release/$$REL; \ + echo "Preparing $$REL"; \ + rm -rf $$REL; mkdir $$REL; \ + for file in ${DISTFILES}; do \ + ./add_copyright $$file $$REL; \ + done; \ + cd $$REL; chmod -w *; chmod +w ${DISTFILES_W}; chmod +x configure; cd ..; \ + echo "Creating release/$$REL/$$REL.tar.gz"; \ + tar -cf - $$REL | gzip -c >release/$$REL/$$REL.tar.gz; \ + echo "Signing release/$$REL/$$REL.tar.gz"; \ + gpg --detach-sign release/$$REL/$$REL.tar.gz; \ + echo "Creating release/$$REL/$$REL.zip"; \ + zip -rq release/$$REL/$$REL.zip $$REL; \ + rm -rf $$REL + +tagall: + @REL=`sed -e '/char version/!d' -e 's/[^0-9.]*\([0-9.]*\).*/v\1/' -e q ${srcdir}/version.c`; \ + echo "tagging $$REL"; \ + for f in ${srcdir}/RCS/*,v; do \ + REV=`co -p $$f 2>&1 | sed -e '1d' -e '3,$$d' -e 's/revision //'`; \ + ${RCS} -N$$REL:$$REV $$f; \ + done diff --git a/files/Sources/files/less/Makefile.dsb b/files/Sources/files/less/Makefile.dsb new file mode 100644 index 00000000..e45b6025 --- /dev/null +++ b/files/Sources/files/less/Makefile.dsb @@ -0,0 +1,51 @@ +# Makefile for less. +# MS-DOS version (Borland C/C++ 4.02) + +#### Start of system configuration section. #### + +CC = bcc +LIBDIR = \bc\lib + +CFLAGS = -A- -mm -O2 -w- -1- -2- -a -d -Z +LDFLAGS = -mm +LIBS = +EXT = .EXE + +#### End of system configuration section. #### + +# This rule allows us to supply the necessary -D options +# in addition to whatever the user asks for. +.c.obj: + $(CC) -c -I. $(CPPFLAGS) $(CFLAGS) $< + +OBJ = \ + main.obj screen.obj brac.obj ch.obj charset.obj cmdbuf.obj \ + command.obj cvt.obj decode.obj edit.obj filename.obj forwback.obj \ + help.obj ifile.obj input.obj jump.obj line.obj linenum.obj \ + lsystem.obj mark.obj optfunc.obj option.obj opttbl.obj os.obj \ + output.obj pattern.obj position.obj prompt.obj search.obj signal.obj \ + tags.obj ttyin.obj version.obj + +all: less$(EXT) lesskey$(EXT) + +# This is really horrible, but the command line is too long for +# MS-DOS if we try to link $(OBJ). +less$(EXT): $(OBJ) + ren lesskey.obj lesskey.obo + $(CC) $(LDFLAGS) -e$@ *.obj $(LIBS) + ren lesskey.obo lesskey.obj + +lesskey$(EXT): lesskey.obj version.obj + $(CC) $(LDFLAGS) -e$@ lesskey.obj version.obj $(LIBS) + +defines.h: defines.ds + -del defines.h + -copy defines.ds defines.h + +$(OBJ): less.h defines.h + +clean: + -del *.obj + -del less.exe + -del lesskey.exe + diff --git a/files/Sources/files/less/Makefile.dsg b/files/Sources/files/less/Makefile.dsg new file mode 100644 index 00000000..b921b5de --- /dev/null +++ b/files/Sources/files/less/Makefile.dsg @@ -0,0 +1,92 @@ +# Makefile for less under DJGPP v2.0 or later. + +#### Start of system configuration section. #### + +srcdir = . +VPATH = . + +CC = gcc +INSTALL = ginstall -c +INSTALL_PROGRAM = ginstall +INSTALL_DATA = ginstall -m 644 +AWK = gawk + +CFLAGS = -O2 -g +CFLAGS_COMPILE_ONLY = -c +#LDFLAGS = -s +LDFLAGS = -g +O=o + +LIBS = +prefix = /dev/env/DJDIR +exec_prefix = ${prefix} + +bindir = ${exec_prefix}/bin +sysconfdir = ${prefix}/etc +mandir = ${prefix}/man +manext = 1 + +#### End of system configuration section. #### + +# This rule allows us to supply the necessary -D options +# in addition to whatever the user asks for. +.c.o: + ${CC} -I. ${CFLAGS_COMPILE_ONLY} -DBINDIR=\"${bindir}\" -DSYSDIR=\"${sysconfdir}\" ${CPPFLAGS} ${CFLAGS} $< + +OBJ = \ + main.${O} screen.${O} brac.${O} ch.${O} charset.${O} cmdbuf.${O} \ + command.${O} cvt.${O} decode.${O} edit.${O} filename.${O} forwback.${O} \ + help.${O} ifile.${O} input.${O} jump.${O} line.${O} linenum.${O} \ + lsystem.${O} mark.${O} optfunc.${O} option.${O} opttbl.${O} os.${O} \ + output.${O} pattern.${O} position.${O} prompt.${O} search.${O} signal.${O} \ + tags.${O} ttyin.${O} version.${O} + +all: less lesskey lessecho + +less: ${OBJ} + ${CC} ${LDFLAGS} -o $@ ${OBJ} ${LIBS} + +lesskey: lesskey.${O} version.${O} + ${CC} ${LDFLAGS} -o $@ lesskey.${O} version.${O} + +lessecho: lessecho.${O} version.${O} + ${CC} ${LDFLAGS} -o $@ lessecho.${O} version.${O} + +defines.h: defines.ds + command.com /c copy $< $@ + +${OBJ}: ${srcdir}/less.h defines.h ${srcdir}/funcs.h + +install: all ${srcdir}/less.man ${srcdir}/lesskey.man + ${INSTALL_PROGRAM} less.exe ${bindir}/less.exe + ${INSTALL_PROGRAM} lesskey.exe ${bindir}/lesskey.exe + ${INSTALL_PROGRAM} lessecho.exe ${bindir}/lessecho.exe + ${INSTALL_DATA} ${srcdir}/less.man ${mandir}/man${manext}/less.${manext} + ${INSTALL_DATA} ${srcdir}/lesskey.man ${mandir}/man${manext}/lesskey.${manext} + +info: +install-info: +dvi: +check: +installcheck: + +TAGS: + etags *.c *.h + +newfuncs: + command.com /c if exist funcs.h del funcs.h + ${AWK} -f mkfuncs.awk ${OBJ:.${O}=.c} > funcs.h + +clean: + command.com /c for %f in (*.${O} less lesskey lessecho *.exe) do if exist %f del %f + +mostlyclean: clean + +distclean: clean + command.com /c if not exist Makefile.dsg ren Makefile Makefile.dsg + command.com /c if not exist defines.ds ren defines.h defines.ds + command.com /c for %f in (Makefile defines.h) do if exist %f del %f + +realclean: distclean + command.com /c if exist TAGS del TAGS + diff --git a/files/Sources/files/less/Makefile.dsu b/files/Sources/files/less/Makefile.dsu new file mode 100644 index 00000000..40a25384 --- /dev/null +++ b/files/Sources/files/less/Makefile.dsu @@ -0,0 +1,59 @@ +# Makefile for less. +# MS-DOS version + +#### Start of system configuration section. #### + +CC = cl +# Change the following directories to match your installation. +LIBDIR = c:\msvc\lib +INCDIR = c:\msvc\include + +# CFLAGS are compile-time options and LDFLAGS are link-time options. They are +# customized for MSVC 1.0 (MSC 8.0). If you have a different version of the +# compiler, you may need to change some of the options to their equivalents. +# -Ot optimize for speed +# -AL large memory model +# -Za ANSI C conformance +# -nologo suppress MSVC banners +# -onerror:noexe no .EXE file if link errors occur +CFLAGS = -Ot -AL -Za -nologo +LDFLAGS = -onerror:noexe -nologo +LIBS = $(LIBDIR)\llibce.lib $(LIBDIR)\graphics.lib + +#### End of system configuration section. #### + +# This rule allows us to supply the necessary -D options +# in addition to whatever the user asks for. +.c.obj: + $(CC) -c -I. -I$(INCDIR) $(CPPFLAGS) $(CFLAGS) $< + +OBJ = \ + main.obj screen.obj brac.obj ch.obj charset.obj cmdbuf.obj \ + command.obj cvt.obj decode.obj edit.obj filename.obj forwback.obj \ + help.obj ifile.obj input.obj jump.obj line.obj linenum.obj \ + lsystem.obj mark.obj optfunc.obj option.obj opttbl.obj os.obj \ + output.obj pattern.obj position.obj prompt.obj search.obj signal.obj \ + tags.obj ttyin.obj version.obj + +all: less lesskey + +# This is really horrible, but the command line is too long for +# MS-DOS if we try to link $(OBJ). +less: $(OBJ) + -if exist lesskey.obj del lesskey.obj + $(CC) $(LDFLAGS) -o $@ *.obj $(LIBS) + +lesskey: lesskey.obj version.obj + $(CC) $(LDFLAGS) -o $@ lesskey.obj version.obj $(LIBS) + +defines.h: defines.ds + -del defines.h + -copy defines.ds defines.h + +$(OBJ): less.h defines.h + +clean: + -del *.obj + -del less.exe + -del lesskey.exe + diff --git a/files/Sources/files/less/Makefile.in b/files/Sources/files/less/Makefile.in new file mode 100644 index 00000000..8c8aed81 --- /dev/null +++ b/files/Sources/files/less/Makefile.in @@ -0,0 +1,122 @@ +# Makefile for less. + +#### Start of system configuration section. #### + +srcdir = @srcdir@ +VPATH = @srcdir@ + +CC = @CC@ +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +CFLAGS = @CFLAGS@ +CFLAGS_COMPILE_ONLY = -c +LDFLAGS = @LDFLAGS@ +CPPFLAGS = @CPPFLAGS@ +EXEEXT = @EXEEXT@ +O=o + +LIBS = @LIBS@ + +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +# Where the installed binary goes. +bindir = @bindir@ +binprefix = + +sysconfdir = @sysconfdir@ +datarootdir = @datarootdir@ + +mandir = @mandir@ +manext = 1 +manprefix = +DESTDIR = + +#### End of system configuration section. #### + +SHELL = /bin/sh + +# This rule allows us to supply the necessary -D options +# in addition to whatever the user asks for. +.c.o: + ${CC} -I. ${CFLAGS_COMPILE_ONLY} -DBINDIR=\"${bindir}\" -DSYSDIR=\"${sysconfdir}\" ${CPPFLAGS} ${CFLAGS} $< + +OBJ = \ + main.${O} screen.${O} brac.${O} ch.${O} charset.${O} cmdbuf.${O} \ + command.${O} cvt.${O} decode.${O} edit.${O} filename.${O} forwback.${O} \ + help.${O} ifile.${O} input.${O} jump.${O} line.${O} linenum.${O} \ + lsystem.${O} mark.${O} optfunc.${O} option.${O} opttbl.${O} os.${O} \ + output.${O} pattern.${O} position.${O} prompt.${O} search.${O} signal.${O} \ + tags.${O} ttyin.${O} version.${O} @REGEX_O@ + +all: less$(EXEEXT) lesskey$(EXEEXT) lessecho$(EXEEXT) + +less$(EXEEXT): ${OBJ} + ${CC} ${LDFLAGS} -o $@ ${OBJ} ${LIBS} + +lesskey$(EXEEXT): lesskey.${O} version.${O} + ${CC} ${LDFLAGS} -o $@ lesskey.${O} version.${O} + +lessecho$(EXEEXT): lessecho.${O} version.${O} + ${CC} ${LDFLAGS} -o $@ lessecho.${O} version.${O} + +${OBJ}: ${srcdir}/less.h ${srcdir}/funcs.h defines.h + +install: all ${srcdir}/less.nro ${srcdir}/lesskey.nro ${srcdir}/lessecho.nro installdirs + ${INSTALL_PROGRAM} less$(EXEEXT) ${DESTDIR}${bindir}/${binprefix}less$(EXEEXT) + ${INSTALL_PROGRAM} lesskey$(EXEEXT) ${DESTDIR}${bindir}/${binprefix}lesskey$(EXEEXT) + ${INSTALL_PROGRAM} lessecho$(EXEEXT) ${DESTDIR}${bindir}/${binprefix}lessecho$(EXEEXT) + ${INSTALL_DATA} ${srcdir}/less.nro ${DESTDIR}${mandir}/man${manext}/${manprefix}less.${manext} + ${INSTALL_DATA} ${srcdir}/lesskey.nro ${DESTDIR}${mandir}/man${manext}/${manprefix}lesskey.${manext} + ${INSTALL_DATA} ${srcdir}/lessecho.nro ${DESTDIR}${mandir}/man${manext}/${manprefix}lessecho.${manext} + +install-strip: + ${MAKE} INSTALL_PROGRAM='${INSTALL_PROGRAM} -s' install + +installdirs: mkinstalldirs + ${srcdir}/mkinstalldirs ${DESTDIR}${bindir} ${DESTDIR}${mandir}/man${manext} + +uninstall: + rm -f ${DESTDIR}${bindir}/${binprefix}less$(EXEEXT) + rm -f ${DESTDIR}${bindir}/${binprefix}lesskey$(EXEEXT) + rm -f ${DESTDIR}${bindir}/${binprefix}lessecho$(EXEEXT) + rm -f ${DESTDIR}${mandir}/man${manext}/${manprefix}less.${manext} + rm -f ${DESTDIR}${mandir}/man${manext}/${manprefix}lesskey.${manext} + rm -f ${DESTDIR}${mandir}/man${manext}/${manprefix}lessecho.${manext} + +info: +install-info: +dvi: +check: +installcheck: + +TAGS: + cd ${srcdir} && etags *.c *.h + +# config.status might not change defines.h +# Don't rerun config.status if we just configured (so there's no stamp-h). +defines.h: stamp-h +stamp-h: defines.h.in config.status + test ! -f stamp-h || CONFIG_FILES= CONFIG_HEADERS=defines.h ./config.status + touch stamp-h +Makefile: ${srcdir}/Makefile.in config.status + CONFIG_FILES=Makefile CONFIG_HEADERS= ./config.status +config.status: ${srcdir}/configure + ./config.status --recheck + +${srcdir}/configure: ${srcdir}/configure.ac + cd ${srcdir}; autoheader; autoconf + +clean: + rm -f *.${O} core less$(EXEEXT) lesskey$(EXEEXT) lessecho$(EXEEXT) + +mostlyclean: clean + +distclean: clean + rm -f Makefile config.status config.log config.cache defines.h stamp-h + +realclean: distclean + rm -f TAGS + diff --git a/files/Sources/files/less/Makefile.o2e b/files/Sources/files/less/Makefile.o2e new file mode 100644 index 00000000..3bb6dfc4 --- /dev/null +++ b/files/Sources/files/less/Makefile.o2e @@ -0,0 +1,43 @@ +# Makefile for less. +# OS/2 version, for emx+gcc compiler + +#### Start of system configuration section. #### + +CC = gcc -Zomf +CFLAGS = -I. -O2 -Wall +LDFLAGS = -s -Zcrtdll +LIBS = -ltermcap +O = obj + +#### End of system configuration section. #### + +.SUFFIXES: .c .${O} + +# This rule allows us to supply the necessary -D options +# in addition to whatever the user asks for. +.c.${O}: + ${CC} -c ${CPPFLAGS} ${CFLAGS} $< + +OBJ = \ + main.${O} screen.${O} brac.${O} ch.${O} charset.${O} cmdbuf.${O} \ + command.${O} cvt.${O} decode.${O} edit.${O} filename.${O} forwback.${O} \ + help.${O} ifile.${O} input.${O} jump.${O} line.${O} linenum.${O} \ + lsystem.${O} mark.${O} optfunc.${O} option.${O} opttbl.${O} os.${O} \ + output.${O} pattern.${O} position.${O} prompt.${O} search.${O} signal.${O} \ + tags.${O} ttyin.${O} version.${O} regexp.${O} + +all: less.exe lesskey.exe scrsize.exe + +less.exe: ${OBJ} + ${CC} ${OBJ} -o $@ ${LDFLAGS} ${LIBS} + +lesskey.exe: lesskey.${O} version.${O} + ${CC} lesskey.${O} version.${O} -o $@ ${LDFLAGS} + +scrsize.exe: scrsize.c + ${CC} ${CFLAGS} -D__ST_MT_ERRNO__ -s -Zmtd -lX11 $< + +${OBJ}: defines.h less.h + +defines.h: defines.o2 + copy defines.o2 defines.h diff --git a/files/Sources/files/less/Makefile.o9c b/files/Sources/files/less/Makefile.o9c new file mode 100644 index 00000000..e107f559 --- /dev/null +++ b/files/Sources/files/less/Makefile.o9c @@ -0,0 +1,47 @@ +# Makefile for less. +# OS-9 version for Microware C 3.2. + +#### Start of system configuration section. #### + +CC = cc +CPPFLAGS = -D_OSK_MWC32 -DDEBUG=0 -DSTRCSPN +CFLAGS = -k=0 -v=. +CFLAGS_COMPILE_ONLY = -r +LDFLAGS = -igm=8 +LIBS = -l=/dd/lib/termlib.l +O = r + + +#### End of system configuration section. #### + +.SUFFIXES: .c .${O} + +# This rule allows us to supply the necessary -D options +# in addition to whatever the user asks for. + +.c.${O}: + ${CC} ${CFLAGS_COMPILE_ONLY} ${CPPFLAGS} ${CFLAGS} $< + +OBJ = \ + main.${O} screen.${O} brac.${O} ch.${O} charset.${O} cmdbuf.${O} \ + command.${O} cvt.${O} decode.${O} edit.${O} filename.${O} forwback.${O} \ + help.${O} ifile.${O} input.${O} jump.${O} line.${O} linenum.${O} \ + lsystem.${O} mark.${O} optfunc.${O} option.${O} opttbl.${O} os.${O} \ + output.${O} pattern.${O} position.${O} prompt.${O} search.${O} signal.${O} \ + tags.${O} ttyin.${O} version.${O} regexp.${O} + +all: less lessecho lesskey + +less: ${OBJ} + ${CC} ${OBJ} -f=$@ ${LDFLAGS} ${LIBS} + +lesskey: lesskey.${O} version.${O} + ${CC} lesskey.${O} version.${O} -f=$@ ${LDFLAGS} + +lessecho: lessecho.${O} version.${O} + ${CC} lessecho.${O} version.${O} -f=$@ ${LDFLAGS} + +${OBJ}: defines.h less.h + +defines.h: defines.o9 + copy defines.o9 defines.h -rf diff --git a/files/Sources/files/less/Makefile.o9u b/files/Sources/files/less/Makefile.o9u new file mode 100644 index 00000000..8ca84c89 --- /dev/null +++ b/files/Sources/files/less/Makefile.o9u @@ -0,0 +1,43 @@ +# Makefile for less. +# OS-9 version for Ultra C. + +#### Start of system configuration section. #### + +CC = cc +CPPFLAGS = +CFLAGS = -v=. +CFLAGS_COMPILE_ONLY = -eas +LDFLAGS = -olM=24k +LIBS = -ltermlib.l -lsys_clib.l -lunix.l +O = r + + +#### End of system configuration section. #### + +.SUFFIXES: .c .${O} + +# This rule allows us to supply the necessary -D options +# in addition to whatever the user asks for. +.c.${O}: + ${CC} ${CFLAGS_COMPILE_ONLY} ${CPPFLAGS} ${CFLAGS} $< + +OBJ = \ + main.${O} screen.${O} brac.${O} ch.${O} charset.${O} cmdbuf.${O} \ + command.${O} cvt.${O} decode.${O} edit.${O} filename.${O} forwback.${O} \ + help.${O} ifile.${O} input.${O} jump.${O} line.${O} linenum.${O} \ + lsystem.${O} mark.${O} optfunc.${O} option.${O} opttbl.${O} os.${O} \ + output.${O} pattern.${O} position.${O} prompt.${O} search.${O} signal.${O} \ + tags.${O} ttyin.${O} version.${O} regexp.${O} + +all: less lesskey + +less: ${OBJ} + ${CC} ${OBJ} -f=$@ ${LDFLAGS} ${LIBS} + +lesskey: lesskey.${O} version.${O} + ${CC} lesskey.${O} version.${O} -f=$@ ${LDFLAGS} + +${OBJ}: defines.h less.h + +defines.h: defines.o9 + copy defines.o9 defines.h -rf diff --git a/files/Sources/files/less/Makefile.wnb b/files/Sources/files/less/Makefile.wnb new file mode 100644 index 00000000..75a65cdd --- /dev/null +++ b/files/Sources/files/less/Makefile.wnb @@ -0,0 +1,71 @@ +# Makefile for less. +# Windows version +# Bolarnd C++ 5.5.1 free command line tools + +#### Start of system configuration section. #### +# +# Borland's make knows its own location in the +# filesystem. +# + +CC = bcc32 +LIBDIR = $(MAKEDIR)\..\lib + +CFLAGS = -O2 -w-pro -TWC -P-c -v- -d -f- -ff- -vi +LDFLAGS = -Tpe -v- -ap -c -x -V4.0 -GF:AGGRESSIVE +LD = ilink32 +LIBS = ${LIBDIR}\import32.lib ${LIBDIR}\cw32.lib + +#### End of system configuration section. #### + +# +# This rule allows us to supply the necessary -D options +# in addition to whatever the user asks for. +# +.c.obj: + ${CC} -c -I. ${CPPFLAGS} ${CFLAGS} $< + +OBJ = \ + main.obj screen.obj brac.obj ch.obj charset.obj cmdbuf.obj \ + command.obj cvt.obj decode.obj edit.obj filename.obj forwback.obj \ + help.obj ifile.obj input.obj jump.obj line.obj linenum.obj \ + lsystem.obj mark.obj optfunc.obj option.obj opttbl.obj os.obj \ + output.obj pattern.obj position.obj prompt.obj search.obj signal.obj \ + tags.obj ttyin.obj version.obj regexp.obj + +all: less lesskey lessecho + +# +# This is really horrible, but the command line is too long for +# MS-DOS if we try to link ${OBJ}. +# +less: ${OBJ} + ${LD} ${LDFLAGS} ${LIBDIR}\c0x32.obj $**, $@,,${LIBS} + +lesskey: lesskey.obj version.obj + ${LD} ${LDFLAGS} ${LIBDIR}\c0x32.obj $**, $@,,${LIBS} + +lessecho: lessecho.obj version.obj + ${LD} ${LDFLAGS} ${LIBDIR}\c0x32.obj $**, $@,,${LIBS} + +defines.h: defines.wn + -del defines.h + -copy defines.wn defines.h + +${OBJ}: less.h defines.h funcs.h cmd.h + +clean: + -del *.obj + -del *.il? + -del *.tds + -del defines.h + +spotless: clean + -del less.exe + -del lesskey.exe + -del lessecho.exe + +realclean: spotless + +distclean: spotless + diff --git a/files/Sources/files/less/Makefile.wnm b/files/Sources/files/less/Makefile.wnm new file mode 100644 index 00000000..11273289 --- /dev/null +++ b/files/Sources/files/less/Makefile.wnm @@ -0,0 +1,53 @@ +# Makefile for less. +# Windows 32 Visual C++ version + +#### Start of system configuration section. #### + +CC = cl + +# Normal flags +CFLAGS = /nologo /MD /W3 /EHsc /O2 /I "." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /c +LDFLAGS = /nologo /subsystem:console /incremental:no /machine:I386 + +# Debugging flags +#CFLAGS = /nologo /MDd /W3 /GX /Od /Gm /Zi /I "." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /c +#LDFLAGS = /nologo /subsystem:console /incremental:yes /debug /machine:I386 + +LD = link +LIBS = user32.lib + +#### End of system configuration section. #### + +# This rule allows us to supply the necessary -D options +# in addition to whatever the user asks for. +.c.obj:: + $(CC) $(CFLAGS) $< + +OBJ = \ + main.obj screen.obj brac.obj ch.obj charset.obj cmdbuf.obj \ + command.obj cvt.obj decode.obj edit.obj filename.obj forwback.obj \ + help.obj ifile.obj input.obj jump.obj line.obj linenum.obj \ + lsystem.obj mark.obj optfunc.obj option.obj opttbl.obj os.obj \ + output.obj pattern.obj position.obj prompt.obj search.obj signal.obj \ + tags.obj ttyin.obj version.obj regexp.obj + +all: less.exe lesskey.exe + +less.exe: $(OBJ) + $(LD) $(LDFLAGS) $** $(LIBS) /out:$@ + +lesskey.exe: lesskey.obj version.obj + $(LD) $(LDFLAGS) lesskey.obj version.obj $(LIBS) /out:$@ + +defines.h: defines.wn + -del defines.h + -copy defines.wn defines.h + +$(OBJ): less.h defines.h funcs.h cmd.h + +clean: + -del *.obj + -del less.exe + -del lesskey.exe + + diff --git a/files/Sources/files/less/NEWS b/files/Sources/files/less/NEWS new file mode 100644 index 00000000..ebdce64b --- /dev/null +++ b/files/Sources/files/less/NEWS @@ -0,0 +1,922 @@ + + NEWS about less + +====================================================================== + + For the latest news about less, see the "less" Web page: + http://www.greenwoodsoftware.com/less + You can also download the latest version of less from there. + + To report bugs, suggestions or comments, send email to bug-less@gnu.org + +====================================================================== + + Major changes between "less" versions 481 and 487 + +* New commands ESC-{ and ESC-} to shift to start/end of displayed lines. + +* Make search highlights work correctly when changing caselessness with -i. + +* New option -Da in Windows version to enable SGR mode. + +* Fix "nothing to search" error when top or bottom line on screen is empty. + +* Fix bug when terminal has no "cm" termcap entry. + +* Fix incorrect display when entering double-width chars in search string. + +* Fix bug in Unicode handling that missed some double width characters. + +* Update Unicode database to 9.0.0. + +====================================================================== + + Major changes between "less" versions 458 and 481 + +* Don't overwrite history file; just append to it. + +* New command ESC-G goes to end of currently buffered data in a pipe. + +* Disable history feature when compiled with LESSHISTFILE set to "-". + +* In more-compatible mode, make the -p option apply to every file opened, + not just the first one. + +* In more-compatible mode, change the -e option to work like -E, not -EF. + +* Treat multiple CRs before LF are like one CR (all the CRs are hidden). + +* Allow "extra" string in lesskey file to append to a multi-char command + (like a search pattern), without executing the command. + +* Ignore -u/-U setting while viewing help file, so that + underline and bold chars are displayed correctly. + +* Improve detection of "binary" files in UTF-8 mode. + +* Fix bug with ++ commands. + +* Fix bug where prompt was sometimes not displayed with +G. + +* Fix possible memory corruption + +* Fix bugs and improve performance in ampersand filtering. + +* Automate construction of Unicode tables from Unicode database. + +* Allow %% escape sequence in LESSOPEN variable. + +====================================================================== + + Major changes between "less" versions 451 and 458 + +* Allow backslash escaping of metacharacters in LESS environment variable + after the --use-backslash option. + +* Don't quit if syntax errors are found in command line options. + +* Increase sizes of some internal buffers. + +* Fix configure bug with --with-regex=none. + +* Fix crash with "stty rows 0". + +* Fix Win32 attribute display bug. + +* Fix display bug when using up/down arrow on the command line. + +====================================================================== + + Major changes between "less" versions 444 and 451 + +* Add ESC-F command to keep reading data until a pattern is found. + +* Use exit code of LESSOPEN script if LESSOPEN starts with "||". + +* When up/down arrow is used on the command line immediately after + typing text, the next command starting with that text is found. + +* Add support for GNU regex. + +* Add configure option --with-regex=none and fix compile errors + when compiling with no regex library. + +* Fix bugs handling SGR sequences in Win32. + +* Fix possible crashes caused by malformed LESSOPEN or + LESSCLOSE variables. + +* Fix bug highlighting text which is discontiguous in the file + due to backspace processing. + +* Fix bug in displaying status column when scrolling backwards + with -J and -S in effect. + +====================================================================== + + Major changes between "less" versions 443 and 444 + +* Fix bug in unget handling that can cause strange effects on the + command line. + +* Remove vestiges of obsolete -l option that can cause a crash. + +====================================================================== + + Major changes between "less" versions 436 and 443 + +* Change search behavior such that when a search is given an explicit + pattern, the entire displayed screen is included in the search and + not just the portion after the target line. + +* Add -A option to change search behavior to the old way: only + the portion of the screen after the target line is searched. + +* Add %F formatting to prompt strings, replaced by the last component + of the input file. + +* Control-G while editing a command exits the command. + +* Less now exits with status 2 if control-C is pressed and -K is in effect. + +* Fix "ungetc overflow" when passing long commands via the -p option. + +* Fix bug in using line filtering via the & command + in combination with -i and -I. + +* Fix bug in handling negative arguments to the -j option. + +* Fix bug in handling %t in prompt strings. + +* Improve handling of long option names. + +* Improve percentage calculation for very large files. + +====================================================================== + + Major changes between "less" versions 429 and 436 + +* Don't pass "-" to non-pipe LESSOPEN unless it starts with "-". + +* Allow a fraction as the argument to the -# (--shift) option. + +* Fix highlight bug when underlined/overstruck text matches at end of line. + +* Fix non-regex searches with ctrl-R. + +====================================================================== + + Major changes between "less" versions 424 and 429 + +* LESSOPEN pipe will now be used on standard input, if the LESSOPEN + environment variable begins with "|-". + +* The -D option with one number now means use the normal background color. + +* Don't change permissions on history file if it is not a regular file. + +* Fix non-ANSI-compliant code that caused problems with some compilers. + +* Fix binary file detection in UTF-8 mode. + +* Fix display problems with long lines on "ignaw" terminals. + +* Fix problem interrupting the line number calculation for initial prompt. + +* Fix SGR emulation when dealing with multiple attributes (eg. bold+underline). + +* Fix highlight bug when searching for underlined/overstruck text. + +====================================================================== + + Major changes between "less" versions 418 and 424 + +* New "&" command allows filtering of lines based on a pattern. + +* Status column now displays a search match, even if the matched + string is scrolled off screen because -S is in effect. + +* Improve behavior of -F option. + +* Allow CSI character (0x9B) to work in UTF-8 mode. + +* Output carriage return at startup in case terminal doesn't default + to column 1. + +* Fix bug in '' (quote, quote) command after G command. + +====================================================================== + + Major changes between "less" versions 416 and 418 + +* Color escape sequences are now supported in WIN32 build. + +* Makefile now uses EXEEXT feature of autoconf. + +* Fix search bug when using -R and text contains ANSI color escape sequences. + +* Fix crash when using -r with UTF-8 text containing 0x9B bytes. + +* Fix display bug when using ' command to move less than one page forward. + +* Update GPL to version 3. + +====================================================================== + + Major changes between "less" versions 409 and 416 + +* New --follow-name option makes F command follow the name of a file + rather than the file descriptor if an open file is renamed. + +* Make searching with -i/-I work correctly with non-ASCII text. + +* Fix DJGPP build. + +====================================================================== + + Major changes between "less" versions 406 and 409 + +* Support CSI escape sequences, like SGR escape sequences. + +* Fix bug which caused screen to fail to repaint when window is resized. + +* Fix bug in using -i and -I flags with non-ASCII text. + +* Fix configure bug on systems which don't support langinfo.h. + +* Fix crash when searching text containing certain invalid UTF-8 sequences. + +====================================================================== + + Major changes between "less" versions 394 and 406 + +* Allow decimal point in number for % (percent) command. + +* Allow decimal point in number for -j option (fraction of screen height). + +* Make n command fetch previous pattern from history file on first search. + +* Don't rewrite history file if it has not changed. + +* Don't move to bottom of screen on first page. + +* Don't output extraneous newlines, so copy & pasting lines from the + output works better. + +* The -c option has been made identical with the -C option. + +* Allow "/dev/null" as synomym for "-" in LESSHISTFILE to indicate + that no history file should be used. + +* Search can now find text which follows a null byte, if the PCRE + library is used, or if no-regex searching (ctrl-R) is used. + +* Better compatibility with POSIX more specification. + +* Make -f work for directories. + +* Make "t" cmd traverse tags in the correct order. + +* Allow a few binary characters in the input file before warning + that the file is binary. + +* Don't warn that file is binary if it merely contains ANSI color sequences + and -R is in effect. + +* Update Unicode character tables. + +* Support DESTDIR in Makefile. + +* Fix bug when filename contains certain shell metacharacters such as "$". + +* Fix bug when resizing the window while waiting for input from a pipe. + +* Fix configure bugs. + +====================================================================== + + Major changes between "less" versions 382 and 394 + +* Add history file to save search and shell command history between + invocations of less. + +* Improve behavior of history list for search and shell commands. + +* Add -K (or --quit-on-intr) option to make less exit immediately on ctrl-C. + +* Improve handling of UTF-8 files and commands, including better + line wrapping and handling double-width chars. + +* Added LESSUTFBINFMT environment variable to control display of + non-printable characters in a UTF-8 file. + +* Add --with-secure option to configure, to make it easier to + build a secure version of less. + +* Show search matches in the status column even if search highlights + are disabled via the -G option or the ESC-u command. + +* Improve performance when the file contains very long lines. + +* Add "windows" charset. + +* Add man page for lessecho. + +* Add support for erase2 character, treated same as erase. + +* Use ASCII lowercase/uppercase logic when operating on the command line. + +* Update makefile for Borland C++ 5.5.1. + +* Fix bug in calculating number of pages for %D prompt. + +* Fix bug in handling tag file error. + +* Fix obscure bug if input file is deleted while viewing help. + +* Fix bug handling filenames which include square brackets. + +* Fix possible buffer overflow in "global" tag search. + +* Fix possible buffer overflow in usage of LESSOPEN and LESSCLOSE. + +* Fix buffer overflow in reverse search. + +====================================================================== + + Major changes between "less" versions 381 and 382 + +* Removed some old copyrighted code. + This probably breaks OS/9 support. + +====================================================================== + + Major changes between "less" versions 378 and 381 + +* New -L option to disable LESSOPEN processing. + +* Further support for large (64 bit) file addressing. + Large file support is now set up by the configure script. + +* Use autoconf 2.54. + Replace configure.in, acconfig.h, defines.h.top with configure.ac. + +* Overstriking underscore with underscore is now bold or underlined + depending on context. + +* Use only 7 spaces for line numbers in -N mode, if possible. + +* Fix some bugs in handling overstriking in UTF-8 files. + +* Fix some nroff issues in the man page. + +====================================================================== + + Major changes between "less" versions 376 and 378 + +* Bug fixes: + Default buffer space is now 64K as documented. + Search highlighting works properly when used with -R. + Windows version works properly when input file contains carriage returns. + Clean up some compiler warnings. + +====================================================================== + + Major changes between "less" versions 358 and 376 + +* -x option can now specify multiple variable-width tab stops. + +* -X option no longer disables keypad initialization. + New option --no-keypad disables keypad initialization. + +* New commands t and T step through multiple tag matches. + Added support for "global(1)" tags + (see http://www.gnu.org/software/global/global.html). + +* New prompt style set by option -Pw defines the message printed + while waiting for data in the F command. + +* System-wide lesskey file now defaults to sysless in etc directory + instead of .sysless in bin directory. + Use "configure --sysconfdir=..." to change it. + (For backwards compatibility, .sysless in bin is still recognized.) + +* Pressing RightArrow or LeftArrow while entering a number now shifts + the display N columns rather than editing the number itself. + +* Status column (enabled with -J) now shows search results. + +* Windows version sets window title. + +* Default LESSCHARSET for MS-DOS versions is now "dos". + +* Searching works better with ANSI (SGR) escape sequences. + ANSI color escape sequences are now supported in the MS-DOS (DJGPP) version. + +* Improved performance in reading very large pipes. + +* Eliminated some dependencies on file offets being 32 bits. + +* Fixed problems when viewing files with very long lines. + +* Fixed overstriking in UTF-8 mode, and overstriking tabs. + +* Improved horizontal shifting of text using -R option with ANSI color. + +* Improved handling of filenames containing shell metacharacters. + +* Some fixes for EBCDIC systems. + +* Some fixes for OS/2 systems. + +====================================================================== + + Major changes between "less" versions 354 and 358 + +* Add -J (--status-column) option to display a status column. + +* Add -# (--shift) option to set default horizontal shift distance. + Default horizontal shift distance is now one-half screen width. + +* Horizontal shifting does not shift line numbers if -N is in effect. + +* Horizontal shifting acts as though -S were set, to avoid confusion. + +====================================================================== + + + Major changes between "less" versions 352 and 354 + +* Allow space after numeric-valued command line options. + +* Fix problem with configuring terminal libraries on some systems. + +* Add support for PCRE regular expression library. + +* Add --with-regex option to configure to allow manually selecting + a regular expression library. + +* Fix bug compiling with SECURE = 1. + +====================================================================== + + + Major changes between "less" versions 346 and 352 + +* Enable UTF-8 if "UTF-8" appears in locale-related environment variables. + +* Add --with-editor option to configure script. + +* The -M prompt and = message now show the top and bottom line number. + +* Fix bug in running the editor on a file whose name contains quotes, etc. + +* Fix bug in horizontal scrolling of long lines. + +* Fix bug in doing :d on a file which contains marks. + +* Fix bug causing cleared lines to sometimes be filled with standout, + bold, underline, etc. on certain terminals. + +* Fixes for MS-DOS (DJGPP) version. + +====================================================================== + + + Major changes between "less" versions 340 and 346 + +* The UTF-8 character set is now supported. + +* The default character set is now latin1 rather than ascii. + +* New option -R (--RAW-CONTROL-CHARS) is like -r but handles + long (wrapped) lines correctly, as long as the input contains only + normal text and ANSI color escape sequences. + +* New option -F (--quit-if-one-screen) quits if the text fits on + the first screen. + +* The -w option now highlights the target line of a g or p command. + +* A system-wide lesskey file is supported (LESSKEY_SYSTEM). + +* New escape for prompt strings: %c is replaced by column number. + +* New escape for prompt strings: %P is replaced by percentage into + file, based on line number rather than byte offset. + +* HOME and END keys now jump to beginning of file or end of file. + +====================================================================== + + + Major changes between "less" versions 337 and 340 + +* Command line options for less may now be given in either the old + single-letter form, or a new long name form (--option-name). + See the less man page or "less --help" for the list of long option names. + +* Command line options for lesskey may now be given in a new long name + form. See the lesskey man page for the list of long option names. + +* New command -- toggles an option using the long option name. + +* New command __ queries an option using the long option name. + +* The old -- command is renamed as -!. + +* If a ^P is entered between the dash and the option letter of the - + command, the message describing the new setting is suppressed. + +* Lesskey files may now contain \k escape sequences to represent the + "special" keys (arrows, PAGE-UP/PAGE-DOWN, HOME, END, INSERT, DELETE). + +* New command :d removes the current file from the list of files. + +* New option -~ (like -w before version 335) + suppresses tildes after end-of-file. + +* Less is now released under the GNU General Public License. + +====================================================================== + + + Major changes between "less" versions 335 and 337 + +* Fixed bugs in "make install". + +====================================================================== + + + Major changes between "less" versions 332 and 335 + +* The old -w flag (suppress tildes after end-of-file) has been removed. + +* New -w flag highlights the first new line after a forward-screen. + +* New -W flag highlights the first new line after any forward movement. + +* Window resize works even if LINES and/or COLUMNS environment + variables are incorrect. + +* New percent escapes for prompt strings: + %d is replaced by the page number, and + %D is replaced by the number of pages in the file. + +* Added charsets "iso8859" and "ebcdic". + +* In Windows version, uses HOMEDRIVE and HOMEPATH if HOME is not defined. + +* Fixed some bugs causing incorrect display on DOS/Windows. + +====================================================================== + + + Major changes between "less" versions 330 and 332 + +* Filenames from the command line are entered into the command history, + so UPARROW/DOWNARROW can be used to retrieve them from the :e command. + +* Now works correctly on Windows when using a scrolling terminal + window (buffer larger than display window). + +* On Windows, now restores the console screen on exit. + Use -X to get the old behavior. + +* Fixed bug on Windows when CAPS-LOCK or NUM-LOCK is pressed. + +* Fixed bug on Windows when piping output of an interactive program. + +* Fixed bug in tags file processing when tags file has DOS-style + line terminators (CR/LF). + +* Fixed compilation problem on OS/2. + +====================================================================== + + + Major changes between "less" versions 321 and 330 + +* Now supports filenames containing spaces (in double quotes). + New option -" can be used to change the quoting characters. + +* In filename completion, a slash is appended to a directory name. + If the environment variable LESSSEPARATOR is set, the value of + that variable, rather than a slash, is appended. + +* LeftArrow and RightArrow are same as ESC-[ and ESC-]. + +* Added commands ESC-( and ESC-), same as ESC-[ and ESC-]. + +* A "quit" command defined in a lesskey file may now have an "extra" + string, which is used to return an exit code from less when it quits. + +* New environment variables LESSMETACHARS and LESSMETAESCAPE provide + more control over how less interfaces to the shell. + +* Ported to Microsoft Visual C compiler for Windows. + +* Ported to DJGPP compiler for MS-DOS. + +* Bug fixes. + +====================================================================== + + + Major changes between "less" versions 291 and 321 + +* Command line at bottom of screen now scrolls, so it can be longer + than the screen width. + +* New commands ESC-] and ESC-[ scroll the display horizontally. + +* New command ESC-SPACE scrolls forward a full screen, even if it + hits end-of-file. + +* Alternate modifiers for search commands: ^N is same as !, + ^F is same as @, and ^E is same as *. + +* New modifier for search commands: ^K means highlight the matches + currently on-screen, but don't move to the first match. + +* New modifier for search commands: ^R means don't use regular + expressions in the search. + +* Environment variable LESSKEY gives name of default lesskey file. + +* Environment variable LESSSECURE will force less to run in + "secure" mode. + +* Command line argument "--" signals that the rest of the arguments + are files (not option flags). + +* Help file (less.hlp) is no longer installed. Help text is now + embedded in the less executable itself. + +* Added -Ph to change the prompt for the help text. + Added -Ps to change the default short prompt (same as plain -P). + +* Ported to the Borland C compiler for MS-DOS. + +* Ported to Windows 95 & Windows NT. + +* Ported to OS-9. + +* Ported to GNU Hurd. + +====================================================================== + + + Major changes between "less" versions 290 and 291 + +* Less environment variables can be specified in lesskey files. + +* Fixed MS-DOS build. + +====================================================================== + + + Major changes between "less" versions 278 and 290 + +* Accepts GNU-style options "--help" and "--version". + +* OS/2 version looks for less.ini in $HOME before $INIT and $PATH. + +* Bug fixes + +====================================================================== + + + Major changes between "less" versions 252 and 278 + +* A LESSOPEN preprocessor may now pipe the converted file data to less, + rather than writing it to a temporary file. + +* Search pattern highlighting has been fixed. It now highlights + reliably, even if a string is split across two screen lines, + contains TABs, etc. + +* The -F flag (which suppress search highlighting) has been changed + to -G. A new flag, -g, changes search highlighting to highlight + only the string found by the last search command, instead of all + strings which match the last search command. + +* New flag -I acts like -i, but ignores case even if the search + pattern contains uppercase letters. + +* Less now checks for the environment variable VISUAL before EDITOR. + +* Ported to OS/2. + +====================================================================== + + + Major changes between "less" versions 237 and 252 + +* Changes in line-editing keys: + The literal key is now ^V or ^A rather than \ (backslash). + Filename completion commands (TAB and ^L) are disabled + when typing a search pattern. + +* Line-editing command keys can be redefined using lesskey. + +* Lesskey with no input file defaults to $HOME/.lesskey + rather than standard input. + +* New option -V displays version number of less. + +* New option -V displays version number of lesskey. + +* Help file less.hlp is now installed by default in /usr/local/share + rather than /usr/local/lib. + + +====================================================================== + + + Major changes between "less" versions 170 and 237 + +* By popular demand, text which matches the current search pattern + is highlighted. New -F flag disables this feature. + +* Henry Spencer's regexp.c is now included, for systems which do not + have a regular expression library. + regexp.c is Copyright (c) 1986 by University of Toronto. + +* New line-editing keys, including command history (arrow keys) and + filename completion (TAB). + +* Input preprocessor allows modification of input files (e.g. uncompress) + via LESSOPEN/LESSCLOSE environment variables. + +* New -X flag disables sending termcap "ti" and "te" (initialize and + deinitialize) strings to the terminal. + +* Changing -i from within less now correctly affects a subsequent + repeated search. + +* Searching for underlined or overstruck text now works when the -u + flag is in effect, rather than the -i flag. + +* Use setlocale (LANG and LC_CTYPE environment variables) to determine + the character set if LESSCHARSET/LESSCHARDEF are not set. + +* The default format for displaying binary characters is now standout + (reverse video) rather than blinking. This can still be changed by + setting the LESSBINFMT environment variable. + +* Use autoconf installation technology. + +* Ported to MS-DOS. + + ******************************** + Things that may surprise you + ******************************** + +* When you enter text at the bottom of the screen (search string, + filename, etc.), some keys act different than previously. + Specifically, \ (backslash), ESC, TAB, BACKTAB, and control-L + now have line editing functions. + +* Some previous unofficial versions of less were able to display + compressed files. The new LESSOPEN/LESSCLOSE feature now provides + this functionality in a different way. + +* Some previous unofficial versions of less provided a -Z flag to + set the number of lines of text to retain between full screen scrolls. + The -z-n flag (that is, -z with a negative number) provides this + functionality. + + +====================================================================== + + + Major changes between "less" versions 123 and 170 + +* New option -j allows target lines to be positioned anywhere on screen. + +* New option -S truncates displayed line at the screen width, + rather than wrapping onto the next line. + +* New option -y limits amount of forward scroll. + +* New option -T specifies a "tags" file. + +* Non-printable, non-control characters are displayed in octal. + Such characters, as well as control characters, are displayed + in blinking mode. + +* New command -+ sets an option to its default. +* New command -- sets an option to the opposite of its default. + +* Lesskey file may have a string appended to a key's action, + which acts as though typed in after the command. + +* New commands ESC-^F and ESC-^B match arbitrary types of brackets. + +* New command F monitors a growing file (like "tail -f"). + +* New command | pipes a section of the input file into a shell command. + +* New command :x directly jumps to a file in the command line list. + +* Search commands have been enhanced and reorganized: + n Repeat search, same direction. + N Repeat search, opposite direction. + ESC-/ Search forward thru file boundaries + ESC-? Search backward thru file boundaries + ESC-n Repeat search thru file boundaries, same direction. + ESC-N Repeat search thru file boundaries, opposite direction. + Special character * causes search to search thru file boundaries. + Special character @ causes search to begin at start/end of file list. + +* Examining a new file adds it to the command line list. + A list of files, or an expression which matches more than one file, + may be examined; all of them are added to the command line list. + +* Environment variables LESSCHARSET and LESSCHARDEF can define + a non-ASCII character set. + +* Partial support for MSDOS, including options -R for repainting screen + on quit, -v/-V to select video mode, and -W to change window size. + + +====================================================================== + + + Major changes between "less" versions 97 and 123 + +* New option (-N) causes line numbers to be displayed in the + text of the file (like vi "set nu"). + +* New option (-?) prints help message immediately. + +* New option (-r) displays "raw" control characters, without + mapping them to ^X notation. + +* New option (-f) forces less to open non-regular files + (directories, etc). + +* New option (-k) can be used to specify lesskey files by name. + +* New option (-y) can be used to set a forward scroll limit + (like -h sets a backward scroll limit). + +* File marks (set by the m command) are now preserved when a new + file is edited. The ' command can thus be used to switch files. + +* New command ESC-/ searches all files (on the command line) + for a pattern. + +* New command ESC-n repeats previous search, spanning files. + +* The N command has been changed to repeat the previous search + in the reverse direction. The old N command is still available + via :n. + +* New command ESC-N repeats previous search in the reverse + direction and spanning files. + +* 8 bit characters are now supported. A new option (-g) can be + used to strip off the eighth bit (the previous behavior). + +* Options which take a following string (like -t) may now + optionally have a space between the option letter and the string. + +* Six new commands { } ( ) [ and ] can be used to match + brackets of specific types, similar to vi % command. + +* New commands z and w move forward/backward one window and + simultaneously set the window size. + +* Prompt string expansion now has %L for line number of the last + line in the file, and %E for the name of the editor. + Also, % escapes which refer to a line (b=bottom, t=top, etc.) + can use j for the jump target line. + +* New environment variable LESSEDIT can be used to tailor the + command string passed to the editor by the v command. + +* Examining a file which was previously examined will return + to the same position in the file. + +* A "%" is expanded to the current filename and a "#" to the + previous filename, in both shell commands and the E command. + (Previously % worked only in shell commands and # worked + only in the E command.) + +* New command ":ta" is equivalent to "-t". + +* New command "s" is equivalent to "-l". + +* The - command may be followed by "+X" to revert to the default + for option X, or "-X" to get the opposite of the default. + +* Lesskey files may now include characters after the action as + extra input to be parsed after the action; for example: + "toggle-option X" to toggle a specific option X. + + + + + diff --git a/files/Sources/files/less/README b/files/Sources/files/less/README new file mode 100644 index 00000000..ee8903de --- /dev/null +++ b/files/Sources/files/less/README @@ -0,0 +1,238 @@ + + Less, version 487 + + This is the distribution of less, version 487, released 25 Oct 2016. + This program is part of the GNU project (http://www.gnu.org). + + This program is free software. You may redistribute it and/or + modify it under the terms of either: + + 1. The GNU General Public License, as published by the Free + Software Foundation; either version 3, or (at your option) any + later version. A copy of this license is in the file COPYING. + or + 2. The Less License, in the file LICENSE. + + Please report any problems to bug-less@gnu.org. + See http://www.greenwoodsoftware.com/less for the latest info. + +========================================================================= + +This is the distribution of "less", a paginator similar to "more" or "pg". + +The formatted manual page is in less.man. +The manual page nroff source is in less.nro. +Major changes made since the last posted version are in NEWS. + +======================================================================= +INSTALLATION (Unix systems only): + +1. Move the distributed source to its own directory and unpack it, + if you have not already done so. + +2. Type "sh configure". + This will generate a Makefile and a defines.h. + Warning: if you have a GNU sed, make sure it is version 2.05 or later. + + The file INSTALL describes the usage of the configure program in + general. In addition, these options to configure are supported: + + --with-editor=program + Specifies the default editor program used by the "v" command. + The default is "vi". + + --with-regex=lib + Specifies the regular expression library used by less for pattern + matching. The default is "auto", which means the configure program + finds a regular expression library automatically. Other values are: + gnu Use the GNU regex library. + pcre Use the PCRE library. + posix Use the POSIX-compatible regcomp. + regcmp Use the regcmp library. + re_comp Use the re_comp library. + regcomp Use the V8-compatible regcomp. + regcomp-local Use Henry Spencer's V8-compatible regcomp + (source is supplied with less). + none No regular expressions, only simple string matching. + --with-secure + Builds a "secure" version of less, with some features disabled + to prevent users from viewing other files, accessing shell + commands, etc. + + +3. It is a good idea to look over the generated Makefile and defines.h + and make sure they look ok. If you know of any peculiarities of + your system that configure might not have detected, you may fix the + Makefile now. Take particular notice of the list of "terminal" + libraries in the LIBS definition in the Makefile; these may need + to be edited. The terminal libraries will be some subset of + -lncurses -lcurses -ltermcap -ltermlib + + If you wish, you may edit defines.h to remove some optional features. + If you choose not to include some features in your version, you may + wish to edit the manual page "less.nro" and the help page "less.hlp" + to remove the descriptions of the features which you are removing. + If you edit less.hlp, you should run "make -f Makefile.aut help.c". + +4. Type "make" and watch the fun. + +5. If the make succeeds, it will generate the programs "less", + "lesskey" and "lessecho" in your current directory. Test the + generated programs. + +6. When satisfied that it works, if you wish to install it + in a public place, type "make install". + + The default install destinations are: + Executables (less, lesskey, lessecho) in /usr/local/bin + Documentation (less.nro, lesskey.nro) in /usr/local/man/man1 + If you want to install any of these files elsewhere, define + bindir and/or mandir to the appropriate directories. + +If you have any problems building or running "less", suggestions, +complaints, etc., you may mail to bug-less@gnu.org. + +Note to hackers: comments noting possible improvements are enclosed +in double curly brackets {{ like this }}. + +(Note that the above note was originally written at a time when +"hackers" most commonly meant "enthusiastic and dedicated computer +programmers", not "persons who attempt to circumvent computer security".) + + + +======================================================================= +INSTALLATION (MS-DOS systems only, + with Microsoft C, Borland C, or DJGPP) + +1. Move the distributed source to its own directory. + Depending on your compiler, you may need to convert the source + to have CR-LF rather than LF as line terminators. + +2. If you are using Microsoft C, rename MAKEFILE.DSU to MAKEFILE. + If you are using Borland C, rename MAKEFILE.DSB to MAKEFILE. + If you are using DJGPP, rename MAKEFILE.DSG to MAKEFILE. + +3. Look at MAKEFILE to make sure that the definitions for CC and LIBDIR + are correct. CC should be the name of your C compiler and + LIBDIR should be the directory where the C libraries reside (for + Microsoft C only). If these definitions need to be changed, you can + either modify the definitions directly in MAKEFILE, or set your + environment variables CC and/or LIBDIR to override the definitions + in MAKEFILE. + +4. If you wish, you may edit DEFINES.DS to remove some optional features. + If you choose not to include some features in your version, you may + wish to edit the manual page LESS.MAN and the help page HELP.C + to remove the descriptions of the features which you are removing. + +5. Run your "make" program and watch the fun. + If your "make" requires a flag to import environment variables, + you should use that flag. + If your compiler runs out of memory, try running "make -n >cmds.bat" + and then run cmds.bat. + +6. If the make succeeds, it will generate the programs "LESS.EXE" and + "LESSKEY.EXE" in your current directory. Test the generated programs. + +7. When satisfied that it works, you may wish to install LESS.EXE and + LESSKEY.EXE in a directory which is included in your PATH. + + + +======================================================================= +INSTALLATION (Windows-95, Windows-98 and Windows-NT systems only, + with Borland C or Microsoft Visual C++) + +1. Move the distributed source to its own directory. + +2. If you are using Borland C, rename Makefile.wnb to Makefile. + If you are using Microsoft Visual C++, rename Makefile.wnm to Makefile. + +3. Check the Makefile to make sure the definitions look ok. + +4. If you wish, you may edit defines.wn to remove some optional features. + If you choose not to include some features in your version, you may + wish to edit the manual page less.man and the help page help.c + to remove the descriptions of the features which you are removing. + +5. Type "make" and watch the fun. + +6. If the make succeeds, it will generate the programs "less.exe" and + "lesskey.exe" in your current directory. Test the generated programs. + +7. When satisfied that it works, if you wish to install it + in a public place, type "make install". + See step 6 of the Unix installation instructions for details + on how to change the default installation directories. + + + +======================================================================= +INSTALLATION (OS/2 systems only, + with EMX C) + +1. Move the distributed source to its own directory. + +2. Rename Makefile.o2e to Makefile. + +3. Check the Makefile to make sure the definitions look ok. + +4. If you wish, you may edit defines.o2 to remove some optional features. + If you choose not to include some features in your version, you may + wish to edit the manual page less.man and the help page help.c + to remove the descriptions of the features which you are removing. + +5. Type "make" and watch the fun. + +6. If the make succeeds, it will generate the programs "less.exe" and + "lesskey.exe" in your current directory. Test the generated programs. + +7. Make sure you have the emx runtime installed. You need the emx DLLs + emx.dll and emxlibcs.dll and also the termcap database, termcap.dat. + Make sure you have termcap.dat either in the default location or + somewhere in a directory listed in the PATH or INIT environment + variables. + +8. When satisfied that it works, you may wish to install less.exe, + lesskey.exe and scrsize.exe in a directory which is included in + your PATH. scrsize.exe is required only if you use a terminal + emulator such as xterm or rxvt. + + + +======================================================================= +INSTALLATION (OS-9 systems only, + with Microware C or Ultra C) + +1. Move the distributed source to its own directory. + +2. If you are using Microware C, rename Makefile.o9c to Makefile. + If you are using Ultra C, rename Makefile.o9u to Makefile. + +3. Check the Makefile to make sure the definitions look ok. + +4. If you wish, you may edit defines.o9 to remove some optional features. + If you choose not to include some features in your version, you may + wish to edit the manual page less.man and the help page help.c + to remove the descriptions of the features which you are removing. + +5. Type "dmake" and watch the fun. + The standard OS-9 "make" will probably not work. If you don't + have dmake, you can get a copy from os9archive.rtsi.com. + +6. If the make succeeds, it will generate the programs "less" and + "lesskey" in your current directory. Test the generated programs. + +7. When satisfied that it works, if you wish to install it + in a public place, type "dmake install". + See step 6 of the Unix installation instructions for details + on how to change the default installation directories. + +======================================================================= +ACKNOWLEDGMENTS: + Some versions of the less distribution are packaged using + Info-ZIP's compression utility. + Info-ZIP's software is free and can be obtained as source + code or executables from various anonymous-ftp sites, + including ftp.uu.net:/pub/archiving/zip. diff --git a/files/Sources/files/less/brac.c b/files/Sources/files/less/brac.c new file mode 100644 index 00000000..5119f4e5 --- /dev/null +++ b/files/Sources/files/less/brac.c @@ -0,0 +1,100 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Routines to perform bracket matching functions. + */ + +#include "less.h" +#include "position.h" + +/* + * Try to match the n-th open bracket + * which appears in the top displayed line (forwdir), + * or the n-th close bracket + * which appears in the bottom displayed line (!forwdir). + * The characters which serve as "open bracket" and + * "close bracket" are given. + */ + public void +match_brac(obrac, cbrac, forwdir, n) + register int obrac; + register int cbrac; + int forwdir; + int n; +{ + register int c; + register int nest; + POSITION pos; + int (*chget)(); + + extern int ch_forw_get(), ch_back_get(); + + /* + * Seek to the line containing the open bracket. + * This is either the top or bottom line on the screen, + * depending on the type of bracket. + */ + pos = position((forwdir) ? TOP : BOTTOM); + if (pos == NULL_POSITION || ch_seek(pos)) + { + if (forwdir) + error("Nothing in top line", NULL_PARG); + else + error("Nothing in bottom line", NULL_PARG); + return; + } + + /* + * Look thru the line to find the open bracket to match. + */ + do + { + if ((c = ch_forw_get()) == '\n' || c == EOI) + { + if (forwdir) + error("No bracket in top line", NULL_PARG); + else + error("No bracket in bottom line", NULL_PARG); + return; + } + } while (c != obrac || --n > 0); + + /* + * Position the file just "after" the open bracket + * (in the direction in which we will be searching). + * If searching forward, we are already after the bracket. + * If searching backward, skip back over the open bracket. + */ + if (!forwdir) + (void) ch_back_get(); + + /* + * Search the file for the matching bracket. + */ + chget = (forwdir) ? ch_forw_get : ch_back_get; + nest = 0; + while ((c = (*chget)()) != EOI) + { + if (c == obrac) + nest++; + else if (c == cbrac && --nest < 0) + { + /* + * Found the matching bracket. + * If searching backward, put it on the top line. + * If searching forward, put it on the bottom line. + */ + jump_line_loc(ch_tell(), forwdir ? -1 : 1); + return; + } + } + error("No matching bracket", NULL_PARG); +} diff --git a/files/Sources/files/less/ch.c b/files/Sources/files/less/ch.c new file mode 100644 index 00000000..af694051 --- /dev/null +++ b/files/Sources/files/less/ch.c @@ -0,0 +1,974 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Low level character input from the input file. + * We use these special purpose routines which optimize moving + * both forward and backward from the current read pointer. + */ + +#include "less.h" +#if MSDOS_COMPILER==WIN32C +#include +#include +#endif + +#if HAVE_STAT_INO +#include +extern dev_t curr_dev; +extern ino_t curr_ino; +#endif + +typedef POSITION BLOCKNUM; + +public int ignore_eoi; + +/* + * Pool of buffers holding the most recently used blocks of the input file. + * The buffer pool is kept as a doubly-linked circular list, + * in order from most- to least-recently used. + * The circular list is anchored by the file state "thisfile". + */ +struct bufnode { + struct bufnode *next, *prev; + struct bufnode *hnext, *hprev; +}; + +#define LBUFSIZE 8192 +struct buf { + struct bufnode node; + BLOCKNUM block; + unsigned int datasize; + unsigned char data[LBUFSIZE]; +}; +#define bufnode_buf(bn) ((struct buf *) bn) + +/* + * The file state is maintained in a filestate structure. + * A pointer to the filestate is kept in the ifile structure. + */ +#define BUFHASH_SIZE 1024 +struct filestate { + struct bufnode buflist; + struct bufnode hashtbl[BUFHASH_SIZE]; + int file; + int flags; + POSITION fpos; + int nbufs; + BLOCKNUM block; + unsigned int offset; + POSITION fsize; +}; + +#define ch_bufhead thisfile->buflist.next +#define ch_buftail thisfile->buflist.prev +#define ch_nbufs thisfile->nbufs +#define ch_block thisfile->block +#define ch_offset thisfile->offset +#define ch_fpos thisfile->fpos +#define ch_fsize thisfile->fsize +#define ch_flags thisfile->flags +#define ch_file thisfile->file + +#define END_OF_CHAIN (&thisfile->buflist) +#define END_OF_HCHAIN(h) (&thisfile->hashtbl[h]) +#define BUFHASH(blk) ((blk) & (BUFHASH_SIZE-1)) + +/* + * Macros to manipulate the list of buffers in thisfile->buflist. + */ +#define FOR_BUFS(bn) \ + for (bn = ch_bufhead; bn != END_OF_CHAIN; bn = bn->next) + +#define BUF_RM(bn) \ + (bn)->next->prev = (bn)->prev; \ + (bn)->prev->next = (bn)->next; + +#define BUF_INS_HEAD(bn) \ + (bn)->next = ch_bufhead; \ + (bn)->prev = END_OF_CHAIN; \ + ch_bufhead->prev = (bn); \ + ch_bufhead = (bn); + +#define BUF_INS_TAIL(bn) \ + (bn)->next = END_OF_CHAIN; \ + (bn)->prev = ch_buftail; \ + ch_buftail->next = (bn); \ + ch_buftail = (bn); + +/* + * Macros to manipulate the list of buffers in thisfile->hashtbl[n]. + */ +#define FOR_BUFS_IN_CHAIN(h,bn) \ + for (bn = thisfile->hashtbl[h].hnext; \ + bn != END_OF_HCHAIN(h); bn = bn->hnext) + +#define BUF_HASH_RM(bn) \ + (bn)->hnext->hprev = (bn)->hprev; \ + (bn)->hprev->hnext = (bn)->hnext; + +#define BUF_HASH_INS(bn,h) \ + (bn)->hnext = thisfile->hashtbl[h].hnext; \ + (bn)->hprev = END_OF_HCHAIN(h); \ + thisfile->hashtbl[h].hnext->hprev = (bn); \ + thisfile->hashtbl[h].hnext = (bn); + +static struct filestate *thisfile; +static int ch_ungotchar = -1; +static int maxbufs = -1; + +extern int autobuf; +extern int sigs; +extern int secure; +extern int screen_trashed; +extern int follow_mode; +extern constant char helpdata[]; +extern constant int size_helpdata; +extern IFILE curr_ifile; +#if LOGFILE +extern int logfile; +extern char *namelogfile; +#endif + +static int ch_addbuf(); + + +/* + * Get the character pointed to by the read pointer. + */ + int +ch_get() +{ + register struct buf *bp; + register struct bufnode *bn; + register int n; + register int slept; + register int h; + POSITION pos; + POSITION len; + + if (thisfile == NULL) + return (EOI); + + /* + * Quick check for the common case where + * the desired char is in the head buffer. + */ + if (ch_bufhead != END_OF_CHAIN) + { + bp = bufnode_buf(ch_bufhead); + if (ch_block == bp->block && ch_offset < bp->datasize) + return bp->data[ch_offset]; + } + + slept = FALSE; + + /* + * Look for a buffer holding the desired block. + */ + h = BUFHASH(ch_block); + FOR_BUFS_IN_CHAIN(h, bn) + { + bp = bufnode_buf(bn); + if (bp->block == ch_block) + { + if (ch_offset >= bp->datasize) + /* + * Need more data in this buffer. + */ + break; + goto found; + } + } + if (bn == END_OF_HCHAIN(h)) + { + /* + * Block is not in a buffer. + * Take the least recently used buffer + * and read the desired block into it. + * If the LRU buffer has data in it, + * then maybe allocate a new buffer. + */ + if (ch_buftail == END_OF_CHAIN || + bufnode_buf(ch_buftail)->block != -1) + { + /* + * There is no empty buffer to use. + * Allocate a new buffer if: + * 1. We can't seek on this file and -b is not in effect; or + * 2. We haven't allocated the max buffers for this file yet. + */ + if ((autobuf && !(ch_flags & CH_CANSEEK)) || + (maxbufs < 0 || ch_nbufs < maxbufs)) + if (ch_addbuf()) + /* + * Allocation failed: turn off autobuf. + */ + autobuf = OPT_OFF; + } + bn = ch_buftail; + bp = bufnode_buf(bn); + BUF_HASH_RM(bn); /* Remove from old hash chain. */ + bp->block = ch_block; + bp->datasize = 0; + BUF_HASH_INS(bn, h); /* Insert into new hash chain. */ + } + + read_more: + pos = (ch_block * LBUFSIZE) + bp->datasize; + if ((len = ch_length()) != NULL_POSITION && pos >= len) + /* + * At end of file. + */ + return (EOI); + + if (pos != ch_fpos) + { + /* + * Not at the correct position: must seek. + * If input is a pipe, we're in trouble (can't seek on a pipe). + * Some data has been lost: just return "?". + */ + if (!(ch_flags & CH_CANSEEK)) + return ('?'); + if (lseek(ch_file, (off_t)pos, SEEK_SET) == BAD_LSEEK) + { + error("seek error", NULL_PARG); + clear_eol(); + return (EOI); + } + ch_fpos = pos; + } + + /* + * Read the block. + * If we read less than a full block, that's ok. + * We use partial block and pick up the rest next time. + */ + if (ch_ungotchar != -1) + { + bp->data[bp->datasize] = ch_ungotchar; + n = 1; + ch_ungotchar = -1; + } else if (ch_flags & CH_HELPFILE) + { + bp->data[bp->datasize] = helpdata[ch_fpos]; + n = 1; + } else + { + n = iread(ch_file, &bp->data[bp->datasize], + (unsigned int)(LBUFSIZE - bp->datasize)); + } + + if (n == READ_INTR) + return (EOI); + if (n < 0) + { +#if MSDOS_COMPILER==WIN32C + if (errno != EPIPE) +#endif + { + error("read error", NULL_PARG); + clear_eol(); + } + n = 0; + } + +#if LOGFILE + /* + * If we have a log file, write the new data to it. + */ + if (!secure && logfile >= 0 && n > 0) + write(logfile, (char *) &bp->data[bp->datasize], n); +#endif + + ch_fpos += n; + bp->datasize += n; + + /* + * If we have read to end of file, set ch_fsize to indicate + * the position of the end of file. + */ + if (n == 0) + { + ch_fsize = pos; + if (ignore_eoi) + { + /* + * We are ignoring EOF. + * Wait a while, then try again. + */ + if (!slept) + { + PARG parg; + parg.p_string = wait_message(); + ierror("%s", &parg); + } +#if !MSDOS_COMPILER + sleep(1); +#else +#if MSDOS_COMPILER==WIN32C + Sleep(1000); +#endif +#endif + slept = TRUE; + +#if HAVE_STAT_INO + if (follow_mode == FOLLOW_NAME) + { + /* See whether the file's i-number has changed, + * or the file has shrunk. + * If so, force the file to be closed and + * reopened. */ + struct stat st; + POSITION curr_pos = ch_tell(); + int r = stat(get_filename(curr_ifile), &st); + if (r == 0 && (st.st_ino != curr_ino || + st.st_dev != curr_dev || + (curr_pos != NULL_POSITION && st.st_size < curr_pos))) + { + /* screen_trashed=2 causes + * make_display to reopen the file. */ + screen_trashed = 2; + return (EOI); + } + } +#endif + } + if (sigs) + return (EOI); + } + + found: + if (ch_bufhead != bn) + { + /* + * Move the buffer to the head of the buffer chain. + * This orders the buffer chain, most- to least-recently used. + */ + BUF_RM(bn); + BUF_INS_HEAD(bn); + + /* + * Move to head of hash chain too. + */ + BUF_HASH_RM(bn); + BUF_HASH_INS(bn, h); + } + + if (ch_offset >= bp->datasize) + /* + * After all that, we still don't have enough data. + * Go back and try again. + */ + goto read_more; + + return (bp->data[ch_offset]); +} + +/* + * ch_ungetchar is a rather kludgy and limited way to push + * a single char onto an input file descriptor. + */ + public void +ch_ungetchar(c) + int c; +{ + if (c != -1 && ch_ungotchar != -1) + error("ch_ungetchar overrun", NULL_PARG); + ch_ungotchar = c; +} + +#if LOGFILE +/* + * Close the logfile. + * If we haven't read all of standard input into it, do that now. + */ + public void +end_logfile() +{ + static int tried = FALSE; + + if (logfile < 0) + return; + if (!tried && ch_fsize == NULL_POSITION) + { + tried = TRUE; + ierror("Finishing logfile", NULL_PARG); + while (ch_forw_get() != EOI) + if (ABORT_SIGS()) + break; + } + close(logfile); + logfile = -1; + namelogfile = NULL; +} + +/* + * Start a log file AFTER less has already been running. + * Invoked from the - command; see toggle_option(). + * Write all the existing buffered data to the log file. + */ + public void +sync_logfile() +{ + register struct buf *bp; + register struct bufnode *bn; + int warned = FALSE; + BLOCKNUM block; + BLOCKNUM nblocks; + + nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE; + for (block = 0; block < nblocks; block++) + { + int wrote = FALSE; + FOR_BUFS(bn) + { + bp = bufnode_buf(bn); + if (bp->block == block) + { + write(logfile, (char *) bp->data, bp->datasize); + wrote = TRUE; + break; + } + } + if (!wrote && !warned) + { + error("Warning: log file is incomplete", + NULL_PARG); + warned = TRUE; + } + } +} + +#endif + +/* + * Determine if a specific block is currently in one of the buffers. + */ + static int +buffered(block) + BLOCKNUM block; +{ + register struct buf *bp; + register struct bufnode *bn; + register int h; + + h = BUFHASH(block); + FOR_BUFS_IN_CHAIN(h, bn) + { + bp = bufnode_buf(bn); + if (bp->block == block) + return (TRUE); + } + return (FALSE); +} + +/* + * Seek to a specified position in the file. + * Return 0 if successful, non-zero if can't seek there. + */ + public int +ch_seek(pos) + register POSITION pos; +{ + BLOCKNUM new_block; + POSITION len; + + if (thisfile == NULL) + return (0); + + len = ch_length(); + if (pos < ch_zero() || (len != NULL_POSITION && pos > len)) + return (1); + + new_block = pos / LBUFSIZE; + if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos && !buffered(new_block)) + { + if (ch_fpos > pos) + return (1); + while (ch_fpos < pos) + { + if (ch_forw_get() == EOI) + return (1); + if (ABORT_SIGS()) + return (1); + } + return (0); + } + /* + * Set read pointer. + */ + ch_block = new_block; + ch_offset = pos % LBUFSIZE; + return (0); +} + +/* + * Seek to the end of the file. + */ + public int +ch_end_seek() +{ + POSITION len; + + if (thisfile == NULL) + return (0); + + if (ch_flags & CH_CANSEEK) + ch_fsize = filesize(ch_file); + + len = ch_length(); + if (len != NULL_POSITION) + return (ch_seek(len)); + + /* + * Do it the slow way: read till end of data. + */ + while (ch_forw_get() != EOI) + if (ABORT_SIGS()) + return (1); + return (0); +} + +/* + * Seek to the last position in the file that is currently buffered. + */ + public int +ch_end_buffer_seek() +{ + register struct buf *bp; + register struct bufnode *bn; + POSITION buf_pos; + POSITION end_pos; + + if (thisfile == NULL || (ch_flags & CH_CANSEEK)) + return (ch_end_seek()); + + end_pos = 0; + FOR_BUFS(bn) + { + bp = bufnode_buf(bn); + buf_pos = (bp->block * LBUFSIZE) + bp->datasize; + if (buf_pos > end_pos) + end_pos = buf_pos; + } + + return (ch_seek(end_pos)); +} + +/* + * Seek to the beginning of the file, or as close to it as we can get. + * We may not be able to seek there if input is a pipe and the + * beginning of the pipe is no longer buffered. + */ + public int +ch_beg_seek() +{ + register struct bufnode *bn; + register struct bufnode *firstbn; + + /* + * Try a plain ch_seek first. + */ + if (ch_seek(ch_zero()) == 0) + return (0); + + /* + * Can't get to position 0. + * Look thru the buffers for the one closest to position 0. + */ + firstbn = ch_bufhead; + if (firstbn == END_OF_CHAIN) + return (1); + FOR_BUFS(bn) + { + if (bufnode_buf(bn)->block < bufnode_buf(firstbn)->block) + firstbn = bn; + } + ch_block = bufnode_buf(firstbn)->block; + ch_offset = 0; + return (0); +} + +/* + * Return the length of the file, if known. + */ + public POSITION +ch_length() +{ + if (thisfile == NULL) + return (NULL_POSITION); + if (ignore_eoi) + return (NULL_POSITION); + if (ch_flags & CH_HELPFILE) + return (size_helpdata); + if (ch_flags & CH_NODATA) + return (0); + return (ch_fsize); +} + +/* + * Return the current position in the file. + */ + public POSITION +ch_tell() +{ + if (thisfile == NULL) + return (NULL_POSITION); + return (ch_block * LBUFSIZE) + ch_offset; +} + +/* + * Get the current char and post-increment the read pointer. + */ + public int +ch_forw_get() +{ + register int c; + + if (thisfile == NULL) + return (EOI); + c = ch_get(); + if (c == EOI) + return (EOI); + if (ch_offset < LBUFSIZE-1) + ch_offset++; + else + { + ch_block ++; + ch_offset = 0; + } + return (c); +} + +/* + * Pre-decrement the read pointer and get the new current char. + */ + public int +ch_back_get() +{ + if (thisfile == NULL) + return (EOI); + if (ch_offset > 0) + ch_offset --; + else + { + if (ch_block <= 0) + return (EOI); + if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1)) + return (EOI); + ch_block--; + ch_offset = LBUFSIZE-1; + } + return (ch_get()); +} + +/* + * Set max amount of buffer space. + * bufspace is in units of 1024 bytes. -1 mean no limit. + */ + public void +ch_setbufspace(bufspace) + int bufspace; +{ + if (bufspace < 0) + maxbufs = -1; + else + { + maxbufs = ((bufspace * 1024) + LBUFSIZE-1) / LBUFSIZE; + if (maxbufs < 1) + maxbufs = 1; + } +} + +/* + * Flush (discard) any saved file state, including buffer contents. + */ + public void +ch_flush() +{ + register struct bufnode *bn; + + if (thisfile == NULL) + return; + + if (!(ch_flags & CH_CANSEEK)) + { + /* + * If input is a pipe, we don't flush buffer contents, + * since the contents can't be recovered. + */ + ch_fsize = NULL_POSITION; + return; + } + + /* + * Initialize all the buffers. + */ + FOR_BUFS(bn) + { + bufnode_buf(bn)->block = -1; + } + + /* + * Figure out the size of the file, if we can. + */ + ch_fsize = filesize(ch_file); + + /* + * Seek to a known position: the beginning of the file. + */ + ch_fpos = 0; + ch_block = 0; /* ch_fpos / LBUFSIZE; */ + ch_offset = 0; /* ch_fpos % LBUFSIZE; */ + +#if 1 + /* + * This is a kludge to workaround a Linux kernel bug: files in + * /proc have a size of 0 according to fstat() but have readable + * data. They are sometimes, but not always, seekable. + * Force them to be non-seekable here. + */ + if (ch_fsize == 0) + { + ch_fsize = NULL_POSITION; + ch_flags &= ~CH_CANSEEK; + } +#endif + + if (lseek(ch_file, (off_t)0, SEEK_SET) == BAD_LSEEK) + { + /* + * Warning only; even if the seek fails for some reason, + * there's a good chance we're at the beginning anyway. + * {{ I think this is bogus reasoning. }} + */ + error("seek error to 0", NULL_PARG); + } +} + +/* + * Allocate a new buffer. + * The buffer is added to the tail of the buffer chain. + */ + static int +ch_addbuf() +{ + register struct buf *bp; + register struct bufnode *bn; + + /* + * Allocate and initialize a new buffer and link it + * onto the tail of the buffer list. + */ + bp = (struct buf *) calloc(1, sizeof(struct buf)); + if (bp == NULL) + return (1); + ch_nbufs++; + bp->block = -1; + bn = &bp->node; + + BUF_INS_TAIL(bn); + BUF_HASH_INS(bn, 0); + return (0); +} + +/* + * + */ + static void +init_hashtbl() +{ + register int h; + + for (h = 0; h < BUFHASH_SIZE; h++) + { + thisfile->hashtbl[h].hnext = END_OF_HCHAIN(h); + thisfile->hashtbl[h].hprev = END_OF_HCHAIN(h); + } +} + +/* + * Delete all buffers for this file. + */ + static void +ch_delbufs() +{ + register struct bufnode *bn; + + while (ch_bufhead != END_OF_CHAIN) + { + bn = ch_bufhead; + BUF_RM(bn); + free(bufnode_buf(bn)); + } + ch_nbufs = 0; + init_hashtbl(); +} + +/* + * Is it possible to seek on a file descriptor? + */ + public int +seekable(f) + int f; +{ +#if MSDOS_COMPILER + extern int fd0; + if (f == fd0 && !ios_isatty(fd0)) + { + /* + * In MS-DOS, pipes are seekable. Check for + * standard input, and pretend it is not seekable. + */ + return (0); + } +#endif + return (lseek(f, (off_t)1, SEEK_SET) != BAD_LSEEK); +} + +/* + * Force EOF to be at the current read position. + * This is used after an ignore_eof read, during which the EOF may change. + */ + public void +ch_set_eof() +{ + ch_fsize = ch_fpos; +} + + +/* + * Initialize file state for a new file. + */ + public void +ch_init(f, flags) + int f; + int flags; +{ + /* + * See if we already have a filestate for this file. + */ + thisfile = (struct filestate *) get_filestate(curr_ifile); + if (thisfile == NULL) + { + /* + * Allocate and initialize a new filestate. + */ + thisfile = (struct filestate *) + calloc(1, sizeof(struct filestate)); + thisfile->buflist.next = thisfile->buflist.prev = END_OF_CHAIN; + thisfile->nbufs = 0; + thisfile->flags = 0; + thisfile->fpos = 0; + thisfile->block = 0; + thisfile->offset = 0; + thisfile->file = -1; + thisfile->fsize = NULL_POSITION; + ch_flags = flags; + init_hashtbl(); + /* + * Try to seek; set CH_CANSEEK if it works. + */ + if ((flags & CH_CANSEEK) && !seekable(f)) + ch_flags &= ~CH_CANSEEK; + set_filestate(curr_ifile, (void *) thisfile); + } + if (thisfile->file == -1) + thisfile->file = f; + ch_flush(); +} + +/* + * Close a filestate. + */ + public void +ch_close() +{ + int keepstate = FALSE; + + if (thisfile == NULL) + return; + + if (ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE)) + { + /* + * We can seek or re-open, so we don't need to keep buffers. + */ + ch_delbufs(); + } else + keepstate = TRUE; + if (!(ch_flags & CH_KEEPOPEN)) + { + /* + * We don't need to keep the file descriptor open + * (because we can re-open it.) + * But don't really close it if it was opened via popen(), + * because pclose() wants to close it. + */ + if (!(ch_flags & (CH_POPENED|CH_HELPFILE))) + close(ch_file); + ch_file = -1; + } else + keepstate = TRUE; + if (!keepstate) + { + /* + * We don't even need to keep the filestate structure. + */ + free(thisfile); + thisfile = NULL; + set_filestate(curr_ifile, (void *) NULL); + } +} + +/* + * Return ch_flags for the current file. + */ + public int +ch_getflags() +{ + if (thisfile == NULL) + return (0); + return (ch_flags); +} + +#if 0 + public void +ch_dump(struct filestate *fs) +{ + struct buf *bp; + struct bufnode *bn; + unsigned char *s; + + if (fs == NULL) + { + printf(" --no filestate\n"); + return; + } + printf(" file %d, flags %x, fpos %x, fsize %x, blk/off %x/%x\n", + fs->file, fs->flags, fs->fpos, + fs->fsize, fs->block, fs->offset); + printf(" %d bufs:\n", fs->nbufs); + for (bn = fs->next; bn != &fs->buflist; bn = bn->next) + { + bp = bufnode_buf(bn); + printf("%x: blk %x, size %x \"", + bp, bp->block, bp->datasize); + for (s = bp->data; s < bp->data + 30; s++) + if (*s >= ' ' && *s < 0x7F) + printf("%c", *s); + else + printf("."); + printf("\"\n"); + } +} +#endif diff --git a/files/Sources/files/less/charset.c b/files/Sources/files/less/charset.c new file mode 100644 index 00000000..16613cc7 --- /dev/null +++ b/files/Sources/files/less/charset.c @@ -0,0 +1,825 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Functions to define the character set + * and do things specific to the character set. + */ + +#include "less.h" +#if HAVE_LOCALE +#include +#include +#include +#endif + +#include "charset.h" + +public int utf_mode = 0; + +/* + * Predefined character sets, + * selected by the LESSCHARSET environment variable. + */ +struct charset { + char *name; + int *p_flag; + char *desc; +} charsets[] = { + { "ascii", NULL, "8bcccbcc18b95.b" }, + { "utf-8", &utf_mode, "8bcccbcc18b95.b126.bb" }, + { "iso8859", NULL, "8bcccbcc18b95.33b." }, + { "latin3", NULL, "8bcccbcc18b95.33b5.b8.b15.b4.b12.b18.b12.b." }, + { "arabic", NULL, "8bcccbcc18b95.33b.3b.7b2.13b.3b.b26.5b19.b" }, + { "greek", NULL, "8bcccbcc18b95.33b4.2b4.b3.b35.b44.b" }, + { "greek2005", NULL, "8bcccbcc18b95.33b14.b35.b44.b" }, + { "hebrew", NULL, "8bcccbcc18b95.33b.b29.32b28.2b2.b" }, + { "koi8-r", NULL, "8bcccbcc18b95.b." }, + { "KOI8-T", NULL, "8bcccbcc18b95.b8.b6.b8.b.b.5b7.3b4.b4.b3.b.b.3b." }, + { "georgianps", NULL, "8bcccbcc18b95.3b11.4b12.2b." }, + { "tcvn", NULL, "b..b...bcccbccbbb7.8b95.b48.5b." }, + { "TIS-620", NULL, "8bcccbcc18b95.b.4b.11b7.8b." }, + { "next", NULL, "8bcccbcc18b95.bb125.bb" }, + { "dos", NULL, "8bcccbcc12bc5b95.b." }, + { "windows-1251", NULL, "8bcccbcc12bc5b95.b24.b." }, + { "windows-1252", NULL, "8bcccbcc12bc5b95.b.b11.b.2b12.b." }, + { "windows-1255", NULL, "8bcccbcc12bc5b95.b.b8.b.5b9.b.4b." }, + { "ebcdic", NULL, "5bc6bcc7bcc41b.9b7.9b5.b..8b6.10b6.b9.7b9.8b8.17b3.3b9.7b9.8b8.6b10.b.b.b." }, + { "IBM-1047", NULL, "4cbcbc3b9cbccbccbb4c6bcc5b3cbbc4bc4bccbc191.b" }, + { NULL, NULL, NULL } +}; + +/* + * Support "locale charmap"/nl_langinfo(CODESET) values, as well as others. + */ +struct cs_alias { + char *name; + char *oname; +} cs_aliases[] = { + { "UTF-8", "utf-8" }, + { "utf8", "utf-8" }, + { "UTF8", "utf-8" }, + { "ANSI_X3.4-1968", "ascii" }, + { "US-ASCII", "ascii" }, + { "latin1", "iso8859" }, + { "ISO-8859-1", "iso8859" }, + { "latin9", "iso8859" }, + { "ISO-8859-15", "iso8859" }, + { "latin2", "iso8859" }, + { "ISO-8859-2", "iso8859" }, + { "ISO-8859-3", "latin3" }, + { "latin4", "iso8859" }, + { "ISO-8859-4", "iso8859" }, + { "cyrillic", "iso8859" }, + { "ISO-8859-5", "iso8859" }, + { "ISO-8859-6", "arabic" }, + { "ISO-8859-7", "greek" }, + { "IBM9005", "greek2005" }, + { "ISO-8859-8", "hebrew" }, + { "latin5", "iso8859" }, + { "ISO-8859-9", "iso8859" }, + { "latin6", "iso8859" }, + { "ISO-8859-10", "iso8859" }, + { "latin7", "iso8859" }, + { "ISO-8859-13", "iso8859" }, + { "latin8", "iso8859" }, + { "ISO-8859-14", "iso8859" }, + { "latin10", "iso8859" }, + { "ISO-8859-16", "iso8859" }, + { "IBM437", "dos" }, + { "EBCDIC-US", "ebcdic" }, + { "IBM1047", "IBM-1047" }, + { "KOI8-R", "koi8-r" }, + { "KOI8-U", "koi8-r" }, + { "GEORGIAN-PS", "georgianps" }, + { "TCVN5712-1", "tcvn" }, + { "NEXTSTEP", "next" }, + { "windows", "windows-1252" }, /* backward compatibility */ + { "CP1251", "windows-1251" }, + { "CP1252", "windows-1252" }, + { "CP1255", "windows-1255" }, + { NULL, NULL } +}; + +#define IS_BINARY_CHAR 01 +#define IS_CONTROL_CHAR 02 + +static char chardef[256]; +static char *binfmt = NULL; +static char *utfbinfmt = NULL; +public int binattr = AT_STANDOUT; + + +/* + * Define a charset, given a description string. + * The string consists of 256 letters, + * one for each character in the charset. + * If the string is shorter than 256 letters, missing letters + * are taken to be identical to the last one. + * A decimal number followed by a letter is taken to be a + * repetition of the letter. + * + * Each letter is one of: + * . normal character + * b binary character + * c control character + */ + static void +ichardef(s) + char *s; +{ + register char *cp; + register int n; + register char v; + + n = 0; + v = 0; + cp = chardef; + while (*s != '\0') + { + switch (*s++) + { + case '.': + v = 0; + break; + case 'c': + v = IS_CONTROL_CHAR; + break; + case 'b': + v = IS_BINARY_CHAR|IS_CONTROL_CHAR; + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = (10 * n) + (s[-1] - '0'); + continue; + + default: + error("invalid chardef", NULL_PARG); + quit(QUIT_ERROR); + /*NOTREACHED*/ + } + + do + { + if (cp >= chardef + sizeof(chardef)) + { + error("chardef longer than 256", NULL_PARG); + quit(QUIT_ERROR); + /*NOTREACHED*/ + } + *cp++ = v; + } while (--n > 0); + n = 0; + } + + while (cp < chardef + sizeof(chardef)) + *cp++ = v; +} + +/* + * Define a charset, given a charset name. + * The valid charset names are listed in the "charsets" array. + */ + static int +icharset(name, no_error) + register char *name; + int no_error; +{ + register struct charset *p; + register struct cs_alias *a; + + if (name == NULL || *name == '\0') + return (0); + + /* First see if the name is an alias. */ + for (a = cs_aliases; a->name != NULL; a++) + { + if (strcmp(name, a->name) == 0) + { + name = a->oname; + break; + } + } + + for (p = charsets; p->name != NULL; p++) + { + if (strcmp(name, p->name) == 0) + { + ichardef(p->desc); + if (p->p_flag != NULL) + *(p->p_flag) = 1; + return (1); + } + } + + if (!no_error) { + error("invalid charset name", NULL_PARG); + quit(QUIT_ERROR); + } + return (0); +} + +#if HAVE_LOCALE +/* + * Define a charset, given a locale name. + */ + static void +ilocale() +{ + register int c; + + for (c = 0; c < (int) sizeof(chardef); c++) + { + if (isprint(c)) + chardef[c] = 0; + else if (iscntrl(c)) + chardef[c] = IS_CONTROL_CHAR; + else + chardef[c] = IS_BINARY_CHAR|IS_CONTROL_CHAR; + } +} +#endif + +/* + * Define the printing format for control (or binary utf) chars. + */ + static void +setbinfmt(s, fmtvarptr, default_fmt) + char *s; + char **fmtvarptr; + char *default_fmt; +{ + if (s && utf_mode) + { + /* It would be too hard to account for width otherwise. */ + char *t = s; + while (*t) + { + if (*t < ' ' || *t > '~') + { + s = default_fmt; + goto attr; + } + t++; + } + } + + /* %n is evil */ + if (s == NULL || *s == '\0' || + (*s == '*' && (s[1] == '\0' || s[2] == '\0' || strchr(s + 2, 'n'))) || + (*s != '*' && strchr(s, 'n'))) + s = default_fmt; + + /* + * Select the attributes if it starts with "*". + */ + attr: + if (*s == '*') + { + switch (s[1]) + { + case 'd': binattr = AT_BOLD; break; + case 'k': binattr = AT_BLINK; break; + case 's': binattr = AT_STANDOUT; break; + case 'u': binattr = AT_UNDERLINE; break; + default: binattr = AT_NORMAL; break; + } + s += 2; + } + *fmtvarptr = s; +} + +/* + * + */ + static void +set_charset() +{ + char *s; + + /* + * See if environment variable LESSCHARSET is defined. + */ + s = lgetenv("LESSCHARSET"); + if (icharset(s, 0)) + return; + + /* + * LESSCHARSET is not defined: try LESSCHARDEF. + */ + s = lgetenv("LESSCHARDEF"); + if (s != NULL && *s != '\0') + { + ichardef(s); + return; + } + +#if HAVE_LOCALE +#ifdef CODESET + /* + * Try using the codeset name as the charset name. + */ + s = nl_langinfo(CODESET); + if (icharset(s, 1)) + return; +#endif +#endif + +#if HAVE_STRSTR + /* + * Check whether LC_ALL, LC_CTYPE or LANG look like UTF-8 is used. + */ + if ((s = lgetenv("LC_ALL")) != NULL || + (s = lgetenv("LC_CTYPE")) != NULL || + (s = lgetenv("LANG")) != NULL) + { + if ( strstr(s, "UTF-8") != NULL || strstr(s, "utf-8") != NULL + || strstr(s, "UTF8") != NULL || strstr(s, "utf8") != NULL) + if (icharset("utf-8", 1)) + return; + } +#endif + +#if HAVE_LOCALE + /* + * Get character definitions from locale functions, + * rather than from predefined charset entry. + */ + ilocale(); +#if MSDOS_COMPILER + /* + * Default to "dos". + */ + (void) icharset("dos", 1); +#else + /* + * Default to "latin1". + */ + (void) icharset("latin1", 1); +#endif +#endif +} + +/* + * Initialize charset data structures. + */ + public void +init_charset() +{ + char *s; + +#if HAVE_LOCALE + setlocale(LC_ALL, ""); +#endif + + set_charset(); + + s = lgetenv("LESSBINFMT"); + setbinfmt(s, &binfmt, "*s<%02X>"); + + s = lgetenv("LESSUTFBINFMT"); + setbinfmt(s, &utfbinfmt, ""); +} + +/* + * Is a given character a "binary" character? + */ + public int +binary_char(c) + LWCHAR c; +{ + if (utf_mode) + return (is_ubin_char(c)); + c &= 0377; + return (chardef[c] & IS_BINARY_CHAR); +} + +/* + * Is a given character a "control" character? + */ + public int +control_char(c) + LWCHAR c; +{ + c &= 0377; + return (chardef[c] & IS_CONTROL_CHAR); +} + +/* + * Return the printable form of a character. + * For example, in the "ascii" charset '\3' is printed as "^C". + */ + public char * +prchar(c) + LWCHAR c; +{ + /* {{ This buffer can be overrun if LESSBINFMT is a long string. }} */ + static char buf[32]; + + c &= 0377; + if ((c < 128 || !utf_mode) && !control_char(c)) + SNPRINTF1(buf, sizeof(buf), "%c", (int) c); + else if (c == ESC) + strcpy(buf, "ESC"); +#if IS_EBCDIC_HOST + else if (!binary_char(c) && c < 64) + SNPRINTF1(buf, sizeof(buf), "^%c", + /* + * This array roughly inverts CONTROL() #defined in less.h, + * and should be kept in sync with CONTROL() and IBM-1047. + */ + "@ABC.I.?...KLMNO" + "PQRS.JH.XY.." + "\\]^_" + "......W[.....EFG" + "..V....D....TU.Z"[c]); +#else + else if (c < 128 && !control_char(c ^ 0100)) + SNPRINTF1(buf, sizeof(buf), "^%c", (int) (c ^ 0100)); +#endif + else + SNPRINTF1(buf, sizeof(buf), binfmt, c); + return (buf); +} + +/* + * Return the printable form of a UTF-8 character. + */ + public char * +prutfchar(ch) + LWCHAR ch; +{ + static char buf[32]; + + if (ch == ESC) + strcpy(buf, "ESC"); + else if (ch < 128 && control_char(ch)) + { + if (!control_char(ch ^ 0100)) + SNPRINTF1(buf, sizeof(buf), "^%c", ((char) ch) ^ 0100); + else + SNPRINTF1(buf, sizeof(buf), binfmt, (char) ch); + } else if (is_ubin_char(ch)) + { + SNPRINTF1(buf, sizeof(buf), utfbinfmt, ch); + } else + { + char *p = buf; + if (ch >= 0x80000000) + ch = 0xFFFD; /* REPLACEMENT CHARACTER */ + put_wchar(&p, ch); + *p = '\0'; + } + return (buf); +} + +/* + * Get the length of a UTF-8 character in bytes. + */ + public int +utf_len(ch) + char ch; +{ + if ((ch & 0x80) == 0) + return 1; + if ((ch & 0xE0) == 0xC0) + return 2; + if ((ch & 0xF0) == 0xE0) + return 3; + if ((ch & 0xF8) == 0xF0) + return 4; + if ((ch & 0xFC) == 0xF8) + return 5; + if ((ch & 0xFE) == 0xFC) + return 6; + /* Invalid UTF-8 encoding. */ + return 1; +} + +/* + * Does the parameter point to the lead byte of a well-formed UTF-8 character? + */ + public int +is_utf8_well_formed(s, slen) + unsigned char *s; + int slen; +{ + int i; + int len; + + if (IS_UTF8_INVALID(s[0])) + return (0); + + len = utf_len((char) s[0]); + if (len > slen) + return (0); + if (len == 1) + return (1); + if (len == 2) + { + if (s[0] < 0xC2) + return (0); + } else + { + unsigned char mask; + mask = (~((1 << (8-len)) - 1)) & 0xFF; + if (s[0] == mask && (s[1] & mask) == 0x80) + return (0); + } + + for (i = 1; i < len; i++) + if (!IS_UTF8_TRAIL(s[i])) + return (0); + return (1); +} + +/* + * Return number of invalid UTF-8 sequences found in a buffer. + */ + public int +utf_bin_count(data, len) + unsigned char *data; + int len; +{ + int bin_count = 0; + while (len > 0) + { + if (is_utf8_well_formed(data, len)) + { + int clen = utf_len(*data); + data += clen; + len -= clen; + } else + { + /* Skip to next lead byte. */ + bin_count++; + do { + ++data; + --len; + } while (len > 0 && !IS_UTF8_LEAD(*data)); + } + } + return (bin_count); +} + +/* + * Get the value of a UTF-8 character. + */ + public LWCHAR +get_wchar(p) + char *p; +{ + switch (utf_len(p[0])) + { + case 1: + default: + /* 0xxxxxxx */ + return (LWCHAR) + (p[0] & 0xFF); + case 2: + /* 110xxxxx 10xxxxxx */ + return (LWCHAR) ( + ((p[0] & 0x1F) << 6) | + (p[1] & 0x3F)); + case 3: + /* 1110xxxx 10xxxxxx 10xxxxxx */ + return (LWCHAR) ( + ((p[0] & 0x0F) << 12) | + ((p[1] & 0x3F) << 6) | + (p[2] & 0x3F)); + case 4: + /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + return (LWCHAR) ( + ((p[0] & 0x07) << 18) | + ((p[1] & 0x3F) << 12) | + ((p[2] & 0x3F) << 6) | + (p[3] & 0x3F)); + case 5: + /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + return (LWCHAR) ( + ((p[0] & 0x03) << 24) | + ((p[1] & 0x3F) << 18) | + ((p[2] & 0x3F) << 12) | + ((p[3] & 0x3F) << 6) | + (p[4] & 0x3F)); + case 6: + /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + return (LWCHAR) ( + ((p[0] & 0x01) << 30) | + ((p[1] & 0x3F) << 24) | + ((p[2] & 0x3F) << 18) | + ((p[3] & 0x3F) << 12) | + ((p[4] & 0x3F) << 6) | + (p[5] & 0x3F)); + } +} + +/* + * Store a character into a UTF-8 string. + */ + public void +put_wchar(pp, ch) + char **pp; + LWCHAR ch; +{ + if (!utf_mode || ch < 0x80) + { + /* 0xxxxxxx */ + *(*pp)++ = (char) ch; + } else if (ch < 0x800) + { + /* 110xxxxx 10xxxxxx */ + *(*pp)++ = (char) (0xC0 | ((ch >> 6) & 0x1F)); + *(*pp)++ = (char) (0x80 | (ch & 0x3F)); + } else if (ch < 0x10000) + { + /* 1110xxxx 10xxxxxx 10xxxxxx */ + *(*pp)++ = (char) (0xE0 | ((ch >> 12) & 0x0F)); + *(*pp)++ = (char) (0x80 | ((ch >> 6) & 0x3F)); + *(*pp)++ = (char) (0x80 | (ch & 0x3F)); + } else if (ch < 0x200000) + { + /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + *(*pp)++ = (char) (0xF0 | ((ch >> 18) & 0x07)); + *(*pp)++ = (char) (0x80 | ((ch >> 12) & 0x3F)); + *(*pp)++ = (char) (0x80 | ((ch >> 6) & 0x3F)); + *(*pp)++ = (char) (0x80 | (ch & 0x3F)); + } else if (ch < 0x4000000) + { + /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + *(*pp)++ = (char) (0xF0 | ((ch >> 24) & 0x03)); + *(*pp)++ = (char) (0x80 | ((ch >> 18) & 0x3F)); + *(*pp)++ = (char) (0x80 | ((ch >> 12) & 0x3F)); + *(*pp)++ = (char) (0x80 | ((ch >> 6) & 0x3F)); + *(*pp)++ = (char) (0x80 | (ch & 0x3F)); + } else + { + /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + *(*pp)++ = (char) (0xF0 | ((ch >> 30) & 0x01)); + *(*pp)++ = (char) (0x80 | ((ch >> 24) & 0x3F)); + *(*pp)++ = (char) (0x80 | ((ch >> 18) & 0x3F)); + *(*pp)++ = (char) (0x80 | ((ch >> 12) & 0x3F)); + *(*pp)++ = (char) (0x80 | ((ch >> 6) & 0x3F)); + *(*pp)++ = (char) (0x80 | (ch & 0x3F)); + } +} + +/* + * Step forward or backward one character in a string. + */ + public LWCHAR +step_char(pp, dir, limit) + char **pp; + signed int dir; + char *limit; +{ + LWCHAR ch; + int len; + char *p = *pp; + + if (!utf_mode) + { + /* It's easy if chars are one byte. */ + if (dir > 0) + ch = (LWCHAR) ((p < limit) ? *p++ : 0); + else + ch = (LWCHAR) ((p > limit) ? *--p : 0); + } else if (dir > 0) + { + len = utf_len(*p); + if (p + len > limit) + { + ch = 0; + p = limit; + } else + { + ch = get_wchar(p); + p += len; + } + } else + { + while (p > limit && IS_UTF8_TRAIL(p[-1])) + p--; + if (p > limit) + ch = get_wchar(--p); + else + ch = 0; + } + *pp = p; + return ch; +} + +/* + * Unicode characters data + * Actual data is in the generated *.uni files. + */ + +#define DECLARE_RANGE_TABLE_START(name) \ + static struct wchar_range name##_array[] = { +#define DECLARE_RANGE_TABLE_END(name) \ + }; struct wchar_range_table name##_table = { name##_array, sizeof(name##_array)/sizeof(*name##_array) }; + +DECLARE_RANGE_TABLE_START(compose) +#include "compose.uni" +DECLARE_RANGE_TABLE_END(compose) + +DECLARE_RANGE_TABLE_START(ubin) +#include "ubin.uni" +DECLARE_RANGE_TABLE_END(ubin) + +DECLARE_RANGE_TABLE_START(wide) +#include "wide.uni" +DECLARE_RANGE_TABLE_END(wide) + +/* comb_table is special pairs, not ranges. */ +static struct wchar_range comb_table[] = { + {0x0644,0x0622}, {0x0644,0x0623}, {0x0644,0x0625}, {0x0644,0x0627}, +}; + + + static int +is_in_table(ch, table) + LWCHAR ch; + struct wchar_range_table *table; +{ + int hi; + int lo; + + /* Binary search in the table. */ + if (ch < table->table[0].first) + return 0; + lo = 0; + hi = table->count - 1; + while (lo <= hi) + { + int mid = (lo + hi) / 2; + if (ch > table->table[mid].last) + lo = mid + 1; + else if (ch < table->table[mid].first) + hi = mid - 1; + else + return 1; + } + return 0; +} + +/* + * Is a character a UTF-8 composing character? + * If a composing character follows any char, the two combine into one glyph. + */ + public int +is_composing_char(ch) + LWCHAR ch; +{ + return is_in_table(ch, &compose_table); +} + +/* + * Should this UTF-8 character be treated as binary? + */ + public int +is_ubin_char(ch) + LWCHAR ch; +{ + return is_in_table(ch, &ubin_table); +} + +/* + * Is this a double width UTF-8 character? + */ + public int +is_wide_char(ch) + LWCHAR ch; +{ + return is_in_table(ch, &wide_table); +} + +/* + * Is a character a UTF-8 combining character? + * A combining char acts like an ordinary char, but if it follows + * a specific char (not any char), the two combine into one glyph. + */ + public int +is_combining_char(ch1, ch2) + LWCHAR ch1; + LWCHAR ch2; +{ + /* The table is small; use linear search. */ + int i; + for (i = 0; i < sizeof(comb_table)/sizeof(*comb_table); i++) + { + if (ch1 == comb_table[i].first && + ch2 == comb_table[i].last) + return 1; + } + return 0; +} + diff --git a/files/Sources/files/less/charset.h b/files/Sources/files/less/charset.h new file mode 100644 index 00000000..a4e3bf1d --- /dev/null +++ b/files/Sources/files/less/charset.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + +#define IS_ASCII_OCTET(c) (((c) & 0x80) == 0) +#define IS_UTF8_TRAIL(c) (((c) & 0xC0) == 0x80) +#define IS_UTF8_LEAD2(c) (((c) & 0xE0) == 0xC0) +#define IS_UTF8_LEAD3(c) (((c) & 0xF0) == 0xE0) +#define IS_UTF8_LEAD4(c) (((c) & 0xF8) == 0xF0) +#define IS_UTF8_LEAD5(c) (((c) & 0xFC) == 0xF8) +#define IS_UTF8_LEAD6(c) (((c) & 0xFE) == 0xFC) +#define IS_UTF8_INVALID(c) (((c) & 0xFE) == 0xFE) +#define IS_UTF8_LEAD(c) (((c) & 0xC0) == 0xC0 && !IS_UTF8_INVALID(c)) diff --git a/files/Sources/files/less/cmd.h b/files/Sources/files/less/cmd.h new file mode 100644 index 00000000..dd07ff8f --- /dev/null +++ b/files/Sources/files/less/cmd.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +#define MAX_USERCMD 1000 +#define MAX_CMDLEN 16 + +#define A_B_LINE 2 +#define A_B_SCREEN 3 +#define A_B_SCROLL 4 +#define A_B_SEARCH 5 +#define A_DIGIT 6 +#define A_DISP_OPTION 7 +#define A_DEBUG 8 +#define A_EXAMINE 9 +#define A_FIRSTCMD 10 +#define A_FREPAINT 11 +#define A_F_LINE 12 +#define A_F_SCREEN 13 +#define A_F_SCROLL 14 +#define A_F_SEARCH 15 +#define A_GOEND 16 +#define A_GOLINE 17 +#define A_GOMARK 18 +#define A_HELP 19 +#define A_NEXT_FILE 20 +#define A_PERCENT 21 +#define A_PREFIX 22 +#define A_PREV_FILE 23 +#define A_QUIT 24 +#define A_REPAINT 25 +#define A_SETMARK 26 +#define A_SHELL 27 +#define A_STAT 28 +#define A_FF_LINE 29 +#define A_BF_LINE 30 +#define A_VERSION 31 +#define A_VISUAL 32 +#define A_F_WINDOW 33 +#define A_B_WINDOW 34 +#define A_F_BRACKET 35 +#define A_B_BRACKET 36 +#define A_PIPE 37 +#define A_INDEX_FILE 38 +#define A_UNDO_SEARCH 39 +#define A_FF_SCREEN 40 +#define A_LSHIFT 41 +#define A_RSHIFT 42 +#define A_AGAIN_SEARCH 43 +#define A_T_AGAIN_SEARCH 44 +#define A_REVERSE_SEARCH 45 +#define A_T_REVERSE_SEARCH 46 +#define A_OPT_TOGGLE 47 +#define A_OPT_SET 48 +#define A_OPT_UNSET 49 +#define A_F_FOREVER 50 +#define A_GOPOS 51 +#define A_REMOVE_FILE 52 +#define A_NEXT_TAG 53 +#define A_PREV_TAG 54 +#define A_FILTER 55 +#define A_F_UNTIL_HILITE 56 +#define A_GOEND_BUF 57 +#define A_LLSHIFT 58 +#define A_RRSHIFT 59 + +#define A_INVALID 100 +#define A_NOACTION 101 +#define A_UINVALID 102 +#define A_END_LIST 103 +#define A_SPECIAL_KEY 104 + +#define A_SKIP 127 + +#define A_EXTRA 0200 + + +/* Line editing characters */ + +#define EC_BACKSPACE 1 +#define EC_LINEKILL 2 +#define EC_RIGHT 3 +#define EC_LEFT 4 +#define EC_W_LEFT 5 +#define EC_W_RIGHT 6 +#define EC_INSERT 7 +#define EC_DELETE 8 +#define EC_HOME 9 +#define EC_END 10 +#define EC_W_BACKSPACE 11 +#define EC_W_DELETE 12 +#define EC_UP 13 +#define EC_DOWN 14 +#define EC_EXPAND 15 +#define EC_F_COMPLETE 17 +#define EC_B_COMPLETE 18 +#define EC_LITERAL 19 +#define EC_ABORT 20 + +#define EC_NOACTION 101 +#define EC_UINVALID 102 + +/* Flags for editchar() */ +#define EC_PEEK 01 +#define EC_NOHISTORY 02 +#define EC_NOCOMPLETE 04 +#define EC_NORIGHTLEFT 010 + +/* Environment variable stuff */ +#define EV_OK 01 + +/* Special keys (keys which output different strings on different terminals) */ +#define SK_SPECIAL_KEY CONTROL('K') +#define SK_RIGHT_ARROW 1 +#define SK_LEFT_ARROW 2 +#define SK_UP_ARROW 3 +#define SK_DOWN_ARROW 4 +#define SK_PAGE_UP 5 +#define SK_PAGE_DOWN 6 +#define SK_HOME 7 +#define SK_END 8 +#define SK_DELETE 9 +#define SK_INSERT 10 +#define SK_CTL_LEFT_ARROW 11 +#define SK_CTL_RIGHT_ARROW 12 +#define SK_CTL_DELETE 13 +#define SK_F1 14 +#define SK_BACKTAB 15 +#define SK_CTL_BACKSPACE 16 +#define SK_CONTROL_K 40 diff --git a/files/Sources/files/less/cmdbuf.c b/files/Sources/files/less/cmdbuf.c new file mode 100644 index 00000000..9bc17cf0 --- /dev/null +++ b/files/Sources/files/less/cmdbuf.c @@ -0,0 +1,1676 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Functions which manipulate the command buffer. + * Used only by command() and related functions. + */ + +#include "less.h" +#include "cmd.h" +#include "charset.h" +#if HAVE_STAT +#include +#endif + +extern int sc_width; +extern int utf_mode; + +static char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */ +static int cmd_col; /* Current column of the cursor */ +static int prompt_col; /* Column of cursor just after prompt */ +static char *cp; /* Pointer into cmdbuf */ +static int cmd_offset; /* Index into cmdbuf of first displayed char */ +static int literal; /* Next input char should not be interpreted */ +static int updown_match = -1; /* Prefix length in up/down movement */ + +#if TAB_COMPLETE_FILENAME +static int cmd_complete(); +/* + * These variables are statics used by cmd_complete. + */ +static int in_completion = 0; +static char *tk_text; +static char *tk_original; +static char *tk_ipoint; +static char *tk_trial; +static struct textlist tk_tlist; +#endif + +static int cmd_left(); +static int cmd_right(); + +#if SPACES_IN_FILENAMES +public char openquote = '"'; +public char closequote = '"'; +#endif + +#if CMD_HISTORY + +/* History file */ +#define HISTFILE_FIRST_LINE ".less-history-file:" +#define HISTFILE_SEARCH_SECTION ".search" +#define HISTFILE_SHELL_SECTION ".shell" + +/* + * A mlist structure represents a command history. + */ +struct mlist +{ + struct mlist *next; + struct mlist *prev; + struct mlist *curr_mp; + char *string; + int modified; +}; + +/* + * These are the various command histories that exist. + */ +struct mlist mlist_search = + { &mlist_search, &mlist_search, &mlist_search, NULL, 0 }; +public void * constant ml_search = (void *) &mlist_search; + +struct mlist mlist_examine = + { &mlist_examine, &mlist_examine, &mlist_examine, NULL, 0 }; +public void * constant ml_examine = (void *) &mlist_examine; + +#if SHELL_ESCAPE || PIPEC +struct mlist mlist_shell = + { &mlist_shell, &mlist_shell, &mlist_shell, NULL, 0 }; +public void * constant ml_shell = (void *) &mlist_shell; +#endif + +#else /* CMD_HISTORY */ + +/* If CMD_HISTORY is off, these are just flags. */ +public void * constant ml_search = (void *)1; +public void * constant ml_examine = (void *)2; +#if SHELL_ESCAPE || PIPEC +public void * constant ml_shell = (void *)3; +#endif + +#endif /* CMD_HISTORY */ + +/* + * History for the current command. + */ +static struct mlist *curr_mlist = NULL; +static int curr_cmdflags; + +static char cmd_mbc_buf[MAX_UTF_CHAR_LEN]; +static int cmd_mbc_buf_len; +static int cmd_mbc_buf_index; + + +/* + * Reset command buffer (to empty). + */ + public void +cmd_reset() +{ + cp = cmdbuf; + *cp = '\0'; + cmd_col = 0; + cmd_offset = 0; + literal = 0; + cmd_mbc_buf_len = 0; + updown_match = -1; +} + +/* + * Clear command line. + */ + public void +clear_cmd() +{ + cmd_col = prompt_col = 0; + cmd_mbc_buf_len = 0; + updown_match = -1; +} + +/* + * Display a string, usually as a prompt for input into the command buffer. + */ + public void +cmd_putstr(s) + char *s; +{ + LWCHAR prev_ch = 0; + LWCHAR ch; + char *endline = s + strlen(s); + while (*s != '\0') + { + char *ns = s; + int width; + ch = step_char(&ns, +1, endline); + while (s < ns) + putchr(*s++); + if (!utf_mode) + width = 1; + else if (is_composing_char(ch) || is_combining_char(prev_ch, ch)) + width = 0; + else + width = is_wide_char(ch) ? 2 : 1; + cmd_col += width; + prompt_col += width; + prev_ch = ch; + } +} + +/* + * How many characters are in the command buffer? + */ + public int +len_cmdbuf() +{ + char *s = cmdbuf; + char *endline = s + strlen(s); + int len = 0; + + while (*s != '\0') + { + step_char(&s, +1, endline); + len++; + } + return (len); +} + +/* + * Common part of cmd_step_right() and cmd_step_left(). + * {{ Returning pwidth and bswidth separately is a historical artifact + * since they're always the same. Maybe clean this up someday. }} + */ + static char * +cmd_step_common(p, ch, len, pwidth, bswidth) + char *p; + LWCHAR ch; + int len; + int *pwidth; + int *bswidth; +{ + char *pr; + int width; + + if (len == 1) + { + pr = prchar((int) ch); + width = (int) strlen(pr); + } else + { + pr = prutfchar(ch); + if (is_composing_char(ch)) + width = 0; + else if (is_ubin_char(ch)) + width = (int) strlen(pr); + else + { + LWCHAR prev_ch = step_char(&p, -1, cmdbuf); + if (is_combining_char(prev_ch, ch)) + width = 0; + else + width = is_wide_char(ch) ? 2 : 1; + } + } + if (pwidth != NULL) + *pwidth = width; + if (bswidth != NULL) + *bswidth = width; + return (pr); +} + +/* + * Step a pointer one character right in the command buffer. + */ + static char * +cmd_step_right(pp, pwidth, bswidth) + char **pp; + int *pwidth; + int *bswidth; +{ + char *p = *pp; + LWCHAR ch = step_char(pp, +1, p + strlen(p)); + + return cmd_step_common(p, ch, *pp - p, pwidth, bswidth); +} + +/* + * Step a pointer one character left in the command buffer. + */ + static char * +cmd_step_left(pp, pwidth, bswidth) + char **pp; + int *pwidth; + int *bswidth; +{ + char *p = *pp; + LWCHAR ch = step_char(pp, -1, cmdbuf); + + return cmd_step_common(*pp, ch, p - *pp, pwidth, bswidth); +} + +/* + * Repaint the line from cp onwards. + * Then position the cursor just after the char old_cp (a pointer into cmdbuf). + */ + static void +cmd_repaint(old_cp) + char *old_cp; +{ + /* + * Repaint the line from the current position. + */ + clear_eol(); + while (*cp != '\0') + { + char *np = cp; + int width; + char *pr = cmd_step_right(&np, &width, NULL); + if (cmd_col + width >= sc_width) + break; + cp = np; + putstr(pr); + cmd_col += width; + } + while (*cp != '\0') + { + char *np = cp; + int width; + char *pr = cmd_step_right(&np, &width, NULL); + if (width > 0) + break; + cp = np; + putstr(pr); + } + + /* + * Back up the cursor to the correct position. + */ + while (cp > old_cp) + cmd_left(); +} + +/* + * Put the cursor at "home" (just after the prompt), + * and set cp to the corresponding char in cmdbuf. + */ + static void +cmd_home() +{ + while (cmd_col > prompt_col) + { + int width, bswidth; + + cmd_step_left(&cp, &width, &bswidth); + while (bswidth-- > 0) + putbs(); + cmd_col -= width; + } + + cp = &cmdbuf[cmd_offset]; +} + +/* + * Shift the cmdbuf display left a half-screen. + */ + static void +cmd_lshift() +{ + char *s; + char *save_cp; + int cols; + + /* + * Start at the first displayed char, count how far to the + * right we'd have to move to reach the center of the screen. + */ + s = cmdbuf + cmd_offset; + cols = 0; + while (cols < (sc_width - prompt_col) / 2 && *s != '\0') + { + int width; + cmd_step_right(&s, &width, NULL); + cols += width; + } + while (*s != '\0') + { + int width; + char *ns = s; + cmd_step_right(&ns, &width, NULL); + if (width > 0) + break; + s = ns; + } + + cmd_offset = (int) (s - cmdbuf); + save_cp = cp; + cmd_home(); + cmd_repaint(save_cp); +} + +/* + * Shift the cmdbuf display right a half-screen. + */ + static void +cmd_rshift() +{ + char *s; + char *save_cp; + int cols; + + /* + * Start at the first displayed char, count how far to the + * left we'd have to move to traverse a half-screen width + * of displayed characters. + */ + s = cmdbuf + cmd_offset; + cols = 0; + while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf) + { + int width; + cmd_step_left(&s, &width, NULL); + cols += width; + } + + cmd_offset = (int) (s - cmdbuf); + save_cp = cp; + cmd_home(); + cmd_repaint(save_cp); +} + +/* + * Move cursor right one character. + */ + static int +cmd_right() +{ + char *pr; + char *ncp; + int width; + + if (*cp == '\0') + { + /* Already at the end of the line. */ + return (CC_OK); + } + ncp = cp; + pr = cmd_step_right(&ncp, &width, NULL); + if (cmd_col + width >= sc_width) + cmd_lshift(); + else if (cmd_col + width == sc_width - 1 && cp[1] != '\0') + cmd_lshift(); + cp = ncp; + cmd_col += width; + putstr(pr); + while (*cp != '\0') + { + pr = cmd_step_right(&ncp, &width, NULL); + if (width > 0) + break; + putstr(pr); + cp = ncp; + } + return (CC_OK); +} + +/* + * Move cursor left one character. + */ + static int +cmd_left() +{ + char *ncp; + int width, bswidth; + + if (cp <= cmdbuf) + { + /* Already at the beginning of the line */ + return (CC_OK); + } + ncp = cp; + while (ncp > cmdbuf) + { + cmd_step_left(&ncp, &width, &bswidth); + if (width > 0) + break; + } + if (cmd_col < prompt_col + width) + cmd_rshift(); + cp = ncp; + cmd_col -= width; + while (bswidth-- > 0) + putbs(); + return (CC_OK); +} + +/* + * Insert a char into the command buffer, at the current position. + */ + static int +cmd_ichar(cs, clen) + char *cs; + int clen; +{ + char *s; + + if (strlen(cmdbuf) + clen >= sizeof(cmdbuf)-1) + { + /* No room in the command buffer for another char. */ + bell(); + return (CC_ERROR); + } + + /* + * Make room for the new character (shift the tail of the buffer right). + */ + for (s = &cmdbuf[strlen(cmdbuf)]; s >= cp; s--) + s[clen] = s[0]; + /* + * Insert the character into the buffer. + */ + for (s = cp; s < cp + clen; s++) + *s = *cs++; + /* + * Reprint the tail of the line from the inserted char. + */ + updown_match = -1; + cmd_repaint(cp); + cmd_right(); + return (CC_OK); +} + +/* + * Backspace in the command buffer. + * Delete the char to the left of the cursor. + */ + static int +cmd_erase() +{ + register char *s; + int clen; + + if (cp == cmdbuf) + { + /* + * Backspace past beginning of the buffer: + * this usually means abort the command. + */ + return (CC_QUIT); + } + /* + * Move cursor left (to the char being erased). + */ + s = cp; + cmd_left(); + clen = (int) (s - cp); + + /* + * Remove the char from the buffer (shift the buffer left). + */ + for (s = cp; ; s++) + { + s[0] = s[clen]; + if (s[0] == '\0') + break; + } + + /* + * Repaint the buffer after the erased char. + */ + updown_match = -1; + cmd_repaint(cp); + + /* + * We say that erasing the entire command string causes us + * to abort the current command, if CF_QUIT_ON_ERASE is set. + */ + if ((curr_cmdflags & CF_QUIT_ON_ERASE) && cp == cmdbuf && *cp == '\0') + return (CC_QUIT); + return (CC_OK); +} + +/* + * Delete the char under the cursor. + */ + static int +cmd_delete() +{ + if (*cp == '\0') + { + /* At end of string; there is no char under the cursor. */ + return (CC_OK); + } + /* + * Move right, then use cmd_erase. + */ + cmd_right(); + cmd_erase(); + return (CC_OK); +} + +/* + * Delete the "word" to the left of the cursor. + */ + static int +cmd_werase() +{ + if (cp > cmdbuf && cp[-1] == ' ') + { + /* + * If the char left of cursor is a space, + * erase all the spaces left of cursor (to the first non-space). + */ + while (cp > cmdbuf && cp[-1] == ' ') + (void) cmd_erase(); + } else + { + /* + * If the char left of cursor is not a space, + * erase all the nonspaces left of cursor (the whole "word"). + */ + while (cp > cmdbuf && cp[-1] != ' ') + (void) cmd_erase(); + } + return (CC_OK); +} + +/* + * Delete the "word" under the cursor. + */ + static int +cmd_wdelete() +{ + if (*cp == ' ') + { + /* + * If the char under the cursor is a space, + * delete it and all the spaces right of cursor. + */ + while (*cp == ' ') + (void) cmd_delete(); + } else + { + /* + * If the char under the cursor is not a space, + * delete it and all nonspaces right of cursor (the whole word). + */ + while (*cp != ' ' && *cp != '\0') + (void) cmd_delete(); + } + return (CC_OK); +} + +/* + * Delete all chars in the command buffer. + */ + static int +cmd_kill() +{ + if (cmdbuf[0] == '\0') + { + /* Buffer is already empty; abort the current command. */ + return (CC_QUIT); + } + cmd_offset = 0; + cmd_home(); + *cp = '\0'; + updown_match = -1; + cmd_repaint(cp); + + /* + * We say that erasing the entire command string causes us + * to abort the current command, if CF_QUIT_ON_ERASE is set. + */ + if (curr_cmdflags & CF_QUIT_ON_ERASE) + return (CC_QUIT); + return (CC_OK); +} + +/* + * Select an mlist structure to be the current command history. + */ + public void +set_mlist(mlist, cmdflags) + void *mlist; + int cmdflags; +{ +#if CMD_HISTORY + curr_mlist = (struct mlist *) mlist; + curr_cmdflags = cmdflags; + + /* Make sure the next up-arrow moves to the last string in the mlist. */ + if (curr_mlist != NULL) + curr_mlist->curr_mp = curr_mlist; +#endif +} + +#if CMD_HISTORY +/* + * Move up or down in the currently selected command history list. + * Only consider entries whose first updown_match chars are equal to + * cmdbuf's corresponding chars. + */ + static int +cmd_updown(action) + int action; +{ + char *s; + struct mlist *ml; + + if (curr_mlist == NULL) + { + /* + * The current command has no history list. + */ + bell(); + return (CC_OK); + } + + if (updown_match < 0) + { + updown_match = (int) (cp - cmdbuf); + } + + /* + * Find the next history entry which matches. + */ + for (ml = curr_mlist->curr_mp;;) + { + ml = (action == EC_UP) ? ml->prev : ml->next; + if (ml == curr_mlist) + { + /* + * We reached the end (or beginning) of the list. + */ + break; + } + if (strncmp(cmdbuf, ml->string, updown_match) == 0) + { + /* + * This entry matches; stop here. + * Copy the entry into cmdbuf and echo it on the screen. + */ + curr_mlist->curr_mp = ml; + s = ml->string; + if (s == NULL) + s = ""; + cmd_home(); + clear_eol(); + strcpy(cmdbuf, s); + for (cp = cmdbuf; *cp != '\0'; ) + cmd_right(); + return (CC_OK); + } + } + /* + * We didn't find a history entry that matches. + */ + bell(); + return (CC_OK); +} +#endif + +/* + * Add a string to an mlist. + */ + public void +cmd_addhist(mlist, cmd, modified) + struct mlist *mlist; + char *cmd; + int modified; +{ +#if CMD_HISTORY + struct mlist *ml; + + /* + * Don't save a trivial command. + */ + if (strlen(cmd) == 0) + return; + + /* + * Save the command unless it's a duplicate of the + * last command in the history. + */ + ml = mlist->prev; + if (ml == mlist || strcmp(ml->string, cmd) != 0) + { + /* + * Did not find command in history. + * Save the command and put it at the end of the history list. + */ + ml = (struct mlist *) ecalloc(1, sizeof(struct mlist)); + ml->string = save(cmd); + ml->modified = modified; + ml->next = mlist; + ml->prev = mlist->prev; + mlist->prev->next = ml; + mlist->prev = ml; + } + /* + * Point to the cmd just after the just-accepted command. + * Thus, an UPARROW will always retrieve the previous command. + */ + mlist->curr_mp = ml->next; +#endif +} + +/* + * Accept the command in the command buffer. + * Add it to the currently selected history list. + */ + public void +cmd_accept() +{ +#if CMD_HISTORY + /* + * Nothing to do if there is no currently selected history list. + */ + if (curr_mlist == NULL) + return; + cmd_addhist(curr_mlist, cmdbuf, 1); + curr_mlist->modified = 1; +#endif +} + +/* + * Try to perform a line-edit function on the command buffer, + * using a specified char as a line-editing command. + * Returns: + * CC_PASS The char does not invoke a line edit function. + * CC_OK Line edit function done. + * CC_QUIT The char requests the current command to be aborted. + */ + static int +cmd_edit(c) + int c; +{ + int action; + int flags; + +#if TAB_COMPLETE_FILENAME +#define not_in_completion() in_completion = 0 +#else +#define not_in_completion() +#endif + + /* + * See if the char is indeed a line-editing command. + */ + flags = 0; +#if CMD_HISTORY + if (curr_mlist == NULL) + /* + * No current history; don't accept history manipulation cmds. + */ + flags |= EC_NOHISTORY; +#endif +#if TAB_COMPLETE_FILENAME + if (curr_mlist == ml_search) + /* + * In a search command; don't accept file-completion cmds. + */ + flags |= EC_NOCOMPLETE; +#endif + + action = editchar(c, flags); + + switch (action) + { + case EC_RIGHT: + not_in_completion(); + return (cmd_right()); + case EC_LEFT: + not_in_completion(); + return (cmd_left()); + case EC_W_RIGHT: + not_in_completion(); + while (*cp != '\0' && *cp != ' ') + cmd_right(); + while (*cp == ' ') + cmd_right(); + return (CC_OK); + case EC_W_LEFT: + not_in_completion(); + while (cp > cmdbuf && cp[-1] == ' ') + cmd_left(); + while (cp > cmdbuf && cp[-1] != ' ') + cmd_left(); + return (CC_OK); + case EC_HOME: + not_in_completion(); + cmd_offset = 0; + cmd_home(); + cmd_repaint(cp); + return (CC_OK); + case EC_END: + not_in_completion(); + while (*cp != '\0') + cmd_right(); + return (CC_OK); + case EC_INSERT: + not_in_completion(); + return (CC_OK); + case EC_BACKSPACE: + not_in_completion(); + return (cmd_erase()); + case EC_LINEKILL: + not_in_completion(); + return (cmd_kill()); + case EC_ABORT: + not_in_completion(); + (void) cmd_kill(); + return (CC_QUIT); + case EC_W_BACKSPACE: + not_in_completion(); + return (cmd_werase()); + case EC_DELETE: + not_in_completion(); + return (cmd_delete()); + case EC_W_DELETE: + not_in_completion(); + return (cmd_wdelete()); + case EC_LITERAL: + literal = 1; + return (CC_OK); +#if CMD_HISTORY + case EC_UP: + case EC_DOWN: + not_in_completion(); + return (cmd_updown(action)); +#endif +#if TAB_COMPLETE_FILENAME + case EC_F_COMPLETE: + case EC_B_COMPLETE: + case EC_EXPAND: + return (cmd_complete(action)); +#endif + case EC_NOACTION: + return (CC_OK); + default: + not_in_completion(); + return (CC_PASS); + } +} + +#if TAB_COMPLETE_FILENAME +/* + * Insert a string into the command buffer, at the current position. + */ + static int +cmd_istr(str) + char *str; +{ + char *s; + int action; + char *endline = str + strlen(str); + + for (s = str; *s != '\0'; ) + { + char *os = s; + step_char(&s, +1, endline); + action = cmd_ichar(os, s - os); + if (action != CC_OK) + { + bell(); + return (action); + } + } + return (CC_OK); +} + +/* + * Find the beginning and end of the "current" word. + * This is the word which the cursor (cp) is inside or at the end of. + * Return pointer to the beginning of the word and put the + * cursor at the end of the word. + */ + static char * +delimit_word() +{ + char *word; +#if SPACES_IN_FILENAMES + char *p; + int delim_quoted = 0; + int meta_quoted = 0; + char *esc = get_meta_escape(); + int esclen = (int) strlen(esc); +#endif + + /* + * Move cursor to end of word. + */ + if (*cp != ' ' && *cp != '\0') + { + /* + * Cursor is on a nonspace. + * Move cursor right to the next space. + */ + while (*cp != ' ' && *cp != '\0') + cmd_right(); + } else if (cp > cmdbuf && cp[-1] != ' ') + { + /* + * Cursor is on a space, and char to the left is a nonspace. + * We're already at the end of the word. + */ + ; +#if 0 + } else + { + /* + * Cursor is on a space and char to the left is a space. + * Huh? There's no word here. + */ + return (NULL); +#endif + } + /* + * Find the beginning of the word which the cursor is in. + */ + if (cp == cmdbuf) + return (NULL); +#if SPACES_IN_FILENAMES + /* + * If we have an unbalanced quote (that is, an open quote + * without a corresponding close quote), we return everything + * from the open quote, including spaces. + */ + for (word = cmdbuf; word < cp; word++) + if (*word != ' ') + break; + if (word >= cp) + return (cp); + for (p = cmdbuf; p < cp; p++) + { + if (meta_quoted) + { + meta_quoted = 0; + } else if (esclen > 0 && p + esclen < cp && + strncmp(p, esc, esclen) == 0) + { + meta_quoted = 1; + p += esclen - 1; + } else if (delim_quoted) + { + if (*p == closequote) + delim_quoted = 0; + } else /* (!delim_quoted) */ + { + if (*p == openquote) + delim_quoted = 1; + else if (*p == ' ') + word = p+1; + } + } +#endif + return (word); +} + +/* + * Set things up to enter completion mode. + * Expand the word under the cursor into a list of filenames + * which start with that word, and set tk_text to that list. + */ + static void +init_compl() +{ + char *word; + char c; + + /* + * Get rid of any previous tk_text. + */ + if (tk_text != NULL) + { + free(tk_text); + tk_text = NULL; + } + /* + * Find the original (uncompleted) word in the command buffer. + */ + word = delimit_word(); + if (word == NULL) + return; + /* + * Set the insertion point to the point in the command buffer + * where the original (uncompleted) word now sits. + */ + tk_ipoint = word; + /* + * Save the original (uncompleted) word + */ + if (tk_original != NULL) + free(tk_original); + tk_original = (char *) ecalloc(cp-word+1, sizeof(char)); + strncpy(tk_original, word, cp-word); + /* + * Get the expanded filename. + * This may result in a single filename, or + * a blank-separated list of filenames. + */ + c = *cp; + *cp = '\0'; + if (*word != openquote) + { + tk_text = fcomplete(word); + } else + { +#if MSDOS_COMPILER + char *qword = NULL; +#else + char *qword = shell_quote(word+1); +#endif + if (qword == NULL) + tk_text = fcomplete(word+1); + else + { + tk_text = fcomplete(qword); + free(qword); + } + } + *cp = c; +} + +/* + * Return the next word in the current completion list. + */ + static char * +next_compl(action, prev) + int action; + char *prev; +{ + switch (action) + { + case EC_F_COMPLETE: + return (forw_textlist(&tk_tlist, prev)); + case EC_B_COMPLETE: + return (back_textlist(&tk_tlist, prev)); + } + /* Cannot happen */ + return ("?"); +} + +/* + * Complete the filename before (or under) the cursor. + * cmd_complete may be called multiple times. The global in_completion + * remembers whether this call is the first time (create the list), + * or a subsequent time (step thru the list). + */ + static int +cmd_complete(action) + int action; +{ + char *s; + + if (!in_completion || action == EC_EXPAND) + { + /* + * Expand the word under the cursor and + * use the first word in the expansion + * (or the entire expansion if we're doing EC_EXPAND). + */ + init_compl(); + if (tk_text == NULL) + { + bell(); + return (CC_OK); + } + if (action == EC_EXPAND) + { + /* + * Use the whole list. + */ + tk_trial = tk_text; + } else + { + /* + * Use the first filename in the list. + */ + in_completion = 1; + init_textlist(&tk_tlist, tk_text); + tk_trial = next_compl(action, (char*)NULL); + } + } else + { + /* + * We already have a completion list. + * Use the next/previous filename from the list. + */ + tk_trial = next_compl(action, tk_trial); + } + + /* + * Remove the original word, or the previous trial completion. + */ + while (cp > tk_ipoint) + (void) cmd_erase(); + + if (tk_trial == NULL) + { + /* + * There are no more trial completions. + * Insert the original (uncompleted) filename. + */ + in_completion = 0; + if (cmd_istr(tk_original) != CC_OK) + goto fail; + } else + { + /* + * Insert trial completion. + */ + if (cmd_istr(tk_trial) != CC_OK) + goto fail; + /* + * If it is a directory, append a slash. + */ + if (is_dir(tk_trial)) + { + if (cp > cmdbuf && cp[-1] == closequote) + (void) cmd_erase(); + s = lgetenv("LESSSEPARATOR"); + if (s == NULL) + s = PATHNAME_SEP; + if (cmd_istr(s) != CC_OK) + goto fail; + } + } + + return (CC_OK); + +fail: + in_completion = 0; + bell(); + return (CC_OK); +} + +#endif /* TAB_COMPLETE_FILENAME */ + +/* + * Process a single character of a multi-character command, such as + * a number, or the pattern of a search command. + * Returns: + * CC_OK The char was accepted. + * CC_QUIT The char requests the command to be aborted. + * CC_ERROR The char could not be accepted due to an error. + */ + public int +cmd_char(c) + int c; +{ + int action; + int len; + + if (!utf_mode) + { + cmd_mbc_buf[0] = c; + len = 1; + } else + { + /* Perform strict validation in all possible cases. */ + if (cmd_mbc_buf_len == 0) + { + retry: + cmd_mbc_buf_index = 1; + *cmd_mbc_buf = c; + if (IS_ASCII_OCTET(c)) + cmd_mbc_buf_len = 1; + else if (IS_UTF8_LEAD(c)) + { + cmd_mbc_buf_len = utf_len(c); + return (CC_OK); + } else + { + /* UTF8_INVALID or stray UTF8_TRAIL */ + bell(); + return (CC_ERROR); + } + } else if (IS_UTF8_TRAIL(c)) + { + cmd_mbc_buf[cmd_mbc_buf_index++] = c; + if (cmd_mbc_buf_index < cmd_mbc_buf_len) + return (CC_OK); + if (!is_utf8_well_formed(cmd_mbc_buf, cmd_mbc_buf_index)) + { + /* complete, but not well formed (non-shortest form), sequence */ + cmd_mbc_buf_len = 0; + bell(); + return (CC_ERROR); + } + } else + { + /* Flush incomplete (truncated) sequence. */ + cmd_mbc_buf_len = 0; + bell(); + /* Handle new char. */ + goto retry; + } + + len = cmd_mbc_buf_len; + cmd_mbc_buf_len = 0; + } + + if (literal) + { + /* + * Insert the char, even if it is a line-editing char. + */ + literal = 0; + return (cmd_ichar(cmd_mbc_buf, len)); + } + + /* + * See if it is a line-editing character. + */ + if (in_mca() && len == 1) + { + action = cmd_edit(c); + switch (action) + { + case CC_OK: + case CC_QUIT: + return (action); + case CC_PASS: + break; + } + } + + /* + * Insert the char into the command buffer. + */ + return (cmd_ichar(cmd_mbc_buf, len)); +} + +/* + * Return the number currently in the command buffer. + */ + public LINENUM +cmd_int(frac) + long *frac; +{ + char *p; + LINENUM n = 0; + int err; + + for (p = cmdbuf; *p >= '0' && *p <= '9'; p++) + n = (n * 10) + (*p - '0'); + *frac = 0; + if (*p++ == '.') + { + *frac = getfraction(&p, NULL, &err); + /* {{ do something if err is set? }} */ + } + return (n); +} + +/* + * Return a pointer to the command buffer. + */ + public char * +get_cmdbuf() +{ + return (cmdbuf); +} + +#if CMD_HISTORY +/* + * Return the last (most recent) string in the current command history. + */ + public char * +cmd_lastpattern() +{ + if (curr_mlist == NULL) + return (NULL); + return (curr_mlist->curr_mp->prev->string); +} +#endif + +#if CMD_HISTORY +/* + */ + static int +mlist_size(ml) + struct mlist *ml; +{ + int size = 0; + for (ml = ml->next; ml->string != NULL; ml = ml->next) + ++size; + return size; +} + +/* + * Get the name of the history file. + */ + static char * +histfile_name() +{ + char *home; + char *name; + int len; + + /* See if filename is explicitly specified by $LESSHISTFILE. */ + name = lgetenv("LESSHISTFILE"); + if (name != NULL && *name != '\0') + { + if (strcmp(name, "-") == 0 || strcmp(name, "/dev/null") == 0) + /* $LESSHISTFILE == "-" means don't use a history file. */ + return (NULL); + return (save(name)); + } + + /* See if history file is disabled in the build. */ + if (strcmp(LESSHISTFILE, "") == 0 || strcmp(LESSHISTFILE, "-") == 0) + return (NULL); + + /* Otherwise, file is in $HOME. */ + home = lgetenv("HOME"); + if (home == NULL || *home == '\0') + { +#if OS2 + home = lgetenv("INIT"); + if (home == NULL || *home == '\0') +#endif + return (NULL); + } + len = (int) (strlen(home) + strlen(LESSHISTFILE) + 2 + strlen("Documents/")); + name = (char *) ecalloc(len, sizeof(char)); + // iOS: HOME is not allowed for read-write. + // SNPRINTF2(name, len, "%s/%s", home, LESSHISTFILE); + SNPRINTF2(name, len, "%s/Documents/%s", home, LESSHISTFILE); + return (name); +} + +/* + * Read a .lesshst file and call a callback for each line in the file. + */ + static void +read_cmdhist2(action, uparam, skip_search, skip_shell) + void (*action)(void*,struct mlist*,char*); + void *uparam; + int skip_search; + int skip_shell; +{ + struct mlist *ml = NULL; + char line[CMDBUF_SIZE]; + char *filename; + FILE *f; + char *p; + int *skip = NULL; + + filename = histfile_name(); + if (filename == NULL) + return; + f = fopen(filename, "r"); + free(filename); + if (f == NULL) + return; + if (fgets(line, sizeof(line), f) == NULL || + strncmp(line, HISTFILE_FIRST_LINE, strlen(HISTFILE_FIRST_LINE)) != 0) + { + fclose(f); + return; + } + while (fgets(line, sizeof(line), f) != NULL) + { + for (p = line; *p != '\0'; p++) + { + if (*p == '\n' || *p == '\r') + { + *p = '\0'; + break; + } + } + if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0) + { + ml = &mlist_search; + skip = &skip_search; + } else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0) + { +#if SHELL_ESCAPE || PIPEC + ml = &mlist_shell; + skip = &skip_shell; +#else + ml = NULL; + skip = NULL; +#endif + } else if (*line == '"') + { + if (ml != NULL) + { + if (skip != NULL && *skip > 0) + --(*skip); + else + (*action)(uparam, ml, line+1); + } + } + } + fclose(f); +} + + static void +read_cmdhist(action, uparam, skip_search, skip_shell) + void (*action)(void*,struct mlist*,char*); + void *uparam; + int skip_search; + int skip_shell; +{ + read_cmdhist2(action, uparam, skip_search, skip_shell); + (*action)(uparam, NULL, NULL); /* signal end of file */ +} + + static void +addhist_init(void *uparam, struct mlist *ml, char *string) +{ + if (ml == NULL || string == NULL) + return; + cmd_addhist(ml, string, 0); +} +#endif /* CMD_HISTORY */ + +/* + * Initialize history from a .lesshist file. + */ + public void +init_cmdhist() +{ +#if CMD_HISTORY + read_cmdhist(&addhist_init, NULL, 0, 0); +#endif /* CMD_HISTORY */ +} + +/* + * Write the header for a section of the history file. + */ +#if CMD_HISTORY + static void +write_mlist_header(ml, f) + struct mlist *ml; + FILE *f; +{ + if (ml == &mlist_search) + fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION); +#if SHELL_ESCAPE || PIPEC + else if (ml == &mlist_shell) + fprintf(f, "%s\n", HISTFILE_SHELL_SECTION); +#endif +} + +/* + * Write all modified entries in an mlist to the history file. + */ + static void +write_mlist(ml, f) + struct mlist *ml; + FILE *f; +{ + for (ml = ml->next; ml->string != NULL; ml = ml->next) + { + if (!ml->modified) + continue; + fprintf(f, "\"%s\n", ml->string); + ml->modified = 0; + } + ml->modified = 0; /* entire mlist is now unmodified */ +} + +/* + * Make a temp name in the same directory as filename. + */ + static char * +make_tempname(filename) + char *filename; +{ + char lastch; + char *tempname = ecalloc(1, strlen(filename)+1); + strcpy(tempname, filename); + lastch = tempname[strlen(tempname)-1]; + tempname[strlen(tempname)-1] = (lastch == 'Q') ? 'Z' : 'Q'; + return tempname; +} + +struct save_ctx +{ + struct mlist *mlist; + FILE *fout; +}; + +/* + * Copy entries from the saved history file to a new file. + * At the end of each mlist, append any new entries + * created during this session. + */ + static void +copy_hist(void *uparam, struct mlist *ml, char *string) +{ + struct save_ctx *ctx = (struct save_ctx *) uparam; + + if (ml != ctx->mlist) { + /* We're changing mlists. */ + if (ctx->mlist) + /* Append any new entries to the end of the current mlist. */ + write_mlist(ctx->mlist, ctx->fout); + /* Write the header for the new mlist. */ + ctx->mlist = ml; + write_mlist_header(ctx->mlist, ctx->fout); + } + if (string != NULL) + { + /* Copy the entry. */ + fprintf(ctx->fout, "\"%s\n", string); + } + if (ml == NULL) /* End of file */ + { + /* Write any sections that were not in the original file. */ + if (mlist_search.modified) + { + write_mlist_header(&mlist_search, ctx->fout); + write_mlist(&mlist_search, ctx->fout); + } +#if SHELL_ESCAPE || PIPEC + if (mlist_shell.modified) + { + write_mlist_header(&mlist_shell, ctx->fout); + write_mlist(&mlist_shell, ctx->fout); + } +#endif + } +} +#endif /* CMD_HISTORY */ + +/* + * Make a file readable only by its owner. + */ + static void +make_file_private(f) + FILE *f; +{ +#if HAVE_FCHMOD + int do_chmod = 1; +#if HAVE_STAT + struct stat statbuf; + int r = fstat(fileno(f), &statbuf); + if (r < 0 || !S_ISREG(statbuf.st_mode)) + /* Don't chmod if not a regular file. */ + do_chmod = 0; +#endif + if (do_chmod) + fchmod(fileno(f), 0600); +#endif +} + +/* + * Does the history file need to be updated? + */ + static int +histfile_modified() +{ + if (mlist_search.modified) + return 1; +#if SHELL_ESCAPE || PIPEC + if (mlist_shell.modified) + return 1; +#endif + return 0; +} + +/* + * Update the .lesshst file. + */ + public void +save_cmdhist() +{ +#if CMD_HISTORY + char *histname; + char *tempname; + int skip_search; + int skip_shell; + struct save_ctx ctx; + char *s; + FILE *fout = NULL; + int histsize = 0; + + if (!histfile_modified()) + return; + histname = histfile_name(); + if (histname == NULL) + return; + tempname = make_tempname(histname); + fout = fopen(tempname, "w"); + if (fout != NULL) + { + make_file_private(fout); + s = lgetenv("LESSHISTSIZE"); + if (s != NULL) + histsize = atoi(s); + if (histsize <= 0) + histsize = 100; + skip_search = mlist_size(&mlist_search) - histsize; +#if SHELL_ESCAPE || PIPEC + skip_shell = mlist_size(&mlist_shell) - histsize; +#endif + fprintf(fout, "%s\n", HISTFILE_FIRST_LINE); + ctx.fout = fout; + ctx.mlist = NULL; + read_cmdhist(copy_hist, &ctx, skip_search, skip_shell); + fclose(fout); +#if MSDOS_COMPILER==WIN32C + /* + * Windows rename doesn't remove an existing file, + * making it useless for atomic operations. Sigh. + */ + remove(histname); +#endif + rename(tempname, histname); + } + free(tempname); + free(histname); +#endif /* CMD_HISTORY */ +} diff --git a/files/Sources/files/less/command.c b/files/Sources/files/less/command.c new file mode 100644 index 00000000..19daa8fa --- /dev/null +++ b/files/Sources/files/less/command.c @@ -0,0 +1,1849 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * User-level command processor. + */ + +#include "less.h" +#if MSDOS_COMPILER==WIN32C +#include +#endif +#include "position.h" +#include "option.h" +#include "cmd.h" + +extern int erase_char, erase2_char, kill_char; +extern int sigs; +extern int quit_if_one_screen; +extern int squished; +extern int sc_width; +extern int sc_height; +extern int swindow; +extern int jump_sline; +extern int quitting; +extern int wscroll; +extern int top_scroll; +extern int ignore_eoi; +extern int secure; +extern int hshift; +extern int bs_mode; +extern int show_attn; +extern POSITION highest_hilite; +extern char *every_first_cmd; +extern char *curr_altfilename; +extern char version[]; +extern struct scrpos initial_scrpos; +extern IFILE curr_ifile; +extern void constant *ml_search; +extern void constant *ml_examine; +#if SHELL_ESCAPE || PIPEC +extern void constant *ml_shell; +#endif +#if EDITOR +extern char *editor; +extern char *editproto; +#endif +extern int screen_trashed; /* The screen has been overwritten */ +extern int shift_count; +extern int oldbot; +extern int forw_prompt; +extern int same_pos_bel; +extern int file_errors; +extern int unix2003_compat; +extern int add_newline; +extern char * active_dashp_command; +extern int display_next_file_or_exit; +extern int less_is_more; +#if SHELL_ESCAPE +static char *shellcmd = NULL; /* For holding last shell command for "!!" */ +#endif +static int mca; /* The multicharacter command (action) */ +static int search_type; /* The previous type of search */ +static LINENUM number; /* The number typed by the user */ +static long fraction; /* The fractional part of the number */ +static struct loption *curropt; +static int opt_lower; +static int optflag; +static int optgetname; +static POSITION bottompos; +static int save_hshift; +static int save_bs_mode; +#if PIPEC +static char pipec; +#endif + +struct ungot { + struct ungot *ug_next; + char ug_char; + char ug_end_command; +}; +static struct ungot* ungot = NULL; + +static void multi_search(); + +/* + * Move the cursor to start of prompt line before executing a command. + * This looks nicer if the command takes a long time before + * updating the screen. + */ + static void +cmd_exec() +{ +#if HILITE_SEARCH + clear_attn(); +#endif + clear_bot(); + flush(); +} + +/* + * Set up the display to start a new multi-character command. + */ + static void +start_mca(action, prompt, mlist, cmdflags) + int action; + constant char *prompt; + constant void *mlist; + int cmdflags; +{ + mca = action; + clear_bot(); + clear_cmd(); + cmd_putstr(prompt); + set_mlist(mlist, cmdflags); +} + + public int +in_mca() +{ + return (mca != 0 && mca != A_PREFIX); +} + +/* + * Set up the display to start a new search command. + */ + static void +mca_search() +{ +#if HILITE_SEARCH + if (search_type & SRCH_FILTER) + mca = A_FILTER; + else +#endif + if (search_type & SRCH_FORW) + mca = A_F_SEARCH; + else + mca = A_B_SEARCH; + + clear_bot(); + clear_cmd(); + + if (search_type & SRCH_NO_MATCH) + cmd_putstr("Non-match "); + if (search_type & SRCH_FIRST_FILE) + cmd_putstr("First-file "); + if (search_type & SRCH_PAST_EOF) + cmd_putstr("EOF-ignore "); + if (search_type & SRCH_NO_MOVE) + cmd_putstr("Keep-pos "); + if (search_type & SRCH_NO_REGEX) + cmd_putstr("Regex-off "); + +#if HILITE_SEARCH + if (search_type & SRCH_FILTER) + cmd_putstr("&/"); + else +#endif + if (search_type & SRCH_FORW) + cmd_putstr("/"); + else + cmd_putstr("?"); + forw_prompt = 0; + set_mlist(ml_search, 0); +} + +/* + * Set up the display to start a new toggle-option command. + */ + static void +mca_opt_toggle() +{ + int no_prompt; + int flag; + char *dash; + + no_prompt = (optflag & OPT_NO_PROMPT); + flag = (optflag & ~OPT_NO_PROMPT); + dash = (flag == OPT_NO_TOGGLE) ? "_" : "-"; + + mca = A_OPT_TOGGLE; + clear_bot(); + clear_cmd(); + cmd_putstr(dash); + if (optgetname) + cmd_putstr(dash); + if (no_prompt) + cmd_putstr("(P)"); + switch (flag) + { + case OPT_UNSET: + cmd_putstr("+"); + break; + case OPT_SET: + cmd_putstr("!"); + break; + } + forw_prompt = 0; + set_mlist(NULL, 0); +} + +/* + * Execute a multicharacter command. + */ + static void +exec_mca() +{ + register char *cbuf; + + cmd_exec(); + cbuf = get_cmdbuf(); + + switch (mca) + { + case A_F_SEARCH: + case A_B_SEARCH: + multi_search(cbuf, (int) number, 0); + break; +#if HILITE_SEARCH + case A_FILTER: + search_type ^= SRCH_NO_MATCH; + set_filter_pattern(cbuf, search_type); + break; +#endif + case A_FIRSTCMD: + /* + * Skip leading spaces or + signs in the string. + */ + while (*cbuf == '+' || *cbuf == ' ') + cbuf++; + if (every_first_cmd != NULL) + free(every_first_cmd); + if (*cbuf == '\0') + every_first_cmd = NULL; + else + every_first_cmd = save(cbuf); + break; + case A_OPT_TOGGLE: + toggle_option(curropt, opt_lower, cbuf, optflag); + curropt = NULL; + break; + case A_F_BRACKET: + match_brac(cbuf[0], cbuf[1], 1, (int) number); + break; + case A_B_BRACKET: + match_brac(cbuf[1], cbuf[0], 0, (int) number); + break; +#if EXAMINE + case A_EXAMINE: + if (secure) + break; + if (edit_list(cbuf)) { + /* means cbuf is empty, so it's the current file */ + if (unix2003_compat) { + /* Test 114: expects :e to reset linenum */ + number = 1; + jump_back(number); + } + } +#if TAGS + /* If tag structure is loaded then clean it up. */ + cleantags(); +#endif + break; +#endif +#if SHELL_ESCAPE + case A_SHELL: + /* + * !! just uses whatever is in shellcmd. + * Otherwise, copy cmdbuf to shellcmd, + * expanding any special characters ("%" or "#"). + */ + if (*cbuf != '!') + { + if (shellcmd != NULL) + free(shellcmd); + shellcmd = fexpand(cbuf); + } + + if (secure) + break; + if (shellcmd == NULL) + lsystem("", "!done"); + else + lsystem(shellcmd, "!done"); + break; +#endif +#if PIPEC + case A_PIPE: + if (secure) + break; + (void) pipe_mark(pipec, cbuf); + error("|done", NULL_PARG); + break; +#endif + } +} + +/* + * Is a character an erase or kill char? + */ + static int +is_erase_char(c) + int c; +{ + return (c == erase_char || c == erase2_char || c == kill_char); +} + +/* + * Handle the first char of an option (after the initial dash). + */ + static int +mca_opt_first_char(c) + int c; +{ + int flag = (optflag & ~OPT_NO_PROMPT); + if (flag == OPT_NO_TOGGLE) + { + switch (c) + { + case '_': + /* "__" = long option name. */ + optgetname = TRUE; + mca_opt_toggle(); + return (MCA_MORE); + } + } else + { + switch (c) + { + case '+': + /* "-+" = UNSET. */ + optflag = (flag == OPT_UNSET) ? + OPT_TOGGLE : OPT_UNSET; + mca_opt_toggle(); + return (MCA_MORE); + case '!': + /* "-!" = SET */ + optflag = (flag == OPT_SET) ? + OPT_TOGGLE : OPT_SET; + mca_opt_toggle(); + return (MCA_MORE); + case CONTROL('P'): + optflag ^= OPT_NO_PROMPT; + mca_opt_toggle(); + return (MCA_MORE); + case '-': + /* "--" = long option name. */ + optgetname = TRUE; + mca_opt_toggle(); + return (MCA_MORE); + } + } + /* Char was not handled here. */ + return (NO_MCA); +} + +/* + * Add a char to a long option name. + * See if we've got a match for an option name yet. + * If so, display the complete name and stop + * accepting chars until user hits RETURN. + */ + static int +mca_opt_nonfirst_char(c) + int c; +{ + char *p; + char *oname; + + if (curropt != NULL) + { + /* + * Already have a match for the name. + * Don't accept anything but erase/kill. + */ + if (is_erase_char(c)) + return (MCA_DONE); + return (MCA_MORE); + } + /* + * Add char to cmd buffer and try to match + * the option name. + */ + if (cmd_char(c) == CC_QUIT) + return (MCA_DONE); + p = get_cmdbuf(); + opt_lower = ASCII_IS_LOWER(p[0]); + curropt = findopt_name(&p, &oname, NULL); + if (curropt != NULL) + { + /* + * Got a match. + * Remember the option and + * display the full option name. + */ + cmd_reset(); + mca_opt_toggle(); + for (p = oname; *p != '\0'; p++) + { + c = *p; + if (!opt_lower && ASCII_IS_LOWER(c)) + c = ASCII_TO_UPPER(c); + if (cmd_char(c) != CC_OK) + return (MCA_DONE); + } + } + return (MCA_MORE); +} + +/* + * Handle a char of an option toggle command. + */ + static int +mca_opt_char(c) + int c; +{ + PARG parg; + + /* + * This may be a short option (single char), + * or one char of a long option name, + * or one char of the option parameter. + */ + if (curropt == NULL && len_cmdbuf() == 0) + { + int ret = mca_opt_first_char(c); + if (ret != NO_MCA) + return (ret); + } + if (optgetname) + { + /* We're getting a long option name. */ + if (c != '\n' && c != '\r') + return (mca_opt_nonfirst_char(c)); + if (curropt == NULL) + { + parg.p_string = get_cmdbuf(); + error("There is no --%s option", &parg); + return (MCA_DONE); + } + optgetname = FALSE; + cmd_reset(); + } else + { + if (is_erase_char(c)) + return (NO_MCA); + if (curropt != NULL) + /* We're getting the option parameter. */ + return (NO_MCA); + curropt = findopt(c); + if (curropt == NULL) + { + parg.p_string = propt(c); + error("There is no %s option", &parg); + return (MCA_DONE); + } + } + /* + * If the option which was entered does not take a + * parameter, toggle the option immediately, + * so user doesn't have to hit RETURN. + */ + if ((optflag & ~OPT_NO_PROMPT) != OPT_TOGGLE || + !opt_has_param(curropt)) + { + toggle_option(curropt, ASCII_IS_LOWER(c), "", optflag); + return (MCA_DONE); + } + /* + * Display a prompt appropriate for the option parameter. + */ + start_mca(A_OPT_TOGGLE, opt_prompt(curropt), (void*)NULL, 0); + return (MCA_MORE); +} + +/* + * Handle a char of a search command. + */ + static int +mca_search_char(c) + int c; +{ + int flag = 0; + + /* + * Certain characters as the first char of + * the pattern have special meaning: + * ! Toggle the NO_MATCH flag + * * Toggle the PAST_EOF flag + * @ Toggle the FIRST_FILE flag + */ + if (active_dashp_command) { + add_newline = 1; + } + if (len_cmdbuf() > 0) + return (NO_MCA); + + switch (c) + { + case '*': + if (less_is_more) + break; + case CONTROL('E'): /* ignore END of file */ + if (mca != A_FILTER) + flag = SRCH_PAST_EOF; + break; + case '@': + if (less_is_more) + break; + case CONTROL('F'): /* FIRST file */ + if (mca != A_FILTER) + flag = SRCH_FIRST_FILE; + break; + case CONTROL('K'): /* KEEP position */ + if (mca != A_FILTER) + flag = SRCH_NO_MOVE; + break; + case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */ + flag = SRCH_NO_REGEX; + break; + case CONTROL('N'): /* NOT match */ + case '!': + flag = SRCH_NO_MATCH; + break; + } + + if (flag != 0) + { + search_type ^= flag; + mca_search(); + return (MCA_MORE); + } + return (NO_MCA); +} + +/* + * Handle a character of a multi-character command. + */ + static int +mca_char(c) + int c; +{ + int ret; + + switch (mca) + { + case 0: + /* + * We're not in a multicharacter command. + */ + return (NO_MCA); + + case A_PREFIX: + /* + * In the prefix of a command. + * This not considered a multichar command + * (even tho it uses cmdbuf, etc.). + * It is handled in the commands() switch. + */ + return (NO_MCA); + + case A_DIGIT: + /* + * Entering digits of a number. + * Terminated by a non-digit. + */ + if (!((c >= '0' && c <= '9') || c == '.') && + editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE|EC_NORIGHTLEFT) == A_INVALID) + { + /* + * Not part of the number. + * End the number and treat this char + * as a normal command character. + */ + number = cmd_int(&fraction); + mca = 0; + cmd_accept(); + return (NO_MCA); + } + break; + + case A_OPT_TOGGLE: + ret = mca_opt_char(c); + if (ret != NO_MCA) + return (ret); + break; + + case A_F_SEARCH: + case A_B_SEARCH: + case A_FILTER: + ret = mca_search_char(c); + if (ret != NO_MCA) + return (ret); + break; + + default: + /* Other multicharacter command. */ + break; + } + + /* + * The multichar command is terminated by a newline. + */ + if (c == '\n' || c == '\r') + { + /* + * Execute the command. + */ + exec_mca(); + return (MCA_DONE); + } + + /* + * Append the char to the command buffer. + */ + if (cmd_char(c) == CC_QUIT) + /* + * Abort the multi-char command. + */ + return (MCA_DONE); + + if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2) + { + /* + * Special case for the bracket-matching commands. + * Execute the command after getting exactly two + * characters from the user. + */ + exec_mca(); + return (MCA_DONE); + } + + /* + * Need another character. + */ + return (MCA_MORE); +} + +/* + * Discard any buffered file data. + */ + static void +clear_buffers() +{ + if (!(ch_getflags() & CH_CANSEEK)) + return; + ch_flush(); + clr_linenum(); +#if HILITE_SEARCH + clr_hilite(); +#endif +} + +/* + * Make sure the screen is displayed. + */ + static void +make_display() +{ + /* + * If nothing is displayed yet, display starting from initial_scrpos. + */ + if (empty_screen()) + { + if (initial_scrpos.pos == NULL_POSITION) + /* + * {{ Maybe this should be: + * jump_loc(ch_zero(), jump_sline); + * but this behavior seems rather unexpected + * on the first screen. }} + */ + jump_loc(ch_zero(), 1); + else + jump_loc(initial_scrpos.pos, initial_scrpos.ln); + } else if (screen_trashed) + { + int save_top_scroll = top_scroll; + int save_ignore_eoi = ignore_eoi; + top_scroll = 1; + ignore_eoi = 0; + if (screen_trashed == 2) + { + /* Special case used by ignore_eoi: re-open the input file + * and jump to the end of the file. */ + reopen_curr_ifile(); + jump_forw(); + } + repaint(); + top_scroll = save_top_scroll; + ignore_eoi = save_ignore_eoi; + } +} + +/* + * Display the appropriate prompt. + */ + static void +prompt() +{ + register constant char *p; + + if (ungot != NULL && !ungot->ug_end_command) + { + /* + * No prompt necessary if commands are from + * ungotten chars rather than from the user. + */ + return; + } + + /* + * Make sure the screen is displayed. + */ + make_display(); + bottompos = position(BOTTOM_PLUS_ONE); + + /* + * If we've hit EOF on the last file and the -E flag is set, quit. + */ + if (get_quit_at_eof() == OPT_ONPLUS && + eof_displayed() && !(ch_getflags() & CH_HELPFILE) && + next_ifile(curr_ifile) == NULL_IFILE) { + if (file_errors && unix2003_compat) { + quit(QUIT_ERROR); + } + quit(QUIT_OK); + } + /* + * If the entire file is displayed and the -F flag is set, quit. + */ + if (quit_if_one_screen && + entire_file_displayed() && !(ch_getflags() & CH_HELPFILE) && + next_ifile(curr_ifile) == NULL_IFILE) + quit(QUIT_OK); + +#if MSDOS_COMPILER==WIN32C + /* + * In Win32, display the file name in the window title. + */ + if (!(ch_getflags() & CH_HELPFILE)) + SetConsoleTitle(pr_expand("Less?f - %f.", 0)); +#endif + /* + * Select the proper prompt and display it. + */ + /* + * If the previous action was a forward movement, + * don't clear the bottom line of the display; + * just print the prompt since the forward movement guarantees + * that we're in the right position to display the prompt. + * Clearing the line could cause a problem: for example, if the last + * line displayed ended at the right screen edge without a newline, + * then clearing would clear the last displayed line rather than + * the prompt line. + */ + if (!forw_prompt) + clear_bot(); + clear_cmd(); + forw_prompt = 0; + p = pr_string(); + if (is_filtering()) + putstr("& "); + if (p == NULL || *p == '\0') + putchr(':'); + else + { + at_enter(AT_STANDOUT); + putstr(p); + at_exit(); + } + clear_eol(); +} + +/* + * Display the less version message. + */ + public void +dispversion() +{ + PARG parg; + + parg.p_string = version; + error("less %s", &parg); +} + +/* + * Get command character. + * The character normally comes from the keyboard, + * but may come from ungotten characters + * (characters previously given to ungetcc or ungetsc). + */ + public int +getcc() +{ + if (ungot == NULL) + { + /* + * Normal case: no ungotten chars, so get one from the user. + */ + return (getchr()); + } + + /* + * Return the next ungotten char. + */ + { + struct ungot *ug = ungot; + char c = ug->ug_char; + int end_command = ug->ug_end_command; + ungot = ug->ug_next; + free(ug); + if (end_command) + { + /* + * Command is incomplete, so try to complete it. + */ + switch (mca) + { + case A_DIGIT: + /* + * We have a number but no command. Treat as #g. + */ + return ('g'); + + case A_F_SEARCH: + case A_B_SEARCH: + /* + * We have "/string" but no newline. Add the \n. + */ + return ('\n'); + + default: + /* + * Some other incomplete command. Let user complete it. + */ + return (getchr()); + } + } + return (c); + } +} + +/* + * "Unget" a command character. + * The next getcc() will return this character. + */ + public void +ungetcc(c) + int c; +{ + struct ungot *ug = (struct ungot *) ecalloc(1, sizeof(struct ungot)); + + ug->ug_char = (char) c; + ug->ug_end_command = (c == CHAR_END_COMMAND); + ug->ug_next = ungot; + ungot = ug; +} + +/* + * Unget a whole string of command characters. + * The next sequence of getcc()'s will return this string. + */ + public void +ungetsc(s) + char *s; +{ + register char *p; + + for (p = s + strlen(s) - 1; p >= s; p--) + ungetcc(*p); +} + +/* + * Search for a pattern, possibly in multiple files. + * If SRCH_FIRST_FILE is set, begin searching at the first file. + * If SRCH_PAST_EOF is set, continue the search thru multiple files. + */ + static void +multi_search(pattern, n, silent) + char *pattern; + int n; + int silent; +{ + register int nomore; + IFILE save_ifile; + int changed_file; + + changed_file = 0; + save_ifile = save_curr_ifile(); + + if (search_type & SRCH_FIRST_FILE) + { + /* + * Start at the first (or last) file + * in the command line list. + */ + if (search_type & SRCH_FORW) + nomore = edit_first(); + else + nomore = edit_last(); + if (nomore) + { + unsave_ifile(save_ifile); + return; + } + changed_file = 1; + search_type &= ~SRCH_FIRST_FILE; + } + + for (;;) + { + n = search(search_type, pattern, n); + /* + * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared + * after being used once. This allows "n" to work after + * using a /@@ search. + */ + search_type &= ~SRCH_NO_MOVE; + if (n == 0) + { + /* + * Found it. + */ + unsave_ifile(save_ifile); + return; + } + + if (n < 0) + /* + * Some kind of error in the search. + * Error message has been printed by search(). + */ + break; + + if ((search_type & SRCH_PAST_EOF) == 0) + /* + * We didn't find a match, but we're + * supposed to search only one file. + */ + break; + /* + * Move on to the next file. + */ + if (search_type & SRCH_FORW) + nomore = edit_next(1); + else + nomore = edit_prev(1); + if (nomore) + break; + changed_file = 1; + } + + /* + * Didn't find it. + * Print an error message if we haven't already. + */ + if (n > 0 && !silent) + error("Pattern not found", NULL_PARG); + + if (changed_file) + { + /* + * Restore the file we were originally viewing. + */ + reedit_ifile(save_ifile); + } else + { + unsave_ifile(save_ifile); + } +} + +/* + * Forward forever, or until a highlighted line appears. + */ + static int +forw_loop(until_hilite) + int until_hilite; +{ + POSITION curr_len; + + if (ch_getflags() & CH_HELPFILE) + return (A_NOACTION); + + cmd_exec(); + jump_forw_buffered(); + curr_len = ch_length(); + highest_hilite = until_hilite ? curr_len : NULL_POSITION; + ignore_eoi = 1; + while (!sigs) + { + if (until_hilite && highest_hilite > curr_len) + { + bell(); + break; + } + make_display(); + forward(1, 0, 0); + } + ignore_eoi = 0; + ch_set_eof(); + + /* + * This gets us back in "F mode" after processing + * a non-abort signal (e.g. window-change). + */ + if (sigs && !ABORT_SIGS()) + return (until_hilite ? A_F_UNTIL_HILITE : A_F_FOREVER); + + return (A_NOACTION); +} + +/* + * Main command processor. + * Accept and execute commands until a quit command. + */ + public void +commands() +{ + register int c; + register int action; + register char *cbuf; + int newaction; + int save_search_type; + char *extra; + char tbuf[2]; + PARG parg; + IFILE old_ifile; + IFILE new_ifile; + char *tagfile; + + search_type = SRCH_FORW; + if (unix2003_compat) { + wscroll = (sc_height) / 2; + } else { + wscroll = (sc_height + 1) / 2; + } + newaction = A_NOACTION; + + for (;;) + { + mca = 0; + cmd_accept(); + number = 0; + curropt = NULL; + + /* + * See if any signals need processing. + */ + if (sigs) + { + psignals(); + if (quitting) + quit(QUIT_SAVED_STATUS); + } + + /* + * See if window size changed, for systems that don't + * generate SIGWINCH. + */ + check_winch(); + + /* + * Display prompt and accept a character. + */ + cmd_reset(); + prompt(); + if (sigs) + continue; + if (newaction == A_NOACTION) + c = getcc(); + + again: + if (sigs) + continue; + + if (newaction != A_NOACTION) + { + action = newaction; + newaction = A_NOACTION; + } else + { + /* + * If we are in a multicharacter command, call mca_char. + * Otherwise we call fcmd_decode to determine the + * action to be performed. + */ + if (mca) + switch (mca_char(c)) + { + case MCA_MORE: + /* + * Need another character. + */ + c = getcc(); + goto again; + case MCA_DONE: + /* + * Command has been handled by mca_char. + * Start clean with a prompt. + */ + continue; + case NO_MCA: + /* + * Not a multi-char command + * (at least, not anymore). + */ + break; + } + + /* + * Decode the command character and decide what to do. + */ + if (mca) + { + /* + * We're in a multichar command. + * Add the character to the command buffer + * and display it on the screen. + * If the user backspaces past the start + * of the line, abort the command. + */ + if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0) + continue; + cbuf = get_cmdbuf(); + } else + { + /* + * Don't use cmd_char if we're starting fresh + * at the beginning of a command, because we + * don't want to echo the command until we know + * it is a multichar command. We also don't + * want erase_char/kill_char to be treated + * as line editing characters. + */ + tbuf[0] = c; + tbuf[1] = '\0'; + cbuf = tbuf; + } + extra = NULL; + action = fcmd_decode(cbuf, &extra); + /* + * If an "extra" string was returned, + * process it as a string of command characters. + */ + if (extra != NULL) + ungetsc(extra); + } + /* + * Clear the cmdbuf string. + * (But not if we're in the prefix of a command, + * because the partial command string is kept there.) + */ + if (action != A_PREFIX) + cmd_reset(); + + display_next_file_or_exit = 0; + switch (action) + { + case A_DIGIT: + /* + * First digit of a number. + */ + start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE); + goto again; + + case A_F_WINDOW: + /* + * Forward one window (and set the window size). + */ + if (number > 0) + swindow = (int) number; + /* FALLTHRU */ + case A_F_SCREEN: + /* + * Forward one screen. + */ + display_next_file_or_exit = 1; + if (number <= 0) + number = get_swindow(); + cmd_exec(); + if (show_attn) + set_attnpos(bottompos); + forward((int) number, 0, 1); + break; + + case A_B_WINDOW: + /* + * Backward one window (and set the window size). + */ + if (number > 0) + swindow = (int) number; + /* FALLTHRU */ + case A_B_SCREEN: + /* + * Backward one screen. + */ + if (number <= 0) + number = get_swindow(); + cmd_exec(); + backward((int) number, 0, 1); + break; + + case A_F_LINE: + /* + * Forward N (default 1) line. + */ + display_next_file_or_exit = 1; + if (number <= 0) + number = 1; + cmd_exec(); + if (show_attn == OPT_ONPLUS && number > 1) + set_attnpos(bottompos); + forward((int) number, 0, 0); + break; + + case A_B_LINE: + /* + * Backward N (default 1) line. + */ + if (number <= 0) + number = 1; + cmd_exec(); + backward((int) number, 0, 0); + break; + + case A_FF_LINE: + /* + * Force forward N (default 1) line. + */ + if (number <= 0) + number = 1; + cmd_exec(); + if (show_attn == OPT_ONPLUS && number > 1) + set_attnpos(bottompos); + forward((int) number, 1, 0); + break; + + case A_BF_LINE: + /* + * Force backward N (default 1) line. + */ + if (number <= 0) + number = 1; + cmd_exec(); + backward((int) number, 1, 0); + break; + + case A_FF_SCREEN: + /* + * Force forward one screen. + */ + if (number <= 0) + number = get_swindow(); + cmd_exec(); + if (show_attn == OPT_ONPLUS) + set_attnpos(bottompos); + forward((int) number, 1, 0); + break; + + case A_F_FOREVER: + /* + * Forward forever, ignoring EOF. + */ + if (show_attn) + set_attnpos(bottompos); + newaction = forw_loop(0); + break; + + case A_F_UNTIL_HILITE: + newaction = forw_loop(1); + break; + + case A_F_SCROLL: + /* + * Forward N lines + * (default same as last 'd' or 'u' command). + */ + if (number > 0) + wscroll = (int) number; + cmd_exec(); + if (show_attn == OPT_ONPLUS) + set_attnpos(bottompos); + forward(wscroll, 0, 0); + break; + + case A_B_SCROLL: + /* + * Forward N lines + * (default same as last 'd' or 'u' command). + */ + display_next_file_or_exit = 1; + if (number > 0) + wscroll = (int) number; + cmd_exec(); + backward(wscroll, 0, 0); + break; + + case A_FREPAINT: + /* + * Flush buffers, then repaint screen. + * Don't flush the buffers on a pipe! + */ + clear_buffers(); + /* FALLTHRU */ + case A_REPAINT: + /* + * Repaint screen. + */ + cmd_exec(); + repaint(); + break; + + case A_GOLINE: + /* + * Go to line N, default beginning of file. + */ + if (number <= 0) + number = 1; + cmd_exec(); + jump_back(number); + break; + + case A_PERCENT: + /* + * Go to a specified percentage into the file. + */ + if (number < 0) + { + number = 0; + fraction = 0; + } + if (number > 100) + { + number = 100; + fraction = 0; + } + cmd_exec(); + jump_percent((int) number, fraction); + break; + + case A_GOEND: + /* + * Go to line N, default end of file. + */ + cmd_exec(); + if (number <= 0) + jump_forw(); + else + jump_back(number); + break; + + case A_GOEND_BUF: + /* + * Go to line N, default last buffered byte. + */ + cmd_exec(); + if (number <= 0) + jump_forw_buffered(); + else + jump_back(number); + break; + + case A_GOPOS: + /* + * Go to a specified byte position in the file. + */ + cmd_exec(); + if (number < 0) + number = 0; + jump_line_loc((POSITION) number, jump_sline); + break; + + case A_STAT: + /* + * Print file name, etc. + */ + if (ch_getflags() & CH_HELPFILE) + break; + cmd_exec(); + parg.p_string = eq_message(); + error("%s", &parg); + break; + + case A_VERSION: + /* + * Print version number, without the "@(#)". + */ + cmd_exec(); + dispversion(); + break; + + case A_QUIT: + /* + * Exit. + */ + if (curr_ifile != NULL_IFILE && + ch_getflags() & CH_HELPFILE) + { + /* + * Quit while viewing the help file + * just means return to viewing the + * previous file. + */ + hshift = save_hshift; + bs_mode = save_bs_mode; + if (edit_prev(1) == 0) + break; + } + if (extra != NULL) + quit(*extra); + if (file_errors) + if (unix2003_compat) + quit(QUIT_ERROR); + quit(QUIT_OK); + break; + +/* + * Define abbreviation for a commonly used sequence below. + */ +#define DO_SEARCH() \ + if (number <= 0) number = 1; \ + mca_search(); \ + cmd_exec(); \ + multi_search((char *)NULL, (int) number, 0); + + + case A_F_SEARCH: + /* + * Search forward for a pattern. + * Get the first char of the pattern. + */ + search_type = SRCH_FORW; + if (number <= 0) + number = 1; + mca_search(); + c = getcc(); + goto again; + + case A_B_SEARCH: + /* + * Search backward for a pattern. + * Get the first char of the pattern. + */ + search_type = SRCH_BACK; + if (number <= 0) + number = 1; + mca_search(); + c = getcc(); + goto again; + + case A_FILTER: +#if HILITE_SEARCH + search_type = SRCH_FORW | SRCH_FILTER; + mca_search(); + c = getcc(); + goto again; +#else + error("Command not available", NULL_PARG); + break; +#endif + + case A_AGAIN_SEARCH: + /* + * Repeat previous search. + */ + DO_SEARCH(); + break; + + case A_T_AGAIN_SEARCH: + /* + * Repeat previous search, multiple files. + */ + search_type |= SRCH_PAST_EOF; + DO_SEARCH(); + break; + + case A_REVERSE_SEARCH: + /* + * Repeat previous search, in reverse direction. + */ + save_search_type = search_type; + search_type = SRCH_REVERSE(search_type); + DO_SEARCH(); + search_type = save_search_type; + break; + + case A_T_REVERSE_SEARCH: + /* + * Repeat previous search, + * multiple files in reverse direction. + */ + save_search_type = search_type; + search_type = SRCH_REVERSE(search_type); + search_type |= SRCH_PAST_EOF; + DO_SEARCH(); + search_type = save_search_type; + break; + + case A_UNDO_SEARCH: + undo_search(); + break; + + case A_HELP: + /* + * Help. + */ + if (ch_getflags() & CH_HELPFILE) + break; + cmd_exec(); + save_hshift = hshift; + hshift = 0; + save_bs_mode = bs_mode; + bs_mode = BS_SPECIAL; + (void) edit(FAKE_HELPFILE); + break; + + case A_EXAMINE: +#if EXAMINE + /* + * Edit a new file. Get the filename. + */ + if (secure) + { + error("Command not available", NULL_PARG); + break; + } + start_mca(A_EXAMINE, "Examine: ", ml_examine, 0); + c = getcc(); + goto again; +#else + error("Command not available", NULL_PARG); + break; +#endif + + case A_VISUAL: + /* + * Invoke an editor on the input file. + */ +#if EDITOR + if (secure) + { + error("Command not available", NULL_PARG); + break; + } + if (ch_getflags() & CH_HELPFILE) + break; + if (strcmp(get_filename(curr_ifile), "-") == 0) + { + error("Cannot edit standard input", NULL_PARG); + break; + } + if (curr_altfilename != NULL) + { + error("WARNING: This file was viewed via LESSOPEN", + NULL_PARG); + } + start_mca(A_SHELL, "!", ml_shell, 0); + /* + * Expand the editor prototype string + * and pass it to the system to execute. + * (Make sure the screen is displayed so the + * expansion of "+%lm" works.) + */ + make_display(); + cmd_exec(); + lsystem(pr_expand(editproto, 0), (char*)NULL); + break; +#else + error("Command not available", NULL_PARG); + break; +#endif + + case A_NEXT_FILE: + /* + * Examine next file. + */ +#if TAGS + if (ntags()) + { + error("No next file", NULL_PARG); + break; + } +#endif + if (number <= 0) + number = 1; + if (edit_next((int) number)) + { + if (get_quit_at_eof() && eof_displayed() && + !(ch_getflags() & CH_HELPFILE)) + quit(QUIT_OK); + parg.p_string = (number > 1) ? "(N-th) " : ""; + error("No %snext file", &parg); + } + break; + + case A_PREV_FILE: + /* + * Examine previous file. + */ +#if TAGS + if (ntags()) + { + error("No previous file", NULL_PARG); + break; + } +#endif + if (number <= 0) + number = 1; + if (edit_prev((int) number)) + { + parg.p_string = (number > 1) ? "(N-th) " : ""; + error("No %sprevious file", &parg); + } + break; + + case A_NEXT_TAG: +#if TAGS + if (number <= 0) + number = 1; + tagfile = nexttag((int) number); + if (tagfile == NULL) + { + error("No next tag", NULL_PARG); + break; + } + if (edit(tagfile) == 0) + { + POSITION pos = tagsearch(); + if (pos != NULL_POSITION) + jump_loc(pos, jump_sline); + } +#else + error("Command not available", NULL_PARG); +#endif + break; + + case A_PREV_TAG: +#if TAGS + if (number <= 0) + number = 1; + tagfile = prevtag((int) number); + if (tagfile == NULL) + { + error("No previous tag", NULL_PARG); + break; + } + if (edit(tagfile) == 0) + { + POSITION pos = tagsearch(); + if (pos != NULL_POSITION) + jump_loc(pos, jump_sline); + } +#else + error("Command not available", NULL_PARG); +#endif + break; + + case A_INDEX_FILE: + /* + * Examine a particular file. + */ + if (number <= 0) + number = 1; + if (edit_index((int) number)) + error("No such file", NULL_PARG); + break; + + case A_REMOVE_FILE: + if (ch_getflags() & CH_HELPFILE) + break; + old_ifile = curr_ifile; + new_ifile = getoff_ifile(curr_ifile); + if (new_ifile == NULL_IFILE) + { + bell(); + break; + } + if (edit_ifile(new_ifile) != 0) + { + reedit_ifile(old_ifile); + break; + } + del_ifile(old_ifile); + break; + + case A_OPT_TOGGLE: + optflag = OPT_TOGGLE; + optgetname = FALSE; + mca_opt_toggle(); + c = getcc(); + goto again; + + case A_DISP_OPTION: + /* + * Report a flag setting. + */ + optflag = OPT_NO_TOGGLE; + optgetname = FALSE; + mca_opt_toggle(); + c = getcc(); + goto again; + + case A_FIRSTCMD: + /* + * Set an initial command for new files. + */ + start_mca(A_FIRSTCMD, "+", (void*)NULL, 0); + c = getcc(); + goto again; + + case A_SHELL: + /* + * Shell escape. + */ +#if SHELL_ESCAPE + if (secure) + { + error("Command not available", NULL_PARG); + break; + } + start_mca(A_SHELL, "!", ml_shell, 0); + c = getcc(); + goto again; +#else + error("Command not available", NULL_PARG); + break; +#endif + + case A_SETMARK: + /* + * Set a mark. + */ + if (ch_getflags() & CH_HELPFILE) + break; + start_mca(A_SETMARK, "mark: ", (void*)NULL, 0); + c = getcc(); + if (c == erase_char || c == erase2_char || + c == kill_char || c == '\n' || c == '\r') + break; + setmark(c); + break; + + case A_GOMARK: + /* + * Go to a mark. + */ + start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0); + c = getcc(); + if (c == erase_char || c == erase2_char || + c == kill_char || c == '\n' || c == '\r') + break; + cmd_exec(); + gomark(c); + break; + + case A_PIPE: +#if PIPEC + if (secure) + { + error("Command not available", NULL_PARG); + break; + } + start_mca(A_PIPE, "|mark: ", (void*)NULL, 0); + c = getcc(); + if (c == erase_char || c == erase2_char || c == kill_char) + break; + if (c == '\n' || c == '\r') + c = '.'; + if (badmark(c)) + break; + pipec = c; + start_mca(A_PIPE, "!", ml_shell, 0); + c = getcc(); + goto again; +#else + error("Command not available", NULL_PARG); + break; +#endif + + case A_B_BRACKET: + case A_F_BRACKET: + start_mca(action, "Brackets: ", (void*)NULL, 0); + c = getcc(); + goto again; + + case A_LSHIFT: + if (number > 0) + shift_count = number; + else + number = (shift_count > 0) ? + shift_count : sc_width / 2; + if (number > hshift) + number = hshift; + hshift -= number; + screen_trashed = 1; + break; + + case A_RSHIFT: + if (number > 0) + shift_count = number; + else + number = (shift_count > 0) ? + shift_count : sc_width / 2; + hshift += number; + screen_trashed = 1; + break; + + case A_LLSHIFT: + hshift = 0; + screen_trashed = 1; + break; + + case A_RRSHIFT: + hshift = rrshift(); + screen_trashed = 1; + break; + + case A_PREFIX: + /* + * The command is incomplete (more chars are needed). + * Display the current char, so the user knows + * what's going on, and get another character. + */ + if (mca != A_PREFIX) + { + cmd_reset(); + start_mca(A_PREFIX, " ", (void*)NULL, + CF_QUIT_ON_ERASE); + (void) cmd_char(c); + } + c = getcc(); + goto again; + + case A_NOACTION: + break; + + default: + bell(); + break; + } + } +} diff --git a/files/Sources/files/less/compose.uni b/files/Sources/files/less/compose.uni new file mode 100644 index 00000000..e3e1fa40 --- /dev/null +++ b/files/Sources/files/less/compose.uni @@ -0,0 +1,291 @@ +/* Generated by "./mkutable -f2 Mn Me -- unicode/UnicodeData.txt" on Tue Sep 20 10:51:43 PDT 2016 */ + { 0x0300, 0x036f }, /* Mn */ + { 0x0483, 0x0487 }, /* Mn */ + { 0x0488, 0x0489 }, /* Me */ + { 0x0591, 0x05bd }, /* Mn */ + { 0x05bf, 0x05bf }, /* Mn */ + { 0x05c1, 0x05c2 }, /* Mn */ + { 0x05c4, 0x05c5 }, /* Mn */ + { 0x05c7, 0x05c7 }, /* Mn */ + { 0x0610, 0x061a }, /* Mn */ + { 0x064b, 0x065f }, /* Mn */ + { 0x0670, 0x0670 }, /* Mn */ + { 0x06d6, 0x06dc }, /* Mn */ + { 0x06df, 0x06e4 }, /* Mn */ + { 0x06e7, 0x06e8 }, /* Mn */ + { 0x06ea, 0x06ed }, /* Mn */ + { 0x0711, 0x0711 }, /* Mn */ + { 0x0730, 0x074a }, /* Mn */ + { 0x07a6, 0x07b0 }, /* Mn */ + { 0x07eb, 0x07f3 }, /* Mn */ + { 0x0816, 0x0819 }, /* Mn */ + { 0x081b, 0x0823 }, /* Mn */ + { 0x0825, 0x0827 }, /* Mn */ + { 0x0829, 0x082d }, /* Mn */ + { 0x0859, 0x085b }, /* Mn */ + { 0x08d4, 0x08e1 }, /* Mn */ + { 0x08e3, 0x0902 }, /* Mn */ + { 0x093a, 0x093a }, /* Mn */ + { 0x093c, 0x093c }, /* Mn */ + { 0x0941, 0x0948 }, /* Mn */ + { 0x094d, 0x094d }, /* Mn */ + { 0x0951, 0x0957 }, /* Mn */ + { 0x0962, 0x0963 }, /* Mn */ + { 0x0981, 0x0981 }, /* Mn */ + { 0x09bc, 0x09bc }, /* Mn */ + { 0x09c1, 0x09c4 }, /* Mn */ + { 0x09cd, 0x09cd }, /* Mn */ + { 0x09e2, 0x09e3 }, /* Mn */ + { 0x0a01, 0x0a02 }, /* Mn */ + { 0x0a3c, 0x0a3c }, /* Mn */ + { 0x0a41, 0x0a42 }, /* Mn */ + { 0x0a47, 0x0a48 }, /* Mn */ + { 0x0a4b, 0x0a4d }, /* Mn */ + { 0x0a51, 0x0a51 }, /* Mn */ + { 0x0a70, 0x0a71 }, /* Mn */ + { 0x0a75, 0x0a75 }, /* Mn */ + { 0x0a81, 0x0a82 }, /* Mn */ + { 0x0abc, 0x0abc }, /* Mn */ + { 0x0ac1, 0x0ac5 }, /* Mn */ + { 0x0ac7, 0x0ac8 }, /* Mn */ + { 0x0acd, 0x0acd }, /* Mn */ + { 0x0ae2, 0x0ae3 }, /* Mn */ + { 0x0b01, 0x0b01 }, /* Mn */ + { 0x0b3c, 0x0b3c }, /* Mn */ + { 0x0b3f, 0x0b3f }, /* Mn */ + { 0x0b41, 0x0b44 }, /* Mn */ + { 0x0b4d, 0x0b4d }, /* Mn */ + { 0x0b56, 0x0b56 }, /* Mn */ + { 0x0b62, 0x0b63 }, /* Mn */ + { 0x0b82, 0x0b82 }, /* Mn */ + { 0x0bc0, 0x0bc0 }, /* Mn */ + { 0x0bcd, 0x0bcd }, /* Mn */ + { 0x0c00, 0x0c00 }, /* Mn */ + { 0x0c3e, 0x0c40 }, /* Mn */ + { 0x0c46, 0x0c48 }, /* Mn */ + { 0x0c4a, 0x0c4d }, /* Mn */ + { 0x0c55, 0x0c56 }, /* Mn */ + { 0x0c62, 0x0c63 }, /* Mn */ + { 0x0c81, 0x0c81 }, /* Mn */ + { 0x0cbc, 0x0cbc }, /* Mn */ + { 0x0cbf, 0x0cbf }, /* Mn */ + { 0x0cc6, 0x0cc6 }, /* Mn */ + { 0x0ccc, 0x0ccd }, /* Mn */ + { 0x0ce2, 0x0ce3 }, /* Mn */ + { 0x0d01, 0x0d01 }, /* Mn */ + { 0x0d41, 0x0d44 }, /* Mn */ + { 0x0d4d, 0x0d4d }, /* Mn */ + { 0x0d62, 0x0d63 }, /* Mn */ + { 0x0dca, 0x0dca }, /* Mn */ + { 0x0dd2, 0x0dd4 }, /* Mn */ + { 0x0dd6, 0x0dd6 }, /* Mn */ + { 0x0e31, 0x0e31 }, /* Mn */ + { 0x0e34, 0x0e3a }, /* Mn */ + { 0x0e47, 0x0e4e }, /* Mn */ + { 0x0eb1, 0x0eb1 }, /* Mn */ + { 0x0eb4, 0x0eb9 }, /* Mn */ + { 0x0ebb, 0x0ebc }, /* Mn */ + { 0x0ec8, 0x0ecd }, /* Mn */ + { 0x0f18, 0x0f19 }, /* Mn */ + { 0x0f35, 0x0f35 }, /* Mn */ + { 0x0f37, 0x0f37 }, /* Mn */ + { 0x0f39, 0x0f39 }, /* Mn */ + { 0x0f71, 0x0f7e }, /* Mn */ + { 0x0f80, 0x0f84 }, /* Mn */ + { 0x0f86, 0x0f87 }, /* Mn */ + { 0x0f8d, 0x0f97 }, /* Mn */ + { 0x0f99, 0x0fbc }, /* Mn */ + { 0x0fc6, 0x0fc6 }, /* Mn */ + { 0x102d, 0x1030 }, /* Mn */ + { 0x1032, 0x1037 }, /* Mn */ + { 0x1039, 0x103a }, /* Mn */ + { 0x103d, 0x103e }, /* Mn */ + { 0x1058, 0x1059 }, /* Mn */ + { 0x105e, 0x1060 }, /* Mn */ + { 0x1071, 0x1074 }, /* Mn */ + { 0x1082, 0x1082 }, /* Mn */ + { 0x1085, 0x1086 }, /* Mn */ + { 0x108d, 0x108d }, /* Mn */ + { 0x109d, 0x109d }, /* Mn */ + { 0x135d, 0x135f }, /* Mn */ + { 0x1712, 0x1714 }, /* Mn */ + { 0x1732, 0x1734 }, /* Mn */ + { 0x1752, 0x1753 }, /* Mn */ + { 0x1772, 0x1773 }, /* Mn */ + { 0x17b4, 0x17b5 }, /* Mn */ + { 0x17b7, 0x17bd }, /* Mn */ + { 0x17c6, 0x17c6 }, /* Mn */ + { 0x17c9, 0x17d3 }, /* Mn */ + { 0x17dd, 0x17dd }, /* Mn */ + { 0x180b, 0x180d }, /* Mn */ + { 0x1885, 0x1886 }, /* Mn */ + { 0x18a9, 0x18a9 }, /* Mn */ + { 0x1920, 0x1922 }, /* Mn */ + { 0x1927, 0x1928 }, /* Mn */ + { 0x1932, 0x1932 }, /* Mn */ + { 0x1939, 0x193b }, /* Mn */ + { 0x1a17, 0x1a18 }, /* Mn */ + { 0x1a1b, 0x1a1b }, /* Mn */ + { 0x1a56, 0x1a56 }, /* Mn */ + { 0x1a58, 0x1a5e }, /* Mn */ + { 0x1a60, 0x1a60 }, /* Mn */ + { 0x1a62, 0x1a62 }, /* Mn */ + { 0x1a65, 0x1a6c }, /* Mn */ + { 0x1a73, 0x1a7c }, /* Mn */ + { 0x1a7f, 0x1a7f }, /* Mn */ + { 0x1ab0, 0x1abd }, /* Mn */ + { 0x1abe, 0x1abe }, /* Me */ + { 0x1b00, 0x1b03 }, /* Mn */ + { 0x1b34, 0x1b34 }, /* Mn */ + { 0x1b36, 0x1b3a }, /* Mn */ + { 0x1b3c, 0x1b3c }, /* Mn */ + { 0x1b42, 0x1b42 }, /* Mn */ + { 0x1b6b, 0x1b73 }, /* Mn */ + { 0x1b80, 0x1b81 }, /* Mn */ + { 0x1ba2, 0x1ba5 }, /* Mn */ + { 0x1ba8, 0x1ba9 }, /* Mn */ + { 0x1bab, 0x1bad }, /* Mn */ + { 0x1be6, 0x1be6 }, /* Mn */ + { 0x1be8, 0x1be9 }, /* Mn */ + { 0x1bed, 0x1bed }, /* Mn */ + { 0x1bef, 0x1bf1 }, /* Mn */ + { 0x1c2c, 0x1c33 }, /* Mn */ + { 0x1c36, 0x1c37 }, /* Mn */ + { 0x1cd0, 0x1cd2 }, /* Mn */ + { 0x1cd4, 0x1ce0 }, /* Mn */ + { 0x1ce2, 0x1ce8 }, /* Mn */ + { 0x1ced, 0x1ced }, /* Mn */ + { 0x1cf4, 0x1cf4 }, /* Mn */ + { 0x1cf8, 0x1cf9 }, /* Mn */ + { 0x1dc0, 0x1df5 }, /* Mn */ + { 0x1dfb, 0x1dff }, /* Mn */ + { 0x20d0, 0x20dc }, /* Mn */ + { 0x20dd, 0x20e0 }, /* Me */ + { 0x20e1, 0x20e1 }, /* Mn */ + { 0x20e2, 0x20e4 }, /* Me */ + { 0x20e5, 0x20f0 }, /* Mn */ + { 0x2cef, 0x2cf1 }, /* Mn */ + { 0x2d7f, 0x2d7f }, /* Mn */ + { 0x2de0, 0x2dff }, /* Mn */ + { 0x302a, 0x302d }, /* Mn */ + { 0x3099, 0x309a }, /* Mn */ + { 0xa66f, 0xa66f }, /* Mn */ + { 0xa670, 0xa672 }, /* Me */ + { 0xa674, 0xa67d }, /* Mn */ + { 0xa69e, 0xa69f }, /* Mn */ + { 0xa6f0, 0xa6f1 }, /* Mn */ + { 0xa802, 0xa802 }, /* Mn */ + { 0xa806, 0xa806 }, /* Mn */ + { 0xa80b, 0xa80b }, /* Mn */ + { 0xa825, 0xa826 }, /* Mn */ + { 0xa8c4, 0xa8c5 }, /* Mn */ + { 0xa8e0, 0xa8f1 }, /* Mn */ + { 0xa926, 0xa92d }, /* Mn */ + { 0xa947, 0xa951 }, /* Mn */ + { 0xa980, 0xa982 }, /* Mn */ + { 0xa9b3, 0xa9b3 }, /* Mn */ + { 0xa9b6, 0xa9b9 }, /* Mn */ + { 0xa9bc, 0xa9bc }, /* Mn */ + { 0xa9e5, 0xa9e5 }, /* Mn */ + { 0xaa29, 0xaa2e }, /* Mn */ + { 0xaa31, 0xaa32 }, /* Mn */ + { 0xaa35, 0xaa36 }, /* Mn */ + { 0xaa43, 0xaa43 }, /* Mn */ + { 0xaa4c, 0xaa4c }, /* Mn */ + { 0xaa7c, 0xaa7c }, /* Mn */ + { 0xaab0, 0xaab0 }, /* Mn */ + { 0xaab2, 0xaab4 }, /* Mn */ + { 0xaab7, 0xaab8 }, /* Mn */ + { 0xaabe, 0xaabf }, /* Mn */ + { 0xaac1, 0xaac1 }, /* Mn */ + { 0xaaec, 0xaaed }, /* Mn */ + { 0xaaf6, 0xaaf6 }, /* Mn */ + { 0xabe5, 0xabe5 }, /* Mn */ + { 0xabe8, 0xabe8 }, /* Mn */ + { 0xabed, 0xabed }, /* Mn */ + { 0xfb1e, 0xfb1e }, /* Mn */ + { 0xfe00, 0xfe0f }, /* Mn */ + { 0xfe20, 0xfe2f }, /* Mn */ + { 0x101fd, 0x101fd }, /* Mn */ + { 0x102e0, 0x102e0 }, /* Mn */ + { 0x10376, 0x1037a }, /* Mn */ + { 0x10a01, 0x10a03 }, /* Mn */ + { 0x10a05, 0x10a06 }, /* Mn */ + { 0x10a0c, 0x10a0f }, /* Mn */ + { 0x10a38, 0x10a3a }, /* Mn */ + { 0x10a3f, 0x10a3f }, /* Mn */ + { 0x10ae5, 0x10ae6 }, /* Mn */ + { 0x11001, 0x11001 }, /* Mn */ + { 0x11038, 0x11046 }, /* Mn */ + { 0x1107f, 0x11081 }, /* Mn */ + { 0x110b3, 0x110b6 }, /* Mn */ + { 0x110b9, 0x110ba }, /* Mn */ + { 0x11100, 0x11102 }, /* Mn */ + { 0x11127, 0x1112b }, /* Mn */ + { 0x1112d, 0x11134 }, /* Mn */ + { 0x11173, 0x11173 }, /* Mn */ + { 0x11180, 0x11181 }, /* Mn */ + { 0x111b6, 0x111be }, /* Mn */ + { 0x111ca, 0x111cc }, /* Mn */ + { 0x1122f, 0x11231 }, /* Mn */ + { 0x11234, 0x11234 }, /* Mn */ + { 0x11236, 0x11237 }, /* Mn */ + { 0x1123e, 0x1123e }, /* Mn */ + { 0x112df, 0x112df }, /* Mn */ + { 0x112e3, 0x112ea }, /* Mn */ + { 0x11300, 0x11301 }, /* Mn */ + { 0x1133c, 0x1133c }, /* Mn */ + { 0x11340, 0x11340 }, /* Mn */ + { 0x11366, 0x1136c }, /* Mn */ + { 0x11370, 0x11374 }, /* Mn */ + { 0x11438, 0x1143f }, /* Mn */ + { 0x11442, 0x11444 }, /* Mn */ + { 0x11446, 0x11446 }, /* Mn */ + { 0x114b3, 0x114b8 }, /* Mn */ + { 0x114ba, 0x114ba }, /* Mn */ + { 0x114bf, 0x114c0 }, /* Mn */ + { 0x114c2, 0x114c3 }, /* Mn */ + { 0x115b2, 0x115b5 }, /* Mn */ + { 0x115bc, 0x115bd }, /* Mn */ + { 0x115bf, 0x115c0 }, /* Mn */ + { 0x115dc, 0x115dd }, /* Mn */ + { 0x11633, 0x1163a }, /* Mn */ + { 0x1163d, 0x1163d }, /* Mn */ + { 0x1163f, 0x11640 }, /* Mn */ + { 0x116ab, 0x116ab }, /* Mn */ + { 0x116ad, 0x116ad }, /* Mn */ + { 0x116b0, 0x116b5 }, /* Mn */ + { 0x116b7, 0x116b7 }, /* Mn */ + { 0x1171d, 0x1171f }, /* Mn */ + { 0x11722, 0x11725 }, /* Mn */ + { 0x11727, 0x1172b }, /* Mn */ + { 0x11c30, 0x11c36 }, /* Mn */ + { 0x11c38, 0x11c3d }, /* Mn */ + { 0x11c3f, 0x11c3f }, /* Mn */ + { 0x11c92, 0x11ca7 }, /* Mn */ + { 0x11caa, 0x11cb0 }, /* Mn */ + { 0x11cb2, 0x11cb3 }, /* Mn */ + { 0x11cb5, 0x11cb6 }, /* Mn */ + { 0x16af0, 0x16af4 }, /* Mn */ + { 0x16b30, 0x16b36 }, /* Mn */ + { 0x16f8f, 0x16f92 }, /* Mn */ + { 0x1bc9d, 0x1bc9e }, /* Mn */ + { 0x1d167, 0x1d169 }, /* Mn */ + { 0x1d17b, 0x1d182 }, /* Mn */ + { 0x1d185, 0x1d18b }, /* Mn */ + { 0x1d1aa, 0x1d1ad }, /* Mn */ + { 0x1d242, 0x1d244 }, /* Mn */ + { 0x1da00, 0x1da36 }, /* Mn */ + { 0x1da3b, 0x1da6c }, /* Mn */ + { 0x1da75, 0x1da75 }, /* Mn */ + { 0x1da84, 0x1da84 }, /* Mn */ + { 0x1da9b, 0x1da9f }, /* Mn */ + { 0x1daa1, 0x1daaf }, /* Mn */ + { 0x1e000, 0x1e006 }, /* Mn */ + { 0x1e008, 0x1e018 }, /* Mn */ + { 0x1e01b, 0x1e021 }, /* Mn */ + { 0x1e023, 0x1e024 }, /* Mn */ + { 0x1e026, 0x1e02a }, /* Mn */ + { 0x1e8d0, 0x1e8d6 }, /* Mn */ + { 0x1e944, 0x1e94a }, /* Mn */ + { 0xe0100, 0xe01ef }, /* Mn */ diff --git a/files/Sources/files/less/configure b/files/Sources/files/less/configure new file mode 100755 index 00000000..53b4634d --- /dev/null +++ b/files/Sources/files/less/configure @@ -0,0 +1,6755 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69 for less 1. +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='less' +PACKAGE_TARNAME='less' +PACKAGE_VERSION='1' +PACKAGE_STRING='less 1' +PACKAGE_BUGREPORT='' +PACKAGE_URL='' + +ac_unique_file="forwback.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='LTLIBOBJS +LIBOBJS +REGEX_O +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +EGREP +GREP +CPP +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_largefile +with_secure +with_no_float +with_regex +with_editor +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures less 1 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/less] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of less 1:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --disable-largefile omit support for large files + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-secure Compile in secure mode + --with-no-float Do not use floating point + --with-regex=LIB select regular expression library (LIB is one of auto,none,gnu,pcre,posix,regcmp,re_comp,regcomp,regcomp-local) [auto] + --with-editor=PROGRAM use PROGRAM as the default editor [vi] + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to the package provider. +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +less configure 1 +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_check_type LINENO TYPE VAR INCLUDES +# ------------------------------------------- +# Tests whether TYPE exists after having included INCLUDES, setting cache +# variable VAR accordingly. +ac_fn_c_check_type () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof ($2)) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof (($2))) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + eval "$3=yes" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_type + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by less $as_me 1, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +ac_config_headers="$ac_config_headers defines.h" + + +# Checks for programs. +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing strerror" >&5 +$as_echo_n "checking for library containing strerror... " >&6; } +if ${ac_cv_search_strerror+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char strerror (); +int +main () +{ +return strerror (); + ; + return 0; +} +_ACEOF +for ac_lib in '' cposix; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_strerror=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_strerror+:} false; then : + break +fi +done +if ${ac_cv_search_strerror+:} false; then : + +else + ac_cv_search_strerror=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_strerror" >&5 +$as_echo "$ac_cv_search_strerror" >&6; } +ac_res=$ac_cv_search_strerror +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +if test $ac_cv_c_compiler_gnu = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC needs -traditional" >&5 +$as_echo_n "checking whether $CC needs -traditional... " >&6; } +if ${ac_cv_prog_gcc_traditional+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_pattern="Autoconf.*'x'" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +Autoconf TIOCGETP +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then : + ac_cv_prog_gcc_traditional=yes +else + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +Autoconf TCGETA +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then : + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_gcc_traditional" >&5 +$as_echo "$ac_cv_prog_gcc_traditional" >&6; } + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + + +# Checks for compilation model. +# Check whether --enable-largefile was given. +if test "${enable_largefile+set}" = set; then : + enableval=$enable_largefile; +fi + +if test "$enable_largefile" != no; then + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 +$as_echo_n "checking for special C compiler options needed for large files... " >&6; } +if ${ac_cv_sys_largefile_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_sys_largefile_CC=no + if test "$GCC" != yes; then + ac_save_CC=$CC + while :; do + # IRIX 6.2 and later do not support large files by default, + # so use the C compiler's -n32 option if that helps. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF + if ac_fn_c_try_compile "$LINENO"; then : + break +fi +rm -f core conftest.err conftest.$ac_objext + CC="$CC -n32" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_largefile_CC=' -n32'; break +fi +rm -f core conftest.err conftest.$ac_objext + break + done + CC=$ac_save_CC + rm -f conftest.$ac_ext + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 +$as_echo "$ac_cv_sys_largefile_CC" >&6; } + if test "$ac_cv_sys_largefile_CC" != no; then + CC=$CC$ac_cv_sys_largefile_CC + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 +$as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } +if ${ac_cv_sys_file_offset_bits+:} false; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_file_offset_bits=no; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _FILE_OFFSET_BITS 64 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_file_offset_bits=64; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_file_offset_bits=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 +$as_echo "$ac_cv_sys_file_offset_bits" >&6; } +case $ac_cv_sys_file_offset_bits in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits +_ACEOF +;; +esac +rm -rf conftest* + if test $ac_cv_sys_file_offset_bits = unknown; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 +$as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } +if ${ac_cv_sys_large_files+:} false; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_large_files=no; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _LARGE_FILES 1 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_large_files=1; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_large_files=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 +$as_echo "$ac_cv_sys_large_files" >&6; } +case $ac_cv_sys_large_files in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _LARGE_FILES $ac_cv_sys_large_files +_ACEOF +;; +esac +rm -rf conftest* + fi + + +fi + + +# Checks for general libraries. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for tgoto in -ltinfo" >&5 +$as_echo_n "checking for tgoto in -ltinfo... " >&6; } +if ${ac_cv_lib_tinfo_tgoto+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ltinfo $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char tgoto (); +int +main () +{ +return tgoto (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_tinfo_tgoto=yes +else + ac_cv_lib_tinfo_tgoto=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_tinfo_tgoto" >&5 +$as_echo "$ac_cv_lib_tinfo_tgoto" >&6; } +if test "x$ac_cv_lib_tinfo_tgoto" = xyes; then : + have_tinfo=yes +else + have_tinfo=no +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for initscr in -lxcurses" >&5 +$as_echo_n "checking for initscr in -lxcurses... " >&6; } +if ${ac_cv_lib_xcurses_initscr+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lxcurses $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char initscr (); +int +main () +{ +return initscr (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_xcurses_initscr=yes +else + ac_cv_lib_xcurses_initscr=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_xcurses_initscr" >&5 +$as_echo "$ac_cv_lib_xcurses_initscr" >&6; } +if test "x$ac_cv_lib_xcurses_initscr" = xyes; then : + have_xcurses=yes +else + have_xcurses=no +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for initscr in -lncursesw" >&5 +$as_echo_n "checking for initscr in -lncursesw... " >&6; } +if ${ac_cv_lib_ncursesw_initscr+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lncursesw $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char initscr (); +int +main () +{ +return initscr (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_ncursesw_initscr=yes +else + ac_cv_lib_ncursesw_initscr=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ncursesw_initscr" >&5 +$as_echo "$ac_cv_lib_ncursesw_initscr" >&6; } +if test "x$ac_cv_lib_ncursesw_initscr" = xyes; then : + have_ncursesw=yes +else + have_ncursesw=no +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for initscr in -lncurses" >&5 +$as_echo_n "checking for initscr in -lncurses... " >&6; } +if ${ac_cv_lib_ncurses_initscr+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lncurses $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char initscr (); +int +main () +{ +return initscr (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_ncurses_initscr=yes +else + ac_cv_lib_ncurses_initscr=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ncurses_initscr" >&5 +$as_echo "$ac_cv_lib_ncurses_initscr" >&6; } +if test "x$ac_cv_lib_ncurses_initscr" = xyes; then : + have_ncurses=yes +else + have_ncurses=no +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for initscr in -lcurses" >&5 +$as_echo_n "checking for initscr in -lcurses... " >&6; } +if ${ac_cv_lib_curses_initscr+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcurses $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char initscr (); +int +main () +{ +return initscr (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_curses_initscr=yes +else + ac_cv_lib_curses_initscr=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_initscr" >&5 +$as_echo "$ac_cv_lib_curses_initscr" >&6; } +if test "x$ac_cv_lib_curses_initscr" = xyes; then : + have_curses=yes +else + have_curses=no +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for tgetent in -ltermcap" >&5 +$as_echo_n "checking for tgetent in -ltermcap... " >&6; } +if ${ac_cv_lib_termcap_tgetent+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ltermcap $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char tgetent (); +int +main () +{ +return tgetent (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_termcap_tgetent=yes +else + ac_cv_lib_termcap_tgetent=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_termcap_tgetent" >&5 +$as_echo "$ac_cv_lib_termcap_tgetent" >&6; } +if test "x$ac_cv_lib_termcap_tgetent" = xyes; then : + have_termcap=yes +else + have_termcap=no +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for tgetent in -ltermlib" >&5 +$as_echo_n "checking for tgetent in -ltermlib... " >&6; } +if ${ac_cv_lib_termlib_tgetent+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ltermlib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char tgetent (); +int +main () +{ +return tgetent (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_termlib_tgetent=yes +else + ac_cv_lib_termlib_tgetent=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_termlib_tgetent" >&5 +$as_echo "$ac_cv_lib_termlib_tgetent" >&6; } +if test "x$ac_cv_lib_termlib_tgetent" = xyes; then : + have_termlib=yes +else + have_termlib=no +fi + +# Regular expressions (regcmp) are in -lgen on Solaris 2, (but in libc +# at least on Solaris 10 (2.10)) and in -lintl on SCO Unix. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing regcmp" >&5 +$as_echo_n "checking for library containing regcmp... " >&6; } +if ${ac_cv_search_regcmp+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char regcmp (); +int +main () +{ +return regcmp (); + ; + return 0; +} +_ACEOF +for ac_lib in '' gen intl PW; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_regcmp=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_regcmp+:} false; then : + break +fi +done +if ${ac_cv_search_regcmp+:} false; then : + +else + ac_cv_search_regcmp=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_regcmp" >&5 +$as_echo "$ac_cv_search_regcmp" >&6; } +ac_res=$ac_cv_search_regcmp +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + + +# Checks for terminal libraries +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working terminal libraries" >&5 +$as_echo_n "checking for working terminal libraries... " >&6; } +TERMLIBS= + +# Check for systems where curses is broken. +curses_broken=0 +if test x`uname -s` = "xHP-UX" >/dev/null 2>&1; then +if test x`uname -r` = "xB.11.00" >/dev/null 2>&1; then + curses_broken=1 +fi +if test x`uname -r` = "xB.11.11" >/dev/null 2>&1; then + curses_broken=1 +fi +fi + +if test $curses_broken = 0; then + +# -- Try tinfo. +if test "x$TERMLIBS" = x; then + if test $have_tinfo = yes; then + TERMLIBS="-ltinfo" + SAVE_LIBS=$LIBS + LIBS="$LIBS $TERMLIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +tgetent(0,0); tgetflag(0); tgetnum(0); tgetstr(0,0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + termok=yes +else + termok=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS=$SAVE_LIBS + if test $termok = no; then TERMLIBS=""; fi + fi +fi + +# -- Try xcurses. +if test "x$TERMLIBS" = x; then + if test $have_xcurses = yes; then + TERMLIBS="-lxcurses" + SAVE_LIBS=$LIBS + LIBS="$LIBS $TERMLIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +tgetent(0,0); tgetflag(0); tgetnum(0); tgetstr(0,0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + termok=yes +else + termok=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS=$SAVE_LIBS + if test $termok = no; then TERMLIBS=""; fi + fi +fi + +# -- Try ncursesw. +if test "x$TERMLIBS" = x; then + if test $have_ncursesw = yes; then + TERMLIBS="-lncursesw" + SAVE_LIBS=$LIBS + LIBS="$LIBS $TERMLIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +tgetent(0,0); tgetflag(0); tgetnum(0); tgetstr(0,0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + termok=yes +else + termok=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS=$SAVE_LIBS + if test $termok = no; then TERMLIBS=""; fi + fi +fi + +# -- Try ncurses. +if test "x$TERMLIBS" = x; then + if test $have_ncurses = yes; then + TERMLIBS="-lncurses" + SAVE_LIBS=$LIBS + LIBS="$LIBS $TERMLIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +tgetent(0,0); tgetflag(0); tgetnum(0); tgetstr(0,0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + termok=yes +else + termok=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS=$SAVE_LIBS + if test $termok = no; then TERMLIBS=""; fi + fi +fi + +# -- Try curses. +if test "x$TERMLIBS" = x; then + if test $have_curses = yes; then + TERMLIBS="-lcurses" + SAVE_LIBS=$LIBS + LIBS="$LIBS $TERMLIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +tgetent(0,0); tgetflag(0); tgetnum(0); tgetstr(0,0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + termok=yes +else + termok=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS=$SAVE_LIBS + if test $termok = no; then TERMLIBS=""; fi + fi +fi + +# -- Try curses & termcap. +if test "x$TERMLIBS" = x; then + if test $have_curses = yes; then + if test $have_termcap = yes; then + TERMLIBS="-lcurses -ltermcap" + SAVE_LIBS=$LIBS + LIBS="$LIBS $TERMLIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +tgetent(0,0); tgetflag(0); tgetnum(0); tgetstr(0,0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + termok=yes +else + termok=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS=$SAVE_LIBS + if test $termok = no; then TERMLIBS=""; fi + fi + fi +fi +fi + +# -- Try termcap. +if test "x$TERMLIBS" = x; then + if test $have_termcap = yes; then + TERMLIBS="-ltermcap" + SAVE_LIBS=$LIBS + LIBS="$LIBS $TERMLIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +tgetent(0,0); tgetflag(0); tgetnum(0); tgetstr(0,0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + termok=yes +else + termok=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS=$SAVE_LIBS + if test $termok = no; then TERMLIBS=""; fi + fi +fi + +# -- Try termlib. +if test "x$TERMLIBS" = x; then + if test $have_termlib = yes; then + TERMLIBS="-lcurses -ltermlib" + SAVE_LIBS=$LIBS + LIBS="$LIBS $TERMLIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +tgetent(0,0); tgetflag(0); tgetnum(0); tgetstr(0,0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + termok=yes +else + termok=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS=$SAVE_LIBS + if test $termok = no; then TERMLIBS=""; fi + fi +fi + +if test "x$TERMLIBS" = x; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Cannot find terminal libraries - configure failed" >&5 +$as_echo "Cannot find terminal libraries - configure failed" >&6; } + exit 1 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: using $TERMLIBS" >&5 +$as_echo "using $TERMLIBS" >&6; } +LIBS="$LIBS $TERMLIBS" + +# Checks for header files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_header in ctype.h errno.h fcntl.h limits.h stdio.h stdlib.h string.h termcap.h termio.h termios.h time.h unistd.h values.h sys/ioctl.h sys/stream.h wctype.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +# Checks for typedefs, structures, and compiler characteristics. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stat file-mode macros are broken" >&5 +$as_echo_n "checking whether stat file-mode macros are broken... " >&6; } +if ${ac_cv_header_stat_broken+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include + +#if defined S_ISBLK && defined S_IFDIR +extern char c1[S_ISBLK (S_IFDIR) ? -1 : 1]; +#endif + +#if defined S_ISBLK && defined S_IFCHR +extern char c2[S_ISBLK (S_IFCHR) ? -1 : 1]; +#endif + +#if defined S_ISLNK && defined S_IFREG +extern char c3[S_ISLNK (S_IFREG) ? -1 : 1]; +#endif + +#if defined S_ISSOCK && defined S_IFREG +extern char c4[S_ISSOCK (S_IFREG) ? -1 : 1]; +#endif + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stat_broken=no +else + ac_cv_header_stat_broken=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stat_broken" >&5 +$as_echo "$ac_cv_header_stat_broken" >&6; } +if test $ac_cv_header_stat_broken = yes; then + +$as_echo "#define STAT_MACROS_BROKEN 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 +$as_echo_n "checking for an ANSI C-conforming const... " >&6; } +if ${ac_cv_c_const+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + +#ifndef __cplusplus + /* Ultrix mips cc rejects this sort of thing. */ + typedef int charset[2]; + const charset cs = { 0, 0 }; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *pcpcc; + char **ppc; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + pcpcc = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++pcpcc; + ppc = (char**) pcpcc; + pcpcc = (char const *const *) ppc; + { /* SCO 3.2v4 cc rejects this sort of thing. */ + char tx; + char *t = &tx; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + if (s) return 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this sort of thing, saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; } bx; + struct s *b = &bx; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + if (!foo) return 0; + } + return !cs[0] && !zero.x; +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_const=yes +else + ac_cv_c_const=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 +$as_echo "$ac_cv_c_const" >&6; } +if test $ac_cv_c_const = no; then + +$as_echo "#define const /**/" >>confdefs.h + +fi + +ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default" +if test "x$ac_cv_type_off_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define off_t long int +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" +if test "x$ac_cv_type_size_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5 +$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; } +if ${ac_cv_header_time+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_time=yes +else + ac_cv_header_time=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5 +$as_echo "$ac_cv_header_time" >&6; } +if test $ac_cv_header_time = yes; then + +$as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h + +fi + + +# Autoheader templates for symbols defined later by AC_DEFINE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# Checks for identifiers. +ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default" +if test "x$ac_cv_type_off_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define off_t long int +_ACEOF + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for void" >&5 +$as_echo_n "checking for void... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +void *foo = 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_VOID 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for const" >&5 +$as_echo_n "checking for const... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +const int foo = 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_CONST 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for time_t" >&5 +$as_echo_n "checking for time_t... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +time_t t = 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_TIME_T 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for st_ino in struct stat" >&5 +$as_echo_n "checking for st_ino in struct stat... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +int +main () +{ +struct stat s; dev_t dev = s.st_dev; ino_t ino = s.st_ino; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_STAT_INO 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +# Checks for library functions. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of signal handlers" >&5 +$as_echo_n "checking return type of signal handlers... " >&6; } +if ${ac_cv_type_signal+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include + +int +main () +{ +return *(signal (0, 0)) (0) == 1; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_type_signal=int +else + ac_cv_type_signal=void +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_signal" >&5 +$as_echo "$ac_cv_type_signal" >&6; } + +cat >>confdefs.h <<_ACEOF +#define RETSIGTYPE $ac_cv_type_signal +_ACEOF + + +for ac_func in fsync popen _setjmp sigprocmask sigsetmask snprintf stat system fchmod +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +# AC_CHECK_FUNCS may not work for inline functions, so test these separately. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for memcpy" >&5 +$as_echo_n "checking for memcpy... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#if HAVE_STRING_H +#include +#endif +int +main () +{ +memcpy(0,0,0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_MEMCPY 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for strchr" >&5 +$as_echo_n "checking for strchr... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#if HAVE_STRING_H +#include +#endif +int +main () +{ +strchr("x",'x'); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_STRCHR 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for strstr" >&5 +$as_echo_n "checking for strstr... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#if HAVE_STRING_H +#include +#endif +int +main () +{ +strstr("x","x"); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_STRSTR 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +# Some systems have termios.h but not the corresponding functions. +ac_fn_c_check_func "$LINENO" "tcgetattr" "ac_cv_func_tcgetattr" +if test "x$ac_cv_func_tcgetattr" = xyes; then : + $as_echo "#define HAVE_TERMIOS_FUNCS 1" >>confdefs.h + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fileno" >&5 +$as_echo_n "checking for fileno... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#if HAVE_STDIO_H +#include +#endif +int +main () +{ +static int x; x = fileno(stdin); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_FILENO 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for strerror" >&5 +$as_echo_n "checking for strerror... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#if HAVE_STDIO_H +#include +#endif +#if HAVE_STRING_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +int +main () +{ +static char *x; x = strerror(0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_STRERROR 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys_errlist" >&5 +$as_echo_n "checking for sys_errlist... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +extern char *sys_errlist[]; static char **x; x = sys_errlist; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_SYS_ERRLIST 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +ac_fn_c_check_type "$LINENO" "sigset_t" "ac_cv_type_sigset_t" "#include +" +if test "x$ac_cv_type_sigset_t" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_SIGSET_T 1 +_ACEOF + + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sigemptyset" >&5 +$as_echo_n "checking for sigemptyset... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include + +int +main () +{ +sigset_t s; sigemptyset(&s); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_SIGEMPTYSET 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +have_errno=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for errno" >&5 +$as_echo_n "checking for errno... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#if HAVE_ERRNO_H +#include +#endif +int +main () +{ +static int x; x = errno; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes - in errno.h" >&5 +$as_echo "yes - in errno.h" >&6; }; $as_echo "#define HAVE_ERRNO 1" >>confdefs.h + have_errno=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test $have_errno = no; then +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#if HAVE_ERRNO_H +#include +#endif +int +main () +{ +extern int errno; static int x; x = errno; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes - must define" >&5 +$as_echo "yes - must define" >&6; }; $as_echo "#define HAVE_ERRNO 1" >>confdefs.h + $as_echo "#define MUST_DEFINE_ERRNO 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for locale" >&5 +$as_echo_n "checking for locale... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +int +main () +{ +setlocale(LC_CTYPE,""); isprint(0); iscntrl(0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_LOCALE 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ctype functions" >&5 +$as_echo_n "checking for ctype functions... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#if HAVE_CTYPE_H +#include +#endif +int +main () +{ +static int x; x = isupper(x); x = tolower(x); x = toupper(x); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_UPPER_LOWER 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for wctype functions" >&5 +$as_echo_n "checking for wctype functions... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +iswlower(0); iswupper(0); towlower(0); towupper(0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_WCTYPE 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +# Checks for external variable ospeed in the termcap library. +have_ospeed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking termcap for ospeed" >&5 +$as_echo_n "checking termcap for ospeed... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#if HAVE_TERMIOS_H +#include +#endif +#if HAVE_TERMCAP_H +#include +#endif +int +main () +{ +ospeed = 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes - in termcap.h" >&5 +$as_echo "yes - in termcap.h" >&6; }; $as_echo "#define HAVE_OSPEED 1" >>confdefs.h + have_ospeed=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test $have_ospeed = no; then +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +extern short ospeed; ospeed = 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes - must define" >&5 +$as_echo "yes - must define" >&6; }; $as_echo "#define HAVE_OSPEED 1" >>confdefs.h + $as_echo "#define MUST_DEFINE_OSPEED 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi + +# Compile in secure mode? + +# Check whether --with-secure was given. +if test "${with_secure+set}" = set; then : + withval=$with_secure; $as_echo "#define SECURE_COMPILE 1" >>confdefs.h + +else + $as_echo "#define SECURE_COMPILE 0" >>confdefs.h + +fi + + +# Should we use floating point? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for floating point" >&5 +$as_echo_n "checking for floating point... " >&6; } + +# Check whether --with-no-float was given. +if test "${with_no_float+set}" = set; then : + withval=$with_no_float; WANT_NO_FLOAT=1 +else + WANT_NO_FLOAT=0 +fi + +if test $WANT_NO_FLOAT = 0; then + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +double f1 = 12.5; double f2 = f1*f1/2.5; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_FLOAT 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: disabled by user" >&5 +$as_echo "disabled by user" >&6; } +fi + +# Checks for regular expression functions. +have_regex=no +have_posix_regex=unknown +supported_regex="" + +# Select a regular expression library. +WANT_REGEX=auto + +# Check whether --with-regex was given. +if test "${with_regex+set}" = set; then : + withval=$with_regex; WANT_REGEX="$withval" +fi + + +if test $have_regex = no; then +if test $WANT_REGEX = auto -o $WANT_REGEX = posix; then +# Some versions of Solaris have a regcomp() function, but it doesn't work! +# So we run a test program. If we're cross-compiling, do it the old way. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for POSIX regcomp" >&5 +$as_echo_n "checking for POSIX regcomp... " >&6; } +if test "$cross_compiling" = yes; then : + have_posix_regex=unknown +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +main() { regex_t r; regmatch_t rm; char *text = "xabcy"; +if (regcomp(&r, "abc", 0)) exit(1); +if (regexec(&r, text, 1, &rm, 0)) exit(1); +#ifndef __WATCOMC__ +if (rm.rm_so != 1) exit(1); /* check for correct offset */ +#else +if (rm.rm_sp != text + 1) exit(1); /* check for correct offset */ +#endif +exit(0); } +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + have_posix_regex=yes +else + have_posix_regex=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +if test $have_posix_regex = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + $as_echo "#define HAVE_POSIX_REGCOMP 1" >>confdefs.h + supported_regex="$supported_regex posix" + have_regex=yes +elif test $have_posix_regex = unknown; then + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +int +main () +{ +regex_t *r; regfree(r); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + $as_echo "#define HAVE_POSIX_REGCOMP 1" >>confdefs.h + have_regex=yes; supported_regex="$supported_regex posix" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +fi +fi + +if test $have_regex = no; then +if test $WANT_REGEX = auto -o $WANT_REGEX = pcre; then +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcre_compile in -lpcre" >&5 +$as_echo_n "checking for pcre_compile in -lpcre... " >&6; } +if ${ac_cv_lib_pcre_pcre_compile+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpcre $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pcre_compile (); +int +main () +{ +return pcre_compile (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_pcre_pcre_compile=yes +else + ac_cv_lib_pcre_pcre_compile=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pcre_pcre_compile" >&5 +$as_echo "$ac_cv_lib_pcre_pcre_compile" >&6; } +if test "x$ac_cv_lib_pcre_pcre_compile" = xyes; then : + $as_echo "#define HAVE_PCRE 1" >>confdefs.h + LIBS="$LIBS -lpcre" have_regex=yes; supported_regex="$supported_regex pcre" +fi + +fi +fi + +if test $have_regex = no; then +if test $WANT_REGEX = auto -o $WANT_REGEX = gnu; then +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for re_compile_pattern in -lc" >&5 +$as_echo_n "checking for re_compile_pattern in -lc... " >&6; } +if ${ac_cv_lib_c_re_compile_pattern+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lc $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char re_compile_pattern (); +int +main () +{ +return re_compile_pattern (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_c_re_compile_pattern=yes +else + ac_cv_lib_c_re_compile_pattern=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_c_re_compile_pattern" >&5 +$as_echo "$ac_cv_lib_c_re_compile_pattern" >&6; } +if test "x$ac_cv_lib_c_re_compile_pattern" = xyes; then : + $as_echo "#define HAVE_GNU_REGEX 1" >>confdefs.h + have_regex=yes; supported_regex="$supported_regex gnu" +fi + +fi +fi + +if test $have_regex = no; then +if test $WANT_REGEX = auto -o $WANT_REGEX = regcmp; then +ac_fn_c_check_func "$LINENO" "regcmp" "ac_cv_func_regcmp" +if test "x$ac_cv_func_regcmp" = xyes; then : + $as_echo "#define HAVE_REGCMP 1" >>confdefs.h + have_regex=yes; supported_regex="$supported_regex regcmp" +fi + +fi +fi + +if test $have_regex = no; then +if test $WANT_REGEX = auto -o $WANT_REGEX = regcomp; then +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for V8 regcomp" >&5 +$as_echo_n "checking for V8 regcomp... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include "regexp.h" +int +main () +{ +regcomp(""); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_V8_REGCOMP 1" >>confdefs.h + have_regex=yes; supported_regex="$supported_regex regcomp" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +fi + +if test $have_regex = no && test -f ${srcdir}/regexp.c; then +if test $WANT_REGEX = auto -o $WANT_REGEX = regcomp-local; then +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: using V8 regcomp -- local source" >&5 +$as_echo "using V8 regcomp -- local source" >&6; }; $as_echo "#define HAVE_V8_REGCOMP 1" >>confdefs.h + +supported_regex="$supported_regex regcomp-local" +$as_echo "#define HAVE_REGEXEC2 1" >>confdefs.h + REGEX_O='regexp.$(O)' have_regex=yes +fi +fi + +if test $have_regex = no; then +if test $WANT_REGEX = auto -o $WANT_REGEX = re_comp; then +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for re_comp" >&5 +$as_echo_n "checking for re_comp... " >&6; } +ac_fn_c_check_func "$LINENO" "re_comp" "ac_cv_func_re_comp" +if test "x$ac_cv_func_re_comp" = xyes; then : + $as_echo "#define HAVE_RE_COMP 1" >>confdefs.h + have_regex=yes; supported_regex="$supported_regex re_comp" +fi + +fi +fi + +if test $have_regex = no; then +if test $WANT_REGEX = auto -o $WANT_REGEX = none; then +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: using no regex" >&5 +$as_echo "using no regex" >&6; } +else +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cannot find regular expression library" >&5 +$as_echo "$as_me: WARNING: cannot find regular expression library" >&2;} +fi +$as_echo "#define NO_REGEX 1" >>confdefs.h + supported_regex="$supported_regex none" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: regular expression library: $supported_regex" >&5 +$as_echo "regular expression library: $supported_regex" >&6; } + + +# Check whether --with-editor was given. +if test "${with_editor+set}" = set; then : + withval=$with_editor; cat >>confdefs.h <<_ACEOF +#define EDIT_PGM "$withval" +_ACEOF + +else + $as_echo "#define EDIT_PGM \"vi\"" >>confdefs.h + +fi + + + + +ac_config_files="$ac_config_files Makefile" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by less $as_me 1, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to the package provider." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +less config.status 1 +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "defines.h") CONFIG_HEADERS="$CONFIG_HEADERS defines.h" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi + ;; + + + esac + +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/files/Sources/files/less/configure.ac b/files/Sources/files/less/configure.ac new file mode 100755 index 00000000..42bdd6b2 --- /dev/null +++ b/files/Sources/files/less/configure.ac @@ -0,0 +1,698 @@ +# Process this file with autoconf to produce a configure script. + +# Copyright (C) 1984-2011 Mark Nudelman +# +# You may distribute under the terms of either the GNU General Public +# License or the Less License, as specified in the README file. +# +# For more information about less, or for information on how to +# contact the author, see the README file. + +# Autoconf initialization. +AC_INIT(less, 1) +AC_CONFIG_SRCDIR([forwback.c]) +AC_CONFIG_HEADER([defines.h]) + +# Checks for programs. +AC_PROG_CC +AC_ISC_POSIX +AC_PROG_GCC_TRADITIONAL +AC_PROG_INSTALL + +# Checks for compilation model. +AC_SYS_LARGEFILE + +# Checks for general libraries. +AC_CHECK_LIB(tinfo, tgoto, [have_tinfo=yes], [have_tinfo=no]) +AC_CHECK_LIB(xcurses, initscr, [have_xcurses=yes], [have_xcurses=no]) +AC_CHECK_LIB(ncursesw, initscr, [have_ncursesw=yes], [have_ncursesw=no]) +AC_CHECK_LIB(ncurses, initscr, [have_ncurses=yes], [have_ncurses=no]) +AC_CHECK_LIB(curses, initscr, [have_curses=yes], [have_curses=no]) +AC_CHECK_LIB(termcap, tgetent, [have_termcap=yes], [have_termcap=no]) +AC_CHECK_LIB(termlib, tgetent, [have_termlib=yes], [have_termlib=no]) +# Regular expressions (regcmp) are in -lgen on Solaris 2, (but in libc +# at least on Solaris 10 (2.10)) and in -lintl on SCO Unix. +AC_SEARCH_LIBS([regcmp], [gen intl PW]) + +# Checks for terminal libraries +AC_MSG_CHECKING([for working terminal libraries]) +TERMLIBS= + +# Check for systems where curses is broken. +curses_broken=0 +if test x`uname -s` = "xHP-UX" >/dev/null 2>&1; then +if test x`uname -r` = "xB.11.00" >/dev/null 2>&1; then + curses_broken=1 +fi +if test x`uname -r` = "xB.11.11" >/dev/null 2>&1; then + curses_broken=1 +fi +fi + +if test $curses_broken = 0; then + +# -- Try tinfo. +if test "x$TERMLIBS" = x; then + if test $have_tinfo = yes; then + TERMLIBS="-ltinfo" + SAVE_LIBS=$LIBS + LIBS="$LIBS $TERMLIBS" + AC_TRY_LINK(, [tgetent(0,0); tgetflag(0); tgetnum(0); tgetstr(0,0);], + [termok=yes], [termok=no]) + LIBS=$SAVE_LIBS + if test $termok = no; then TERMLIBS=""; fi + fi +fi + +# -- Try xcurses. +if test "x$TERMLIBS" = x; then + if test $have_xcurses = yes; then + TERMLIBS="-lxcurses" + SAVE_LIBS=$LIBS + LIBS="$LIBS $TERMLIBS" + AC_TRY_LINK(, [tgetent(0,0); tgetflag(0); tgetnum(0); tgetstr(0,0);], + [termok=yes], [termok=no]) + LIBS=$SAVE_LIBS + if test $termok = no; then TERMLIBS=""; fi + fi +fi + +# -- Try ncursesw. +if test "x$TERMLIBS" = x; then + if test $have_ncursesw = yes; then + TERMLIBS="-lncursesw" + SAVE_LIBS=$LIBS + LIBS="$LIBS $TERMLIBS" + AC_TRY_LINK(, [tgetent(0,0); tgetflag(0); tgetnum(0); tgetstr(0,0);], + [termok=yes], [termok=no]) + LIBS=$SAVE_LIBS + if test $termok = no; then TERMLIBS=""; fi + fi +fi + +# -- Try ncurses. +if test "x$TERMLIBS" = x; then + if test $have_ncurses = yes; then + TERMLIBS="-lncurses" + SAVE_LIBS=$LIBS + LIBS="$LIBS $TERMLIBS" + AC_TRY_LINK(, [tgetent(0,0); tgetflag(0); tgetnum(0); tgetstr(0,0);], + [termok=yes], [termok=no]) + LIBS=$SAVE_LIBS + if test $termok = no; then TERMLIBS=""; fi + fi +fi + +# -- Try curses. +if test "x$TERMLIBS" = x; then + if test $have_curses = yes; then + TERMLIBS="-lcurses" + SAVE_LIBS=$LIBS + LIBS="$LIBS $TERMLIBS" + AC_TRY_LINK(, [tgetent(0,0); tgetflag(0); tgetnum(0); tgetstr(0,0);], + [termok=yes], [termok=no]) + LIBS=$SAVE_LIBS + if test $termok = no; then TERMLIBS=""; fi + fi +fi + +# -- Try curses & termcap. +if test "x$TERMLIBS" = x; then + if test $have_curses = yes; then + if test $have_termcap = yes; then + TERMLIBS="-lcurses -ltermcap" + SAVE_LIBS=$LIBS + LIBS="$LIBS $TERMLIBS" + AC_TRY_LINK(, [tgetent(0,0); tgetflag(0); tgetnum(0); tgetstr(0,0);], + [termok=yes], [termok=no]) + LIBS=$SAVE_LIBS + if test $termok = no; then TERMLIBS=""; fi + fi + fi +fi +fi + +# -- Try termcap. +if test "x$TERMLIBS" = x; then + if test $have_termcap = yes; then + TERMLIBS="-ltermcap" + SAVE_LIBS=$LIBS + LIBS="$LIBS $TERMLIBS" + AC_TRY_LINK(, [tgetent(0,0); tgetflag(0); tgetnum(0); tgetstr(0,0);], + [termok=yes], [termok=no]) + LIBS=$SAVE_LIBS + if test $termok = no; then TERMLIBS=""; fi + fi +fi + +# -- Try termlib. +if test "x$TERMLIBS" = x; then + if test $have_termlib = yes; then + TERMLIBS="-lcurses -ltermlib" + SAVE_LIBS=$LIBS + LIBS="$LIBS $TERMLIBS" + AC_TRY_LINK(, [tgetent(0,0); tgetflag(0); tgetnum(0); tgetstr(0,0);], + [termok=yes], [termok=no]) + LIBS=$SAVE_LIBS + if test $termok = no; then TERMLIBS=""; fi + fi +fi + +if test "x$TERMLIBS" = x; then + AC_MSG_RESULT(Cannot find terminal libraries - configure failed) + exit 1 +fi +AC_MSG_RESULT(using $TERMLIBS) +LIBS="$LIBS $TERMLIBS" + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([ctype.h errno.h fcntl.h limits.h stdio.h stdlib.h string.h termcap.h termio.h termios.h time.h unistd.h values.h sys/ioctl.h sys/stream.h wctype.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_HEADER_STAT +AC_C_CONST +AC_TYPE_OFF_T +AC_TYPE_SIZE_T +AC_HEADER_TIME + +# Autoheader templates for symbols defined later by AC_DEFINE. +AH_TEMPLATE([HAVE_GNU_REGEX], + [GNU regex library]) +AH_TEMPLATE([HAVE_POSIX_REGCOMP], + [POSIX regcomp() and regex.h]) +AH_TEMPLATE([HAVE_PCRE], + [PCRE (Perl-compatible regular expression) library]) +AH_TEMPLATE([HAVE_RE_COMP], + [BSD re_comp()]) +AH_TEMPLATE([HAVE_REGCMP], + [System V regcmp()]) +AH_TEMPLATE([HAVE_V8_REGCOMP], + [Henry Spencer V8 regcomp() and regexp.h]) +AH_TEMPLATE([NO_REGEX], + [pattern matching is supported, but without metacharacters.]) +AH_TEMPLATE([HAVE_REGEXEC2], + []) +AH_TEMPLATE([HAVE_VOID], + [Define HAVE_VOID if your compiler supports the "void" type.]) +AH_TEMPLATE([HAVE_FLOAT], + [Define HAVE_FLOAT if your compiler supports the "double" type.]) +AH_TEMPLATE([HAVE_CONST], + [Define HAVE_CONST if your compiler supports the "const" modifier.]) +AH_TEMPLATE([HAVE_STAT_INO], + [Define HAVE_STAT_INO if your struct stat has st_ino and st_dev.]) +AH_TEMPLATE([HAVE_TIME_T], + [Define HAVE_TIME_T if your system supports the "time_t" type.]) +AH_TEMPLATE([HAVE_STRERROR], + [Define HAVE_STRERROR if you have the strerror() function.]) +AH_TEMPLATE([HAVE_FILENO], + [Define HAVE_FILENO if you have the fileno() macro.]) +AH_TEMPLATE([HAVE_ERRNO], + [Define HAVE_ERRNO if you have the errno variable.]) +AH_TEMPLATE([MUST_DEFINE_ERRNO], + [Define MUST_DEFINE_ERRNO if you have errno but it is not define in errno.h.]) +AH_TEMPLATE([HAVE_SYS_ERRLIST], + [Define HAVE_SYS_ERRLIST if you have the sys_errlist[] variable.]) +AH_TEMPLATE([HAVE_OSPEED], + [Define HAVE_OSPEED if your termcap library has the ospeed variable.]) +AH_TEMPLATE([MUST_DEFINE_OSPEED], + [Define MUST_DEFINE_OSPEED if you have ospeed but it is not defined in termcap.h.]) +AH_TEMPLATE([HAVE_LOCALE], + [Define HAVE_LOCALE if you have locale.h and setlocale.]) +AH_TEMPLATE([HAVE_TERMIOS_FUNCS], + [Define HAVE_TERMIOS_FUNCS if you have tcgetattr/tcsetattr.]) +AH_TEMPLATE([HAVE_UPPER_LOWER], + [Define HAVE_UPPER_LOWER if you have isupper, islower, toupper, tolower.]) +AH_TEMPLATE([HAVE_WCTYPE], + [Define HAVE_WCTYPE if you have iswupper, iswlower, towupper, towlower.]) +AH_TEMPLATE([HAVE_SIGSET_T], + [Define HAVE_SIGSET_T you have the sigset_t type.]) +AH_TEMPLATE([HAVE_SIGEMPTYSET], + [Define HAVE_SIGEMPTYSET if you have the sigemptyset macro.]) +AH_TEMPLATE([EDIT_PGM], + [Define EDIT_PGM to your editor.]) +AH_TEMPLATE([SECURE_COMPILE], + [Define SECURE_COMPILE=1 to build a secure version of less.]) + +# Checks for identifiers. +AC_TYPE_OFF_T +AC_MSG_CHECKING(for void) +AC_TRY_COMPILE(, [void *foo = 0;], + [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_VOID)], [AC_MSG_RESULT(no)]) +AC_MSG_CHECKING(for const) +AC_TRY_COMPILE(, [const int foo = 0;], + [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_CONST)], [AC_MSG_RESULT(no)]) +AC_MSG_CHECKING(for time_t) +AC_TRY_COMPILE([#include ], [time_t t = 0;], + [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_TIME_T)], [AC_MSG_RESULT(no)]) +AC_MSG_CHECKING(for st_ino in struct stat) +AC_TRY_COMPILE([#include +#include ], + [struct stat s; dev_t dev = s.st_dev; ino_t ino = s.st_ino;], + [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_STAT_INO)], [AC_MSG_RESULT(no)]) + +# Checks for library functions. +AC_TYPE_SIGNAL +AC_CHECK_FUNCS([fsync popen _setjmp sigprocmask sigsetmask snprintf stat system fchmod]) + +# AC_CHECK_FUNCS may not work for inline functions, so test these separately. +AC_MSG_CHECKING(for memcpy) +AC_TRY_LINK([ +#if HAVE_STRING_H +#include +#endif], [memcpy(0,0,0);], + [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_MEMCPY)], [AC_MSG_RESULT(no)]) + +AC_MSG_CHECKING(for strchr) +AC_TRY_LINK([ +#if HAVE_STRING_H +#include +#endif], [strchr("x",'x');], + [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_STRCHR)], [AC_MSG_RESULT(no)]) + +AC_MSG_CHECKING(for strstr) +AC_TRY_LINK([ +#if HAVE_STRING_H +#include +#endif], [strstr("x","x");], + [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_STRSTR)], [AC_MSG_RESULT(no)]) + +# Some systems have termios.h but not the corresponding functions. +AC_CHECK_FUNC(tcgetattr, AC_DEFINE(HAVE_TERMIOS_FUNCS)) + +AC_MSG_CHECKING(for fileno) +AC_TRY_LINK([ +#if HAVE_STDIO_H +#include +#endif], [static int x; x = fileno(stdin);], + [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_FILENO)], [AC_MSG_RESULT(no)]) + +AC_MSG_CHECKING(for strerror) +AC_TRY_LINK([ +#if HAVE_STDIO_H +#include +#endif +#if HAVE_STRING_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif], [static char *x; x = strerror(0);], + [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_STRERROR)], [AC_MSG_RESULT(no)]) + +AC_MSG_CHECKING(for sys_errlist) +AC_TRY_LINK(, [extern char *sys_errlist[]; static char **x; x = sys_errlist;], + [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYS_ERRLIST)], [AC_MSG_RESULT(no)]) + +AC_CHECK_TYPES([sigset_t],,,[#include ]) + +AC_MSG_CHECKING(for sigemptyset) +AC_TRY_LINK([ +#include +], [sigset_t s; sigemptyset(&s);], + [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SIGEMPTYSET)], [AC_MSG_RESULT(no)]) + +have_errno=no +AC_MSG_CHECKING(for errno) +AC_TRY_LINK([ +#if HAVE_ERRNO_H +#include +#endif], [static int x; x = errno;], + [AC_MSG_RESULT(yes - in errno.h); AC_DEFINE(HAVE_ERRNO) have_errno=yes]) +if test $have_errno = no; then +AC_TRY_LINK([ +#if HAVE_ERRNO_H +#include +#endif], [extern int errno; static int x; x = errno;], + [AC_MSG_RESULT(yes - must define); AC_DEFINE(HAVE_ERRNO) AC_DEFINE(MUST_DEFINE_ERRNO)], + [AC_MSG_RESULT(no)]) +fi + +AC_MSG_CHECKING(for locale) +AC_TRY_LINK([#include +#include +#include ], [setlocale(LC_CTYPE,""); isprint(0); iscntrl(0);], + [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_LOCALE)], [AC_MSG_RESULT(no)]) + +AC_MSG_CHECKING(for ctype functions) +AC_TRY_LINK([ +#if HAVE_CTYPE_H +#include +#endif], [static int x; x = isupper(x); x = tolower(x); x = toupper(x);], + [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_UPPER_LOWER)], [AC_MSG_RESULT(no)]) + +AC_MSG_CHECKING(for wctype functions) +AC_TRY_LINK([#include ], [iswlower(0); iswupper(0); towlower(0); towupper(0);], + [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_WCTYPE)], [AC_MSG_RESULT(no)]) + +# Checks for external variable ospeed in the termcap library. +have_ospeed=no +AC_MSG_CHECKING(termcap for ospeed) +AC_TRY_LINK([ +#include +#if HAVE_TERMIOS_H +#include +#endif +#if HAVE_TERMCAP_H +#include +#endif], [ospeed = 0;], +[AC_MSG_RESULT(yes - in termcap.h); AC_DEFINE(HAVE_OSPEED) have_ospeed=yes]) +if test $have_ospeed = no; then +AC_TRY_LINK(, [extern short ospeed; ospeed = 0;], + [AC_MSG_RESULT(yes - must define); AC_DEFINE(HAVE_OSPEED) AC_DEFINE(MUST_DEFINE_OSPEED)], + [AC_MSG_RESULT(no)]) +fi + +# Compile in secure mode? +AC_ARG_WITH(secure, + [ --with-secure Compile in secure mode], + AC_DEFINE(SECURE_COMPILE, 1), AC_DEFINE(SECURE_COMPILE, 0)) + +# Should we use floating point? +AC_MSG_CHECKING(for floating point) +AC_ARG_WITH(no-float, + [ --with-no-float Do not use floating point], + WANT_NO_FLOAT=1, WANT_NO_FLOAT=0) +if test $WANT_NO_FLOAT = 0; then + AC_TRY_LINK(, [double f1 = 12.5; double f2 = f1*f1/2.5;], + [AC_MSG_RESULT(yes); AC_DEFINE(HAVE_FLOAT)], [AC_MSG_RESULT(no)]) +else + AC_MSG_RESULT(disabled by user) +fi + +# Checks for regular expression functions. +have_regex=no +have_posix_regex=unknown +supported_regex="" + +# Select a regular expression library. +WANT_REGEX=auto +AC_ARG_WITH(regex, + [ --with-regex=LIB select regular expression library (LIB is one of auto,none,gnu,pcre,posix,regcmp,re_comp,regcomp,regcomp-local) [[auto]]], + WANT_REGEX="$withval") + +if test $have_regex = no; then +if test $WANT_REGEX = auto -o $WANT_REGEX = posix; then +# Some versions of Solaris have a regcomp() function, but it doesn't work! +# So we run a test program. If we're cross-compiling, do it the old way. +AC_MSG_CHECKING(for POSIX regcomp) +AC_TRY_RUN([ +#include +#include +main() { regex_t r; regmatch_t rm; char *text = "xabcy"; +if (regcomp(&r, "abc", 0)) exit(1); +if (regexec(&r, text, 1, &rm, 0)) exit(1); +#ifndef __WATCOMC__ +if (rm.rm_so != 1) exit(1); /* check for correct offset */ +#else +if (rm.rm_sp != text + 1) exit(1); /* check for correct offset */ +#endif +exit(0); }], + have_posix_regex=yes, have_posix_regex=no, have_posix_regex=unknown) +if test $have_posix_regex = yes; then + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_POSIX_REGCOMP) supported_regex="$supported_regex posix" + have_regex=yes +elif test $have_posix_regex = unknown; then + AC_TRY_LINK([ +#include +#include ], + [regex_t *r; regfree(r);], + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_POSIX_REGCOMP) have_regex=yes; supported_regex="$supported_regex posix") +else + AC_MSG_RESULT(no) +fi +fi +fi + +if test $have_regex = no; then +if test $WANT_REGEX = auto -o $WANT_REGEX = pcre; then +AC_CHECK_LIB(pcre, pcre_compile, +[AC_DEFINE(HAVE_PCRE) LIBS="$LIBS -lpcre" have_regex=yes; supported_regex="$supported_regex pcre"], []) +fi +fi + +if test $have_regex = no; then +if test $WANT_REGEX = auto -o $WANT_REGEX = gnu; then +AC_CHECK_LIB(c, re_compile_pattern, +[AC_DEFINE(HAVE_GNU_REGEX) have_regex=yes; supported_regex="$supported_regex gnu"], []) +fi +fi + +if test $have_regex = no; then +if test $WANT_REGEX = auto -o $WANT_REGEX = regcmp; then +AC_CHECK_FUNC(regcmp, +[AC_DEFINE(HAVE_REGCMP) have_regex=yes; supported_regex="$supported_regex regcmp"],[]) +fi +fi + +if test $have_regex = no; then +if test $WANT_REGEX = auto -o $WANT_REGEX = regcomp; then +AC_MSG_CHECKING(for V8 regcomp) +AC_TRY_LINK([ +#include "regexp.h"], [regcomp("");], +[AC_MSG_RESULT(yes); AC_DEFINE(HAVE_V8_REGCOMP) have_regex=yes; supported_regex="$supported_regex regcomp"],[AC_MSG_RESULT(no)]) +fi +fi + +if test $have_regex = no && test -f ${srcdir}/regexp.c; then +if test $WANT_REGEX = auto -o $WANT_REGEX = regcomp-local; then +AC_MSG_RESULT(using V8 regcomp -- local source); AC_DEFINE(HAVE_V8_REGCOMP) +supported_regex="$supported_regex regcomp-local" +AC_DEFINE(HAVE_REGEXEC2) REGEX_O='regexp.$(O)' AC_SUBST(REGEX_O) have_regex=yes +fi +fi + +if test $have_regex = no; then +if test $WANT_REGEX = auto -o $WANT_REGEX = re_comp; then +AC_MSG_CHECKING(for re_comp) +AC_CHECK_FUNC(re_comp, +[AC_DEFINE(HAVE_RE_COMP) have_regex=yes; supported_regex="$supported_regex re_comp"],[]) +fi +fi + +if test $have_regex = no; then +if test $WANT_REGEX = auto -o $WANT_REGEX = none; then +AC_MSG_RESULT(using no regex) +else +AC_MSG_WARN(cannot find regular expression library) +fi +AC_DEFINE(NO_REGEX) supported_regex="$supported_regex none" +fi + +AC_MSG_RESULT(regular expression library: $supported_regex) + +AC_ARG_WITH(editor, + [ --with-editor=PROGRAM use PROGRAM as the default editor [[vi]]], + AC_DEFINE_UNQUOTED(EDIT_PGM, "$withval"), AC_DEFINE(EDIT_PGM, "vi")) + +AH_TOP([ +/* Unix definition file for less. -*- C -*- + * + * This file has 3 sections: + * User preferences. + * Settings always true on Unix. + * Settings automatically determined by configure. + * + * * * * * * WARNING * * * * * * + * If you edit defines.h by hand, do "touch stamp-h" before you run make + * so config.status doesn't overwrite your changes. + */ + +/* User preferences. */ + +/* + * SECURE is 1 if you wish to disable a bunch of features in order to + * be safe to run by unprivileged users. + * SECURE_COMPILE is set by the --with-secure configure option. + */ +#define SECURE SECURE_COMPILE + +/* + * SHELL_ESCAPE is 1 if you wish to allow shell escapes. + * (This is possible only if your system supplies the system() function.) + */ +#define SHELL_ESCAPE (!SECURE) + +/* + * EXAMINE is 1 if you wish to allow examining files by name from within less. + */ +#define EXAMINE (!SECURE) + +/* + * TAB_COMPLETE_FILENAME is 1 if you wish to allow the TAB key + * to complete filenames at prompts. + */ +#define TAB_COMPLETE_FILENAME (!SECURE) + +/* + * CMD_HISTORY is 1 if you wish to allow keys to cycle through + * previous commands at prompts. + */ +#define CMD_HISTORY 1 + +/* + * HILITE_SEARCH is 1 if you wish to have search targets to be + * displayed in standout mode. + */ +#define HILITE_SEARCH 1 + +/* + * EDITOR is 1 if you wish to allow editor invocation (the "v" command). + * (This is possible only if your system supplies the system() function.) + * EDIT_PGM is the name of the (default) editor to be invoked. + */ +#define EDITOR (!SECURE) + +/* + * TAGS is 1 if you wish to support tag files. + */ +#define TAGS (!SECURE) + +/* + * USERFILE is 1 if you wish to allow a .less file to specify + * user-defined key bindings. + */ +#define USERFILE (!SECURE) + +/* + * GLOB is 1 if you wish to have shell metacharacters expanded in filenames. + * This will generally work if your system provides the "popen" function + * and the "echo" shell command. + */ +#define GLOB (!SECURE) + +/* + * PIPEC is 1 if you wish to have the "|" command + * which allows the user to pipe data into a shell command. + */ +#define PIPEC (!SECURE) + +/* + * LOGFILE is 1 if you wish to allow the -l option (to create log files). + */ +#define LOGFILE (!SECURE) + +/* + * GNU_OPTIONS is 1 if you wish to support the GNU-style command + * line options --help and --version. + */ +#define GNU_OPTIONS 1 + +/* + * ONLY_RETURN is 1 if you want RETURN to be the only input which + * will continue past an error message. + * Otherwise, any key will continue past an error message. + */ +#define ONLY_RETURN 0 + +/* + * LESSKEYFILE is the filename of the default lesskey output file + * (in the HOME directory). + * LESSKEYFILE_SYS is the filename of the system-wide lesskey output file. + * DEF_LESSKEYINFILE is the filename of the default lesskey input + * (in the HOME directory). + * LESSHISTFILE is the filename of the history file + * (in the HOME directory). + */ +#define LESSKEYFILE ".less" +#define LESSKEYFILE_SYS SYSDIR "/sysless" +#define DEF_LESSKEYINFILE ".lesskey" +#define LESSHISTFILE ".lesshst" + + +/* Settings always true on Unix. */ + +/* + * Define MSDOS_COMPILER if compiling under Microsoft C. + */ +#define MSDOS_COMPILER 0 + +/* + * Pathname separator character. + */ +#define PATHNAME_SEP "/" + +/* + * The value returned from tgetent on success. + * Some HP-UX systems return 0 on success. + */ +#define TGETENT_OK 1 + +/* + * HAVE_SYS_TYPES_H is 1 if your system has . + */ +#define HAVE_SYS_TYPES_H 1 + +/* + * Define if you have the header file. + */ +#undef HAVE_SGSTAT_H + +/* + * HAVE_PERROR is 1 if your system has the perror() call. + * (Actually, if it has sys_errlist, sys_nerr and errno.) + */ +#define HAVE_PERROR 1 + +/* + * HAVE_TIME is 1 if your system has the time() call. + */ +#define HAVE_TIME 1 + +/* + * HAVE_SHELL is 1 if your system supports a SHELL command interpreter. + */ +#define HAVE_SHELL 1 + +/* + * Default shell metacharacters and meta-escape character. + */ +#define DEF_METACHARS "; *?\t\n'\"()<>[]|&^`#\\$%=~" +#define DEF_METAESCAPE "\\" + +/* + * HAVE_DUP is 1 if your system has the dup() call. + */ +#define HAVE_DUP 1 + +/* Define to 1 if you have the memcpy() function. */ +#define HAVE_MEMCPY 1 + +/* Define to 1 if you have the strchr() function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the strstr() function. */ +#define HAVE_STRSTR 1 + +/* + * Sizes of various buffers. + */ +#if 0 /* old sizes for small memory machines */ +#define CMDBUF_SIZE 512 /* Buffer for multichar commands */ +#define UNGOT_SIZE 100 /* Max chars to unget() */ +#define LINEBUF_SIZE 1024 /* Max size of line in input file */ +#define OUTBUF_SIZE 1024 /* Output buffer */ +#define PROMPT_SIZE 200 /* Max size of prompt string */ +#define TERMBUF_SIZE 2048 /* Termcap buffer for tgetent */ +#define TERMSBUF_SIZE 1024 /* Buffer to hold termcap strings */ +#define TAGLINE_SIZE 512 /* Max size of line in tags file */ +#define TABSTOP_MAX 32 /* Max number of custom tab stops */ +#else /* more reasonable sizes for modern machines */ +#define CMDBUF_SIZE 2048 /* Buffer for multichar commands */ +#define UNGOT_SIZE 200 /* Max chars to unget() */ +#define LINEBUF_SIZE 1024 /* Initial max size of line in input file */ +#define OUTBUF_SIZE 1024 /* Output buffer */ +#define PROMPT_SIZE 2048 /* Max size of prompt string */ +#define TERMBUF_SIZE 2048 /* Termcap buffer for tgetent */ +#define TERMSBUF_SIZE 1024 /* Buffer to hold termcap strings */ +#define TAGLINE_SIZE 1024 /* Max size of line in tags file */ +#define TABSTOP_MAX 128 /* Max number of custom tab stops */ +#endif + +/* Settings automatically determined by configure. */ +]) + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/files/Sources/files/less/cvt.c b/files/Sources/files/less/cvt.c new file mode 100644 index 00000000..dc20b9c7 --- /dev/null +++ b/files/Sources/files/less/cvt.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + +/* + * Routines to convert text in various ways. Used by search. + */ + +#include "less.h" +#include "charset.h" + +extern int utf_mode; + +/* + * Get the length of a buffer needed to convert a string. + */ + public int +cvt_length(len, ops) + int len; + int ops; +{ + if (utf_mode) + /* + * Just copying a string in UTF-8 mode can cause it to grow + * in length. + * Four output bytes for one input byte is the worst case. + */ + len *= 4; + return (len + 1); +} + +/* + * Allocate a chpos array for use by cvt_text. + */ + public int * +cvt_alloc_chpos(len) + int len; +{ + int i; + int *chpos = (int *) ecalloc(sizeof(int), len); + /* Initialize all entries to an invalid position. */ + for (i = 0; i < len; i++) + chpos[i] = -1; + return (chpos); +} + +/* + * Convert text. Perform the transformations specified by ops. + * Returns converted text in odst. The original offset of each + * odst character (when it was in osrc) is returned in the chpos array. + */ + public void +cvt_text(odst, osrc, chpos, lenp, ops) + char *odst; + char *osrc; + int *chpos; + int *lenp; + int ops; +{ + char *dst; + char *edst = odst; + char *src; + register char *src_end; + LWCHAR ch; + + if (lenp != NULL) + src_end = osrc + *lenp; + else + src_end = osrc + strlen(osrc); + + for (src = osrc, dst = odst; src < src_end; ) + { + int src_pos = (int) (src - osrc); + int dst_pos = (int) (dst - odst); + ch = step_char(&src, +1, src_end); + if ((ops & CVT_BS) && ch == '\b' && dst > odst) + { + /* Delete backspace and preceding char. */ + do { + dst--; + } while (dst > odst && + !IS_ASCII_OCTET(*dst) && !IS_UTF8_LEAD(*dst)); + } else if ((ops & CVT_ANSI) && IS_CSI_START(ch)) + { + /* Skip to end of ANSI escape sequence. */ + src++; /* skip the CSI start char */ + while (src < src_end) + if (!is_ansi_middle(*src++)) + break; + } else + { + /* Just copy the char to the destination buffer. */ + if ((ops & CVT_TO_LC) && IS_UPPER(ch)) + ch = TO_LOWER(ch); + put_wchar(&dst, ch); + /* Record the original position of the char. */ + if (chpos != NULL) + chpos[dst_pos] = src_pos; + } + if (dst > edst) + edst = dst; + } + if ((ops & CVT_CRLF) && edst > odst && edst[-1] == '\r') + edst--; + *edst = '\0'; + if (lenp != NULL) + *lenp = (int) (edst - odst); + /* FIXME: why was this here? if (chpos != NULL) chpos[dst - odst] = src - osrc; */ +} diff --git a/files/Sources/files/less/decode.c b/files/Sources/files/less/decode.c new file mode 100644 index 00000000..2032564c --- /dev/null +++ b/files/Sources/files/less/decode.c @@ -0,0 +1,907 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Routines to decode user commands. + * + * This is all table driven. + * A command table is a sequence of command descriptors. + * Each command descriptor is a sequence of bytes with the following format: + * ...<0> + * The characters c1,c2,...,cN are the command string; that is, + * the characters which the user must type. + * It is terminated by a null <0> byte. + * The byte after the null byte is the action code associated + * with the command string. + * If an action byte is OR-ed with A_EXTRA, this indicates + * that the option byte is followed by an extra string. + * + * There may be many command tables. + * The first (default) table is built-in. + * Other tables are read in from "lesskey" files. + * All the tables are linked together and are searched in order. + */ + +#include "less.h" +#include "cmd.h" +#ifdef __APPLE__ +// #include "get_compat.h" +// #else +#define COMPAT_MODE(func, mode) 1 +#endif + +extern int erase_char, erase2_char, kill_char; +extern int secure; + +#define SK(k) \ + SK_SPECIAL_KEY, (k), 6, 1, 1, 1 +/* + * Command table is ordered roughly according to expected + * frequency of use, so the common commands are near the beginning. + */ + +#include "tableinit.h" + +static unsigned char cmdtable[] = +{ + '\r',0, A_F_LINE, + '\n',0, A_F_LINE, + 'e',0, A_F_LINE, + 'j',0, A_F_LINE, + SK(SK_DOWN_ARROW),0, A_F_LINE, + CONTROL('E'),0, A_F_LINE, + CONTROL('N'),0, A_F_LINE, + 'k',0, A_B_LINE, + 'y',0, A_B_LINE, + CONTROL('Y'),0, A_B_LINE, + SK(SK_CONTROL_K),0, A_B_LINE, + CONTROL('P'),0, A_B_LINE, + SK(SK_UP_ARROW),0, A_B_LINE, + 'J',0, A_FF_LINE, + 'K',0, A_BF_LINE, + 'Y',0, A_BF_LINE, + 'd',0, A_F_SCROLL, + CONTROL('D'),0, A_F_SCROLL, + 'u',0, A_B_SCROLL, + CONTROL('U'),0, A_B_SCROLL, + ' ',0, A_F_SCREEN, + 'f',0, A_F_SCREEN, + CONTROL('F'),0, A_F_SCREEN, + CONTROL('V'),0, A_F_SCREEN, + SK(SK_PAGE_DOWN),0, A_F_SCREEN, + 'b',0, A_B_SCREEN, + CONTROL('B'),0, A_B_SCREEN, + ESC,'v',0, A_B_SCREEN, + SK(SK_PAGE_UP),0, A_B_SCREEN, + 'z',0, A_F_WINDOW, + 'w',0, A_B_WINDOW, + ESC,' ',0, A_FF_SCREEN, + 'F',0, A_F_FOREVER, + ESC,'F',0, A_F_UNTIL_HILITE, + 'R',0, A_FREPAINT, + 'r',0, A_REPAINT, + CONTROL('R'),0, A_REPAINT, + CONTROL('L'),0, A_REPAINT, + ESC,'u',0, A_UNDO_SEARCH, + 'g',0, A_GOLINE, + SK(SK_HOME),0, A_GOLINE, + '<',0, A_GOLINE, + ESC,'<',0, A_GOLINE, + 'p',0, A_PERCENT, + '%',0, A_PERCENT, + ESC,'[',0, A_LSHIFT, + ESC,']',0, A_RSHIFT, + ESC,'(',0, A_LSHIFT, + ESC,')',0, A_RSHIFT, + ESC,'{',0, A_LLSHIFT, + ESC,'}',0, A_RRSHIFT, + SK(SK_RIGHT_ARROW),0, A_RSHIFT, + SK(SK_LEFT_ARROW),0, A_LSHIFT, + SK(SK_CTL_RIGHT_ARROW),0, A_RRSHIFT, + SK(SK_CTL_LEFT_ARROW),0, A_LLSHIFT, + '{',0, A_F_BRACKET|A_EXTRA, '{','}',0, + '}',0, A_B_BRACKET|A_EXTRA, '{','}',0, + '(',0, A_F_BRACKET|A_EXTRA, '(',')',0, + ')',0, A_B_BRACKET|A_EXTRA, '(',')',0, + '[',0, A_F_BRACKET|A_EXTRA, '[',']',0, + ']',0, A_B_BRACKET|A_EXTRA, '[',']',0, + ESC,CONTROL('F'),0, A_F_BRACKET, + ESC,CONTROL('B'),0, A_B_BRACKET, + 'G',0, A_GOEND, + ESC,'G',0, A_GOEND_BUF, + ESC,'>',0, A_GOEND, + '>',0, A_GOEND, + SK(SK_END),0, A_GOEND, + 'P',0, A_GOPOS, + + '0',0, A_DIGIT, + '1',0, A_DIGIT, + '2',0, A_DIGIT, + '3',0, A_DIGIT, + '4',0, A_DIGIT, + '5',0, A_DIGIT, + '6',0, A_DIGIT, + '7',0, A_DIGIT, + '8',0, A_DIGIT, + '9',0, A_DIGIT, + '.',0, A_DIGIT, + + '=',0, A_STAT, + CONTROL('G'),0, A_STAT, + ':','f',0, A_STAT, + '/',0, A_F_SEARCH, + '?',0, A_B_SEARCH, + ESC,'/',0, A_F_SEARCH|A_EXTRA, '*',0, + ESC,'?',0, A_B_SEARCH|A_EXTRA, '*',0, + 'n',0, A_AGAIN_SEARCH, + ESC,'n',0, A_T_AGAIN_SEARCH, + 'N',0, A_REVERSE_SEARCH, + ESC,'N',0, A_T_REVERSE_SEARCH, + '&',0, A_FILTER, + 'm',0, A_SETMARK, + '\'',0, A_GOMARK, + CONTROL('X'),CONTROL('X'),0, A_GOMARK, + 'E',0, A_EXAMINE, + ':','e',0, A_EXAMINE, + CONTROL('X'),CONTROL('V'),0, A_EXAMINE, + ':','n',0, A_NEXT_FILE, + ':','p',0, A_PREV_FILE, + 't',0, A_NEXT_TAG, + 'T',0, A_PREV_TAG, + ':','x',0, A_INDEX_FILE, + ':','d',0, A_REMOVE_FILE, + '-',0, A_OPT_TOGGLE, + ':','t',0, A_OPT_TOGGLE|A_EXTRA, 't',0, + 's',0, A_OPT_TOGGLE|A_EXTRA, 'o',0, + '_',0, A_DISP_OPTION, + '|',0, A_PIPE, + 'v',0, A_VISUAL, + '!',0, A_SHELL, + '+',0, A_FIRSTCMD, + + 'H',0, A_HELP, + 'h',0, A_HELP, + SK(SK_F1),0, A_HELP, + 'V',0, A_VERSION, + 'q',0, A_QUIT, + 'Q',0, A_QUIT, + ':','q',0, A_QUIT, + ':','Q',0, A_QUIT, + 'Z','Z',0, A_QUIT +}; + +/* + * Command table for UNIX 2003 compatibility: added first before builtin + * so that these commands override the normal LESS commands + */ + +static unsigned char UNIX03cmdtable[] = +{ + 's',0, A_F_LINE +}; + +static unsigned char edittable[] = +{ + '\t',0, EC_F_COMPLETE, /* TAB */ + '\17',0, EC_B_COMPLETE, /* BACKTAB */ + SK(SK_BACKTAB),0, EC_B_COMPLETE, /* BACKTAB */ + ESC,'\t',0, EC_B_COMPLETE, /* ESC TAB */ + CONTROL('L'),0, EC_EXPAND, /* CTRL-L */ + CONTROL('V'),0, EC_LITERAL, /* BACKSLASH */ + CONTROL('A'),0, EC_LITERAL, /* BACKSLASH */ + ESC,'l',0, EC_RIGHT, /* ESC l */ + SK(SK_RIGHT_ARROW),0, EC_RIGHT, /* RIGHTARROW */ + ESC,'h',0, EC_LEFT, /* ESC h */ + SK(SK_LEFT_ARROW),0, EC_LEFT, /* LEFTARROW */ + ESC,'b',0, EC_W_LEFT, /* ESC b */ + ESC,SK(SK_LEFT_ARROW),0, EC_W_LEFT, /* ESC LEFTARROW */ + SK(SK_CTL_LEFT_ARROW),0, EC_W_LEFT, /* CTRL-LEFTARROW */ + ESC,'w',0, EC_W_RIGHT, /* ESC w */ + ESC,SK(SK_RIGHT_ARROW),0, EC_W_RIGHT, /* ESC RIGHTARROW */ + SK(SK_CTL_RIGHT_ARROW),0, EC_W_RIGHT, /* CTRL-RIGHTARROW */ + ESC,'i',0, EC_INSERT, /* ESC i */ + SK(SK_INSERT),0, EC_INSERT, /* INSERT */ + ESC,'x',0, EC_DELETE, /* ESC x */ + SK(SK_DELETE),0, EC_DELETE, /* DELETE */ + ESC,'X',0, EC_W_DELETE, /* ESC X */ + ESC,SK(SK_DELETE),0, EC_W_DELETE, /* ESC DELETE */ + SK(SK_CTL_DELETE),0, EC_W_DELETE, /* CTRL-DELETE */ + SK(SK_CTL_BACKSPACE),0, EC_W_BACKSPACE, /* CTRL-BACKSPACE */ + ESC,'\b',0, EC_W_BACKSPACE, /* ESC BACKSPACE */ + ESC,'0',0, EC_HOME, /* ESC 0 */ + SK(SK_HOME),0, EC_HOME, /* HOME */ + ESC,'$',0, EC_END, /* ESC $ */ + SK(SK_END),0, EC_END, /* END */ + ESC,'k',0, EC_UP, /* ESC k */ + SK(SK_UP_ARROW),0, EC_UP, /* UPARROW */ + ESC,'j',0, EC_DOWN, /* ESC j */ + SK(SK_DOWN_ARROW),0, EC_DOWN, /* DOWNARROW */ + CONTROL('G'),0, EC_ABORT, /* CTRL-G */ +}; + +/* + * Structure to support a list of command tables. + */ +struct tablelist +{ + struct tablelist *t_next; + char *t_start; + char *t_end; +}; + +/* + * List of command tables and list of line-edit tables. + */ +static struct tablelist *list_UNIX03cmd_tables = NULL; +static struct tablelist *list_fcmd_tables = NULL; +static struct tablelist *list_ecmd_tables = NULL; +static struct tablelist *list_var_tables = NULL; +static struct tablelist *list_sysvar_tables = NULL; + + +/* + * Expand special key abbreviations in a command table. + */ + static void +expand_special_keys(table, len) + char *table; + int len; +{ + register char *fm; + register char *to; + register int a; + char *repl; + int klen; + + for (fm = table; fm < table + len; ) + { + /* + * Rewrite each command in the table with any + * special key abbreviations expanded. + */ + for (to = fm; *fm != '\0'; ) + { + if (*fm != SK_SPECIAL_KEY) + { + *to++ = *fm++; + continue; + } + /* + * After SK_SPECIAL_KEY, next byte is the type + * of special key (one of the SK_* contants), + * and the byte after that is the number of bytes, + * N, reserved by the abbreviation (including the + * SK_SPECIAL_KEY and key type bytes). + * Replace all N bytes with the actual bytes + * output by the special key on this terminal. + */ + repl = special_key_str(fm[1]); + klen = fm[2] & 0377; + fm += klen; + if (repl == NULL || (int) strlen(repl) > klen) + repl = "\377"; + while (*repl != '\0') + *to++ = *repl++; + } + *to++ = '\0'; + /* + * Fill any unused bytes between end of command and + * the action byte with A_SKIP. + */ + while (to <= fm) + *to++ = A_SKIP; + fm++; + a = *fm++ & 0377; + if (a & A_EXTRA) + { + while (*fm++ != '\0') + continue; + } + } +} + +/* + * Initialize the command lists. + */ + public void +init_cmds() +{ + // iOS: re-initialize the tables? + memcpy(UNIX03cmdtable, UNIX03cmdtable_init, sizeof(UNIX03cmdtable_init)); + memcpy(cmdtable, cmdtable_init, sizeof(cmdtable_init)); + memcpy(edittable, edittable_init, sizeof(edittable_init)); + + /* + * Add the default command tables. + */ + if (COMPAT_MODE("bin/more", "unix2003")) { + add_UNIX03cmd_table((char*)UNIX03cmdtable, sizeof(UNIX03cmdtable)); + } + add_fcmd_table((char*)cmdtable, sizeof(cmdtable)); + add_ecmd_table((char*)edittable, sizeof(edittable)); +#if USERFILE + /* + * For backwards compatibility, + * try to add tables in the OLD system lesskey file. + */ +#ifdef BINDIR + add_hometable(NULL, BINDIR "/.sysless", 1); +#endif + /* + * Try to add the tables in the system lesskey file. + */ + add_hometable("LESSKEY_SYSTEM", LESSKEYFILE_SYS, 1); + /* + * Try to add the tables in the standard lesskey file "$HOME/.less". + */ + add_hometable("LESSKEY", LESSKEYFILE, 0); +#endif +} + +/* + * Cleanup the command lists + */ + public void +clean_cmds() +{ + // Not enough. + free(list_UNIX03cmd_tables); + list_UNIX03cmd_tables = NULL; + free(list_fcmd_tables); + list_fcmd_tables = NULL; + free(list_ecmd_tables); + list_ecmd_tables = NULL; + free(list_var_tables); + list_var_tables = NULL; + free(list_sysvar_tables); + list_sysvar_tables = NULL; +} +/* + * Add a command table. + */ + static int +add_cmd_table(tlist, buf, len) + struct tablelist **tlist; + char *buf; + int len; +{ + register struct tablelist *t; + + if (len == 0) + return (0); + /* + * Allocate a tablelist structure, initialize it, + * and link it into the list of tables. + */ + if ((t = (struct tablelist *) + calloc(1, sizeof(struct tablelist))) == NULL) + { + return (-1); + } + expand_special_keys(buf, len); + t->t_start = buf; + t->t_end = buf + len; + t->t_next = *tlist; + *tlist = t; + return (0); +} + +/* + * Add the UNIX2003 command table. + */ + public void +add_UNIX03cmd_table(buf, len) + char *buf; + int len; +{ + if (add_cmd_table(&list_UNIX03cmd_tables, buf, len) < 0) + error("Warning: some commands disabled", NULL_PARG); +} + +/* + * Add a command table. + */ + public void +add_fcmd_table(buf, len) + char *buf; + int len; +{ + if (add_cmd_table(&list_fcmd_tables, buf, len) < 0) + error("Warning: some commands disabled", NULL_PARG); +} + +/* + * Add an editing command table. + */ + public void +add_ecmd_table(buf, len) + char *buf; + int len; +{ + if (add_cmd_table(&list_ecmd_tables, buf, len) < 0) + error("Warning: some edit commands disabled", NULL_PARG); +} + +/* + * Add an environment variable table. + */ + static void +add_var_table(tlist, buf, len) + struct tablelist **tlist; + char *buf; + int len; +{ + if (add_cmd_table(tlist, buf, len) < 0) + error("Warning: environment variables from lesskey file unavailable", NULL_PARG); +} + +/* + * Search a single command table for the command string in cmd. + */ + static int +cmd_search(cmd, table, endtable, sp) + char *cmd; + char *table; + char *endtable; + char **sp; +{ + register char *p; + register char *q; + register int a; + + *sp = NULL; + for (p = table, q = cmd; p < endtable; p++, q++) + { + if (*p == *q) + { + /* + * Current characters match. + * If we're at the end of the string, we've found it. + * Return the action code, which is the character + * after the null at the end of the string + * in the command table. + */ + if (*p == '\0') + { + a = *++p & 0377; + + while (a == A_SKIP) + a = *++p & 0377; + if (a == A_END_LIST) + { + /* + * We get here only if the original + * cmd string passed in was empty (""). + * I don't think that can happen, + * but just in case ... + */ + return (A_UINVALID); + } + /* + * Check for an "extra" string. + */ + if (a & A_EXTRA) + { + *sp = ++p; + a &= ~A_EXTRA; + } + return (a); + } + } else if (*q == '\0') + { + /* + * Hit the end of the user's command, + * but not the end of the string in the command table. + * The user's command is incomplete. + */ + return (A_PREFIX); + } else + { + /* + * Not a match. + * Skip ahead to the next command in the + * command table, and reset the pointer + * to the beginning of the user's command. + */ + if (*p == '\0' && p[1] == A_END_LIST) + { + /* + * A_END_LIST is a special marker that tells + * us to abort the cmd search. + */ + return (A_UINVALID); + } + while (*p++ != '\0') + continue; + while (*p == A_SKIP) + p++; + if (*p & A_EXTRA) + while (*++p != '\0') + continue; + q = cmd-1; + } + } + /* + * No match found in the entire command table. + */ + return (A_INVALID); +} + +/* + * Decode a command character and return the associated action. + * The "extra" string, if any, is returned in sp. + */ + static int +cmd_decode(tlist, cmd, sp) + struct tablelist *tlist; + char *cmd; + char **sp; +{ + register struct tablelist *t; + register int action = A_INVALID; + + /* + * Search thru all the command tables. + * Stop when we find an action which is not A_INVALID. + */ + for (t = tlist; t != NULL; t = t->t_next) + { + action = cmd_search(cmd, t->t_start, t->t_end, sp); + if (action != A_INVALID) + break; + } + if (action == A_UINVALID) + action = A_INVALID; + return (action); +} + +/* + * Decode a command from the cmdtables list. + */ + public int +fcmd_decode(cmd, sp) + char *cmd; + char **sp; +{ + if (list_UNIX03cmd_tables != NULL) { + /* If it exists, try to decode it first */ + int v = cmd_decode(list_UNIX03cmd_tables, cmd, sp); + if (v != A_INVALID) return v; + } + return (cmd_decode(list_fcmd_tables, cmd, sp)); +} + +/* + * Decode a command from the edittables list. + */ + public int +ecmd_decode(cmd, sp) + char *cmd; + char **sp; +{ + return (cmd_decode(list_ecmd_tables, cmd, sp)); +} + +/* + * Get the value of an environment variable. + * Looks first in the lesskey file, then in the real environment. + */ + public char * +lgetenv(var) + char *var; +{ + int a; + char *s; + + a = cmd_decode(list_var_tables, var, &s); + if (a == EV_OK) + return (s); + s = getenv(var); + if (s != NULL && *s != '\0') + return (s); + a = cmd_decode(list_sysvar_tables, var, &s); + if (a == EV_OK) + return (s); + return (NULL); +} + +#if USERFILE +/* + * Get an "integer" from a lesskey file. + * Integers are stored in a funny format: + * two bytes, low order first, in radix KRADIX. + */ + static int +gint(sp) + char **sp; +{ + int n; + + n = *(*sp)++; + n += *(*sp)++ * KRADIX; + return (n); +} + +/* + * Process an old (pre-v241) lesskey file. + */ + static int +old_lesskey(buf, len) + char *buf; + int len; +{ + /* + * Old-style lesskey file. + * The file must end with either + * ...,cmd,0,action + * or ...,cmd,0,action|A_EXTRA,string,0 + * So the last byte or the second to last byte must be zero. + */ + if (buf[len-1] != '\0' && buf[len-2] != '\0') + return (-1); + add_fcmd_table(buf, len); + return (0); +} + +/* + * Process a new (post-v241) lesskey file. + */ + static int +new_lesskey(buf, len, sysvar) + char *buf; + int len; + int sysvar; +{ + char *p; + register int c; + register int n; + + /* + * New-style lesskey file. + * Extract the pieces. + */ + if (buf[len-3] != C0_END_LESSKEY_MAGIC || + buf[len-2] != C1_END_LESSKEY_MAGIC || + buf[len-1] != C2_END_LESSKEY_MAGIC) + return (-1); + p = buf + 4; + for (;;) + { + c = *p++; + switch (c) + { + case CMD_SECTION: + n = gint(&p); + add_fcmd_table(p, n); + p += n; + break; + case EDIT_SECTION: + n = gint(&p); + add_ecmd_table(p, n); + p += n; + break; + case VAR_SECTION: + n = gint(&p); + add_var_table((sysvar) ? + &list_sysvar_tables : &list_var_tables, p, n); + p += n; + break; + case END_SECTION: + return (0); + default: + /* + * Unrecognized section type. + */ + return (-1); + } + } +} + +/* + * Set up a user command table, based on a "lesskey" file. + */ + public int +lesskey(filename, sysvar) + char *filename; + int sysvar; +{ + register char *buf; + register POSITION len; + register long n; + register int f; + + if (secure) + return (1); + /* + * Try to open the lesskey file. + */ + filename = shell_unquote(filename); + f = open(filename, OPEN_READ); + free(filename); + if (f < 0) + return (1); + + /* + * Read the file into a buffer. + * We first figure out the size of the file and allocate space for it. + * {{ Minimal error checking is done here. + * A garbage .less file will produce strange results. + * To avoid a large amount of error checking code here, we + * rely on the lesskey program to generate a good .less file. }} + */ + len = filesize(f); + if (len == NULL_POSITION || len < 3) + { + /* + * Bad file (valid file must have at least 3 chars). + */ + close(f); + return (-1); + } + if ((buf = (char *) calloc((int)len, sizeof(char))) == NULL) + { + close(f); + return (-1); + } + if (lseek(f, (off_t)0, SEEK_SET) == BAD_LSEEK) + { + free(buf); + close(f); + return (-1); + } + n = read(f, buf, (unsigned int) len); + close(f); + if (n != len) + { + free(buf); + return (-1); + } + + /* + * Figure out if this is an old-style (before version 241) + * or new-style lesskey file format. + */ + if (buf[0] != C0_LESSKEY_MAGIC || buf[1] != C1_LESSKEY_MAGIC || + buf[2] != C2_LESSKEY_MAGIC || buf[3] != C3_LESSKEY_MAGIC) + return (old_lesskey(buf, (int)len)); + return (new_lesskey(buf, (int)len, sysvar)); +} + +/* + * Add the standard lesskey file "$HOME/.less" + */ + public void +add_hometable(envname, def_filename, sysvar) + char *envname; + char *def_filename; + int sysvar; +{ + char *filename; + PARG parg; + + if (envname != NULL && (filename = lgetenv(envname)) != NULL) + filename = save(filename); + else if (sysvar) + filename = save(def_filename); + else + filename = homefile(def_filename); + if (filename == NULL) + return; + if (lesskey(filename, sysvar) < 0) + { + parg.p_string = filename; + error("Cannot use lesskey file \"%s\"", &parg); + } + free(filename); +} +#endif + +/* + * See if a char is a special line-editing command. + */ + public int +editchar(c, flags) + int c; + int flags; +{ + int action; + int nch; + char *s; + char usercmd[MAX_CMDLEN+1]; + + /* + * An editing character could actually be a sequence of characters; + * for example, an escape sequence sent by pressing the uparrow key. + * To match the editing string, we use the command decoder + * but give it the edit-commands command table + * This table is constructed to match the user's keyboard. + */ + if (c == erase_char || c == erase2_char) + return (EC_BACKSPACE); + if (c == kill_char) + return (EC_LINEKILL); + + /* + * Collect characters in a buffer. + * Start with the one we have, and get more if we need them. + */ + nch = 0; + do { + if (nch > 0) + c = getcc(); + usercmd[nch] = c; + usercmd[nch+1] = '\0'; + nch++; + action = ecmd_decode(usercmd, &s); + } while (action == A_PREFIX); + + if (flags & EC_NORIGHTLEFT) + { + switch (action) + { + case EC_RIGHT: + case EC_LEFT: + action = A_INVALID; + break; + } + } +#if CMD_HISTORY + if (flags & EC_NOHISTORY) + { + /* + * The caller says there is no history list. + * Reject any history-manipulation action. + */ + switch (action) + { + case EC_UP: + case EC_DOWN: + action = A_INVALID; + break; + } + } +#endif +#if TAB_COMPLETE_FILENAME + if (flags & EC_NOCOMPLETE) + { + /* + * The caller says we don't want any filename completion cmds. + * Reject them. + */ + switch (action) + { + case EC_F_COMPLETE: + case EC_B_COMPLETE: + case EC_EXPAND: + action = A_INVALID; + break; + } + } +#endif + if ((flags & EC_PEEK) || action == A_INVALID) + { + /* + * We're just peeking, or we didn't understand the command. + * Unget all the characters we read in the loop above. + * This does NOT include the original character that was + * passed in as a parameter. + */ + while (nch > 1) + { + ungetcc(usercmd[--nch]); + } + } else + { + if (s != NULL) + ungetsc(s); + } + return action; +} + diff --git a/files/Sources/files/less/defines.ds b/files/Sources/files/less/defines.ds new file mode 100644 index 00000000..6009129d --- /dev/null +++ b/files/Sources/files/less/defines.ds @@ -0,0 +1,414 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* DOS definition file for less. */ +/* + * This file has 2 sections: + * User preferences. + * Settings always true for MS-DOS systems. + */ + +/* User preferences. */ + +/* + * SECURE is 1 if you wish to disable a bunch of features in order to + * be safe to run by unprivileged users. + */ +#define SECURE 0 + +/* + * SHELL_ESCAPE is 1 if you wish to allow shell escapes. + * (This is possible only if your system supplies the system() function.) + */ +#define SHELL_ESCAPE (!SECURE) + +/* + * EXAMINE is 1 if you wish to allow examining files by name from within less. + */ +#define EXAMINE (!SECURE) + +/* + * TAB_COMPLETE_FILENAME is 1 if you wish to allow the TAB key + * to complete filenames at prompts. + */ +#define TAB_COMPLETE_FILENAME (!SECURE) + +/* + * CMD_HISTORY is 1 if you wish to allow keys to cycle through + * previous commands at prompts. + */ +#define CMD_HISTORY 1 + +/* + * HILITE_SEARCH is 1 if you wish to have search targets to be + * displayed in standout mode. + */ +#define HILITE_SEARCH 1 + +/* + * EDITOR is 1 if you wish to allow editor invocation (the "v" command). + * (This is possible only if your system supplies the system() function.) + * EDIT_PGM is the name of the (default) editor to be invoked. + */ +#define EDITOR (!SECURE) +#define EDIT_PGM "vi" + +/* + * TAGS is 1 if you wish to support tag files. + */ +#define TAGS (!SECURE) + +/* + * USERFILE is 1 if you wish to allow a .less file to specify + * user-defined key bindings. + */ +#define USERFILE (!SECURE) + +/* + * GLOB is 1 if you wish to have shell metacharacters expanded in filenames. + * This will generally work if your system provides the "popen" function + * and the "echo" shell command. + */ +#ifdef __DJGPP__ +#define GLOB (!SECURE) +#else +#define GLOB 0 +#endif + +/* + * PIPEC is 1 if you wish to have the "|" command + * which allows the user to pipe data into a shell command. + */ +#ifdef __DJGPP__ +#define PIPEC (!SECURE) +#else +#define PIPEC 0 +#endif + +/* + * LOGFILE is 1 if you wish to allow the -l option (to create log files). + */ +#define LOGFILE (!SECURE) + +/* + * GNU_OPTIONS is 1 if you wish to support the GNU-style command + * line options --help and --version. + */ +#define GNU_OPTIONS 1 + +/* + * ONLY_RETURN is 1 if you want RETURN to be the only input which + * will continue past an error message. + * Otherwise, any key will continue past an error message. + */ +#define ONLY_RETURN 0 + +/* + * LESSKEYFILE is the filename of the default lesskey output file + * (in the HOME directory). + * LESSKEYFILE_SYS is the filename of the system-wide lesskey output file. + * DEF_LESSKEYINFILE is the filename of the default lesskey input + * (in the HOME directory). + * LESSHISTFILE is the filename of the history file + * (in the HOME directory). + */ +#define LESSKEYFILE "_less" +#define LESSKEYFILE_SYS "c:\\_sysless" +#define DEF_LESSKEYINFILE "_lesskey" +#define LESSHISTFILE "_lesshst" + + +/* Settings always true for MS-DOS systems. */ + +/* + * Define MSDOS_COMPILER if compiling for MS-DOS. + */ +#ifdef __DJGPP__ +#define MSDOS_COMPILER DJGPPC +#else +#ifdef __BORLANDC__ +#define MSDOS_COMPILER BORLANDC +#else +#define MSDOS_COMPILER MSOFTC +#endif +#endif + +/* + * Pathname separator character. + */ +#define PATHNAME_SEP "\\" + +/* + * HAVE_SYS_TYPES_H is 1 if your system has . + */ +#define HAVE_SYS_TYPES_H 1 + +/* + * Define if you have the header file. + */ +#define HAVE_SGSTAT_H 0 + +/* + * HAVE_PERROR is 1 if your system has the perror() call. + * (Actually, if it has sys_errlist, sys_nerr and errno.) + */ +#define HAVE_PERROR 1 + +/* + * HAVE_TIME is 1 if your system has the time() call. + */ +#define HAVE_TIME 1 + +/* + * HAVE_SHELL is 1 if your system supports a SHELL command interpreter. + */ +#define HAVE_SHELL 0 + +/* + * Default shell metacharacters and meta-escape character. + */ +#define DEF_METACHARS "; *?\t\n'\"()<>|&" +#define DEF_METAESCAPE "" + +/* + * HAVE_DUP is 1 if your system has the dup() call. + */ +#define HAVE_DUP 1 + +/* + * Sizes of various buffers. + */ +#if 0 /* old sizes for small memory machines +#define CMDBUF_SIZE 512 /* Buffer for multichar commands */ +#define UNGOT_SIZE 100 /* Max chars to unget() */ +#define LINEBUF_SIZE 1024 /* Max size of line in input file */ +#define OUTBUF_SIZE 1024 /* Output buffer */ +#define PROMPT_SIZE 200 /* Max size of prompt string */ +#define TERMBUF_SIZE 2048 /* Termcap buffer for tgetent */ +#define TERMSBUF_SIZE 1024 /* Buffer to hold termcap strings */ +#define TAGLINE_SIZE 512 /* Max size of line in tags file */ +#define TABSTOP_MAX 32 /* Max number of custom tab stops */ +#else /* more reasonable sizes for modern machines */ +#define CMDBUF_SIZE 2048 /* Buffer for multichar commands */ +#define UNGOT_SIZE 200 /* Max chars to unget() */ +#define LINEBUF_SIZE 1024 /* Initial max size of line in input file */ +#define OUTBUF_SIZE 1024 /* Output buffer */ +#define PROMPT_SIZE 2048 /* Max size of prompt string */ +#define TERMBUF_SIZE 2048 /* Termcap buffer for tgetent */ +#define TERMSBUF_SIZE 1024 /* Buffer to hold termcap strings */ +#define TAGLINE_SIZE 1024 /* Max size of line in tags file */ +#define TABSTOP_MAX 128 /* Max number of custom tab stops */ +#endif + +/* Define to `long' if doesn't define. */ +#if MSDOS_COMPILER==BORLANDC +#define off_t long +#endif + +/* Define if you need to in order for stat and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + + +/* + * Regular expression library. + * Define exactly one of the following to be 1: + * HAVE_POSIX_REGCOMP: POSIX regcomp() and regex.h + * HAVE_RE_COMP: BSD re_comp() + * HAVE_REGCMP: System V regcmp() + * HAVE_V8_REGCOMP: Henry Spencer V8 regcomp() and regexp.h + * NO_REGEX: pattern matching is supported, but without metacharacters. + */ +/* #undef HAVE_POSIX_REGCOMP */ +/* #undef HAVE_RE_COMP */ +/* #undef HAVE_REGCMP */ +/* #undef HAVE_V8_REGCOMP */ +#if MSDOS_COMPILER==DJGPPC +#define HAVE_POSIX_REGCOMP 1 +#else +#define NO_REGEX 1 +#endif + +/* Define HAVE_VOID if your compiler supports the "void" type. */ +#define HAVE_VOID 1 + +/* Define HAVE_CONST if your compiler supports the "const" modifier. */ +#define HAVE_CONST 1 + +/* Define HAVE_TIME_T if your system supports the "time_t" type. */ +#define HAVE_TIME_T 1 + +/* Define HAVE_STRERROR if you have the strerror() function. */ +#define HAVE_STRERROR 1 + +/* Define HAVE_FILENO if you have the fileno() macro. */ +#define HAVE_FILENO 1 + +/* Define HAVE_ERRNO if you have the errno variable */ +/* Define MUST_DEFINE_ERRNO if you have errno but it is not defined + * in errno.h */ +#if MSDOS_COMPILER==MSOFTC || MSDOS_COMPILER==DJGPPC +#define HAVE_ERRNO 1 +#define MUST_DEFINE_ERRNO 0 +#else +#define HAVE_ERRNO 1 +#define MUST_DEFINE_ERRNO 1 +#endif + +/* Define HAVE_SYS_ERRLIST if you have the sys_errlist[] variable */ +#define HAVE_SYS_ERRLIST 1 + +/* Define HAVE_OSPEED if your termcap library has the ospeed variable */ +/* Define MUST_DEFINE_OSPEED if you have ospeed but it is not defined + * in termcap.h. */ +#define HAVE_OSPEED 0 +#define MUST_DEFINE_OSPEED 0 + +/* Define HAVE_LOCALE if you have locale.h and setlocale. */ +#define HAVE_LOCALE 0 + +/* Define HAVE_TERMIOS_FUNCS if you have tcgetattr/tcsetattr */ +#define HAVE_TERMIOS_FUNCS 0 + +/* Define HAVE_UPPER_LOWER if you have isupper, islower, toupper, tolower */ +#define HAVE_UPPER_LOWER 1 + +/* Define if you have the _setjmp function. */ +#if MSDOS_COMPILER==MSOFTC || MSDOS_COMPILER==DJGPPC +#define HAVE__SETJMP 0 +#else +#define HAVE__SETJMP 1 +#endif + +/* Define if you have the memcpy function. */ +#define HAVE_MEMCPY 1 + +/* Define if you have the popen function. */ +#if MSDOS_COMPILER==DJGPPC +#define HAVE_POPEN 1 +#else +#define HAVE_POPEN 0 +#endif + +/* Define if you have the sigsetmask function. */ +#define HAVE_SIGSETMASK 0 + +/* Define if you have the sigprocmask function. */ +#define HAVE_SIGPROCMASK 0 + +/* Define if you have the sigset_t type and sigemptyset macro */ +#define HAVE_SIGSET_T 0 +#define HAVE_SIGEMPTYSET 0 + +/* Define if you have the stat function. */ +#define HAVE_STAT 1 + +/* Define if you have the strchr function. */ +#define HAVE_STRCHR 1 + +/* Define if you have the system function. */ +#define HAVE_SYSTEM 1 + +/* Define if you have the snprintf function. */ +#define HAVE_SNPRINTF 0 + +/* Define if you have the header file. */ +#define HAVE_CTYPE_H 1 + +/* Define if you have the header file. */ +#define HAVE_WCTYPE_H 0 + +/* Define if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define HAVE_FLOAT if your compiler supports the "double" type. */ +#define HAVE_FLOAT 1 + +/* Define if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDIO_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_IOCTL_H 0 + +/* Define if you have the header file. */ +#define HAVE_SYS_PTEM_H 0 + +/* Define if you have the header file. */ +#define HAVE_SYS_STREAM_H 0 + +/* Define if you have the header file. */ +#define HAVE_TERMCAP_H 0 + +/* Define if you have the header file. */ +#define HAVE_TERMIO_H 0 + +/* Define if you have the header file. */ +#define HAVE_TERMIOS_H 0 + +/* Define if you have the header file. */ +#define HAVE_TIME_H 1 + +/* Define if you have the header file. */ +#if MSDOS_COMPILER==DJGPPC +#define HAVE_UNISTD_H 1 +#else +#define HAVE_UNISTD_H 0 +#endif + +/* Define if you have the header file. */ +#if MSDOS_COMPILER==MSOFTC +#define HAVE_VALUES_H 0 +#else +#define HAVE_VALUES_H 1 +#endif + +#if MSDOS_COMPILER == MSOFTC && _MSC_VER >= 700 +/* + * The names of these things changed in Microsoft C version 7.0. + */ +#define videoconfig _videoconfig +#define rccoord _rccoord +#define O_RDONLY _O_RDONLY +#define O_WRONLY _O_WRONLY +#define O_APPEND _O_APPEND +#define O_BINARY _O_BINARY +#define O_TEXT _O_TEXT +#define find_t _find_t +#define stat _stat +#define S_IFMT _S_IFMT +#define S_IFDIR _S_IFDIR +#define S_IFREG _S_IFREG +#define dup _dup +#define open _open +#define lseek _lseek +#define write _write +#define creat _creat +#define fstat _fstat +#define isatty _isatty +#define close _close +#define read _read +#define ungetch _ungetch +#define kbhit _kbhit +#define getch _getch +#endif diff --git a/files/Sources/files/less/defines.h b/files/Sources/files/less/defines.h new file mode 100644 index 00000000..73946abd --- /dev/null +++ b/files/Sources/files/less/defines.h @@ -0,0 +1,421 @@ +/* defines.h. Generated from defines.h.in by configure. */ +/* defines.h.in. Generated from configure.ac by autoheader. */ + + +/* Unix definition file for less. -*- C -*- + * + * This file has 3 sections: + * User preferences. + * Settings always true on Unix. + * Settings automatically determined by configure. + * + * * * * * * WARNING * * * * * * + * If you edit defines.h by hand, do "touch stamp-h" before you run make + * so config.status doesn't overwrite your changes. + */ + +/* User preferences. */ + +/* + * SECURE is 1 if you wish to disable a bunch of features in order to + * be safe to run by unprivileged users. + * SECURE_COMPILE is set by the --with-secure configure option. + */ +#define SECURE SECURE_COMPILE + +/* + * SHELL_ESCAPE is 1 if you wish to allow shell escapes. + * (This is possible only if your system supplies the system() function.) + */ +#define SHELL_ESCAPE (!SECURE) + +/* + * EXAMINE is 1 if you wish to allow examining files by name from within less. + */ +#define EXAMINE (!SECURE) + +/* + * TAB_COMPLETE_FILENAME is 1 if you wish to allow the TAB key + * to complete filenames at prompts. + */ +#define TAB_COMPLETE_FILENAME (!SECURE) + +/* + * CMD_HISTORY is 1 if you wish to allow keys to cycle through + * previous commands at prompts. + */ +#define CMD_HISTORY 1 + +/* + * HILITE_SEARCH is 1 if you wish to have search targets to be + * displayed in standout mode. + */ +#define HILITE_SEARCH 1 + +/* + * EDITOR is 1 if you wish to allow editor invocation (the "v" command). + * (This is possible only if your system supplies the system() function.) + * EDIT_PGM is the name of the (default) editor to be invoked. + */ +#define EDITOR (!SECURE) + +/* + * TAGS is 1 if you wish to support tag files. + */ +#define TAGS (!SECURE) + +/* + * USERFILE is 1 if you wish to allow a .less file to specify + * user-defined key bindings. + */ +#define USERFILE 0 + +/* + * GLOB is 1 if you wish to have shell metacharacters expanded in filenames. + * This will generally work if your system provides the "popen" function + * and the "echo" shell command. + */ +#define GLOB (!SECURE) + +/* + * PIPEC is 1 if you wish to have the "|" command + * which allows the user to pipe data into a shell command. + */ +#define PIPEC (!SECURE) + +/* + * LOGFILE is 1 if you wish to allow the -l option (to create log files). + */ +#define LOGFILE (!SECURE) + +/* + * GNU_OPTIONS is 1 if you wish to support the GNU-style command + * line options --help and --version. + */ +#define GNU_OPTIONS 1 + +/* + * ONLY_RETURN is 1 if you want RETURN to be the only input which + * will continue past an error message. + * Otherwise, any key will continue past an error message. + */ +#define ONLY_RETURN 0 + +/* + * LESSKEYFILE is the filename of the default lesskey output file + * (in the HOME directory). + * LESSKEYFILE_SYS is the filename of the system-wide lesskey output file. + * DEF_LESSKEYINFILE is the filename of the default lesskey input + * (in the HOME directory). + * LESSHISTFILE is the filename of the history file + * (in the HOME directory). + */ +#define LESSKEYFILE ".less" +#define LESSKEYFILE_SYS SYSDIR "/sysless" +#define DEF_LESSKEYINFILE ".lesskey" +#define LESSHISTFILE ".lesshst" + + +/* Settings always true on Unix. */ + +/* + * Define MSDOS_COMPILER if compiling under Microsoft C. + */ +#define MSDOS_COMPILER 0 + +/* + * Pathname separator character. + */ +#define PATHNAME_SEP "/" + +/* + * The value returned from tgetent on success. + * Some HP-UX systems return 0 on success. + */ +#define TGETENT_OK 1 + +/* + * HAVE_SYS_TYPES_H is 1 if your system has . + */ +#define HAVE_SYS_TYPES_H 1 + +/* + * Define if you have the header file. + */ +/* #undef HAVE_SGSTAT_H */ + +/* + * HAVE_PERROR is 1 if your system has the perror() call. + * (Actually, if it has sys_errlist, sys_nerr and errno.) + */ +#define HAVE_PERROR 1 + +/* + * HAVE_TIME is 1 if your system has the time() call. + */ +#define HAVE_TIME 1 + +/* + * HAVE_SHELL is 1 if your system supports a SHELL command interpreter. + */ +#define HAVE_SHELL 1 + +/* + * Default shell metacharacters and meta-escape character. + */ +#define DEF_METACHARS "; *?\t\n'\"()<>[]|&^`#\\$%=~" +#define DEF_METAESCAPE "\\" + +/* + * HAVE_DUP is 1 if your system has the dup() call. + */ +#define HAVE_DUP 1 + +/* Define to 1 if you have the memcpy() function. */ +#define HAVE_MEMCPY 1 + +/* Define to 1 if you have the strchr() function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the strstr() function. */ +#define HAVE_STRSTR 1 + +/* + * Sizes of various buffers. + */ +#define CMDBUF_SIZE 512 /* Buffer for multichar commands */ +#define UNGOT_SIZE 100 /* Max chars to unget() */ +#define LINEBUF_SIZE 1024 /* Max size of line in input file */ +#define OUTBUF_SIZE 1024 /* Output buffer */ +#define PROMPT_SIZE 200 /* Max size of prompt string */ +#define TERMBUF_SIZE 2048 /* Termcap buffer for tgetent */ +#define TERMSBUF_SIZE 1024 /* Buffer to hold termcap strings */ +#define TAGLINE_SIZE 512 /* Max size of line in tags file */ +#define TABSTOP_MAX 32 /* Max number of custom tab stops */ + +/* Settings automatically determined by configure. */ + + +/* Define EDIT_PGM to your editor. */ +#define EDIT_PGM "vi" + +/* Define HAVE_CONST if your compiler supports the "const" modifier. */ +#define HAVE_CONST 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_CTYPE_H 1 + +/* Define HAVE_ERRNO if you have the errno variable. */ +#define HAVE_ERRNO 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the `fchmod' function. */ +#define HAVE_FCHMOD 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define HAVE_FILENO if you have the fileno() macro. */ +#define HAVE_FILENO 1 + +/* Define to 1 if you have the `fsync' function. */ +#define HAVE_FSYNC 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `gen' library (-lgen). */ +/* #undef HAVE_LIBGEN */ + +/* Define to 1 if you have the `intl' library (-lintl). */ +/* #undef HAVE_LIBINTL */ + +/* Define to 1 if you have the `PW' library (-lPW). */ +/* #undef HAVE_LIBPW */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define HAVE_LOCALE if you have locale.h and setlocale. */ +#define HAVE_LOCALE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define HAVE_OSPEED if your termcap library has the ospeed variable. */ +#define HAVE_OSPEED 1 + +/* PCRE (Perl-compatible regular expression) library */ +/* #undef HAVE_PCRE */ + +/* Define to 1 if you have the `popen' function. */ +#define HAVE_POPEN 1 + +/* POSIX regcomp() and regex.h */ +#define HAVE_POSIX_REGCOMP 1 + +/* System V regcmp() */ +/* #undef HAVE_REGCMP */ + +/* */ +/* #undef HAVE_REGEXEC2 */ + +/* BSD re_comp() */ +/* #undef HAVE_RE_COMP */ + +/* Define HAVE_SIGEMPTYSET if you have the sigemptyset macro. */ +#define HAVE_SIGEMPTYSET 1 + +/* Define to 1 if you have the `sigprocmask' function. */ +#define HAVE_SIGPROCMASK 1 + +/* Define to 1 if you have the `sigsetmask' function. */ +#define HAVE_SIGSETMASK 1 + +/* Define to 1 if the system has the type `sigset_t'. */ +#define HAVE_SIGSET_T 1 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if you have the `stat' function. */ +#define HAVE_STAT 1 + +/* Define HAVE_STAT_INO if your struct stat has st_ino and st_dev. */ +#define HAVE_STAT_INO 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define HAVE_STRERROR if you have the strerror() function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `system' function. */ +#define HAVE_SYSTEM 1 + +/* Define HAVE_SYS_ERRLIST if you have the sys_errlist[] variable. */ +#define HAVE_SYS_ERRLIST 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_STREAM_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_TERMCAP_H 1 + +/* Define HAVE_TERMIOS_FUNCS if you have tcgetattr/tcsetattr. */ +#define HAVE_TERMIOS_FUNCS 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_TERMIOS_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_TERMIO_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_TIME_H 1 + +/* Define HAVE_TIME_T if your system supports the "time_t" type. */ +#define HAVE_TIME_T 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define HAVE_UPPER_LOWER if you have isupper, islower, toupper, tolower. */ +#define HAVE_UPPER_LOWER 1 + +/* Henry Spencer V8 regcomp() and regexp.h */ +/* #undef HAVE_V8_REGCOMP */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_VALUES_H */ + +/* Define HAVE_VOID if your compiler supports the "void" type. */ +#define HAVE_VOID 1 + +/* Define HAVE_WCTYPE if you have iswupper, iswlower, towupper, towlower. */ +#define HAVE_WCTYPE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_WCTYPE_H 1 + +/* Define to 1 if you have the `_setjmp' function. */ +#define HAVE__SETJMP 1 + +/* Define MUST_DEFINE_ERRNO if you have errno but it is not define in errno.h. + */ +/* #undef MUST_DEFINE_ERRNO */ + +/* Define MUST_DEFINE_OSPEED if you have ospeed but it is not defined in + termcap.h. */ +/* #undef MUST_DEFINE_OSPEED */ + +/* pattern matching is supported, but without metacharacters. */ +/* #undef NO_REGEX */ + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "less" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "less 1" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "less" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1" + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* Define SECURE_COMPILE=1 to build a secure version of less. */ +#define SECURE_COMPILE 0 + +/* Define to 1 if the `S_IS*' macros in do not work properly. */ +/* #undef STAT_MACROS_BROKEN */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `long int' if does not define. */ +/* #undef off_t */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ diff --git a/files/Sources/files/less/defines.h.in b/files/Sources/files/less/defines.h.in new file mode 100644 index 00000000..46232c0e --- /dev/null +++ b/files/Sources/files/less/defines.h.in @@ -0,0 +1,437 @@ +/* defines.h.in. Generated from configure.ac by autoheader. */ + + +/* Unix definition file for less. -*- C -*- + * + * This file has 3 sections: + * User preferences. + * Settings always true on Unix. + * Settings automatically determined by configure. + * + * * * * * * WARNING * * * * * * + * If you edit defines.h by hand, do "touch stamp-h" before you run make + * so config.status doesn't overwrite your changes. + */ + +/* User preferences. */ + +/* + * SECURE is 1 if you wish to disable a bunch of features in order to + * be safe to run by unprivileged users. + * SECURE_COMPILE is set by the --with-secure configure option. + */ +#define SECURE SECURE_COMPILE + +/* + * SHELL_ESCAPE is 1 if you wish to allow shell escapes. + * (This is possible only if your system supplies the system() function.) + */ +#define SHELL_ESCAPE (!SECURE) + +/* + * EXAMINE is 1 if you wish to allow examining files by name from within less. + */ +#define EXAMINE (!SECURE) + +/* + * TAB_COMPLETE_FILENAME is 1 if you wish to allow the TAB key + * to complete filenames at prompts. + */ +#define TAB_COMPLETE_FILENAME (!SECURE) + +/* + * CMD_HISTORY is 1 if you wish to allow keys to cycle through + * previous commands at prompts. + */ +#define CMD_HISTORY 1 + +/* + * HILITE_SEARCH is 1 if you wish to have search targets to be + * displayed in standout mode. + */ +#define HILITE_SEARCH 1 + +/* + * EDITOR is 1 if you wish to allow editor invocation (the "v" command). + * (This is possible only if your system supplies the system() function.) + * EDIT_PGM is the name of the (default) editor to be invoked. + */ +#define EDITOR (!SECURE) + +/* + * TAGS is 1 if you wish to support tag files. + */ +#define TAGS (!SECURE) + +/* + * USERFILE is 1 if you wish to allow a .less file to specify + * user-defined key bindings. + */ +#define USERFILE (!SECURE) + +/* + * GLOB is 1 if you wish to have shell metacharacters expanded in filenames. + * This will generally work if your system provides the "popen" function + * and the "echo" shell command. + */ +#define GLOB (!SECURE) + +/* + * PIPEC is 1 if you wish to have the "|" command + * which allows the user to pipe data into a shell command. + */ +#define PIPEC (!SECURE) + +/* + * LOGFILE is 1 if you wish to allow the -l option (to create log files). + */ +#define LOGFILE (!SECURE) + +/* + * GNU_OPTIONS is 1 if you wish to support the GNU-style command + * line options --help and --version. + */ +#define GNU_OPTIONS 1 + +/* + * ONLY_RETURN is 1 if you want RETURN to be the only input which + * will continue past an error message. + * Otherwise, any key will continue past an error message. + */ +#define ONLY_RETURN 0 + +/* + * LESSKEYFILE is the filename of the default lesskey output file + * (in the HOME directory). + * LESSKEYFILE_SYS is the filename of the system-wide lesskey output file. + * DEF_LESSKEYINFILE is the filename of the default lesskey input + * (in the HOME directory). + * LESSHISTFILE is the filename of the history file + * (in the HOME directory). + */ +#define LESSKEYFILE ".less" +#define LESSKEYFILE_SYS SYSDIR "/sysless" +#define DEF_LESSKEYINFILE ".lesskey" +#define LESSHISTFILE ".lesshst" + + +/* Settings always true on Unix. */ + +/* + * Define MSDOS_COMPILER if compiling under Microsoft C. + */ +#define MSDOS_COMPILER 0 + +/* + * Pathname separator character. + */ +#define PATHNAME_SEP "/" + +/* + * The value returned from tgetent on success. + * Some HP-UX systems return 0 on success. + */ +#define TGETENT_OK 1 + +/* + * HAVE_SYS_TYPES_H is 1 if your system has . + */ +#define HAVE_SYS_TYPES_H 1 + +/* + * Define if you have the header file. + */ +#undef HAVE_SGSTAT_H + +/* + * HAVE_PERROR is 1 if your system has the perror() call. + * (Actually, if it has sys_errlist, sys_nerr and errno.) + */ +#define HAVE_PERROR 1 + +/* + * HAVE_TIME is 1 if your system has the time() call. + */ +#define HAVE_TIME 1 + +/* + * HAVE_SHELL is 1 if your system supports a SHELL command interpreter. + */ +#define HAVE_SHELL 1 + +/* + * Default shell metacharacters and meta-escape character. + */ +#define DEF_METACHARS "; *?\t\n'\"()<>[]|&^`#\\$%=~" +#define DEF_METAESCAPE "\\" + +/* + * HAVE_DUP is 1 if your system has the dup() call. + */ +#define HAVE_DUP 1 + +/* Define to 1 if you have the memcpy() function. */ +#define HAVE_MEMCPY 1 + +/* Define to 1 if you have the strchr() function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the strstr() function. */ +#define HAVE_STRSTR 1 + +/* + * Sizes of various buffers. + */ +#if 0 /* old sizes for small memory machines */ +#define CMDBUF_SIZE 512 /* Buffer for multichar commands */ +#define UNGOT_SIZE 100 /* Max chars to unget() */ +#define LINEBUF_SIZE 1024 /* Max size of line in input file */ +#define OUTBUF_SIZE 1024 /* Output buffer */ +#define PROMPT_SIZE 200 /* Max size of prompt string */ +#define TERMBUF_SIZE 2048 /* Termcap buffer for tgetent */ +#define TERMSBUF_SIZE 1024 /* Buffer to hold termcap strings */ +#define TAGLINE_SIZE 512 /* Max size of line in tags file */ +#define TABSTOP_MAX 32 /* Max number of custom tab stops */ +#else /* more reasonable sizes for modern machines */ +#define CMDBUF_SIZE 2048 /* Buffer for multichar commands */ +#define UNGOT_SIZE 200 /* Max chars to unget() */ +#define LINEBUF_SIZE 1024 /* Initial max size of line in input file */ +#define OUTBUF_SIZE 1024 /* Output buffer */ +#define PROMPT_SIZE 2048 /* Max size of prompt string */ +#define TERMBUF_SIZE 2048 /* Termcap buffer for tgetent */ +#define TERMSBUF_SIZE 1024 /* Buffer to hold termcap strings */ +#define TAGLINE_SIZE 1024 /* Max size of line in tags file */ +#define TABSTOP_MAX 128 /* Max number of custom tab stops */ +#endif + +/* Settings automatically determined by configure. */ + + +/* Define EDIT_PGM to your editor. */ +#undef EDIT_PGM + +/* Define HAVE_CONST if your compiler supports the "const" modifier. */ +#undef HAVE_CONST + +/* Define to 1 if you have the header file. */ +#undef HAVE_CTYPE_H + +/* Define HAVE_ERRNO if you have the errno variable. */ +#undef HAVE_ERRNO + +/* Define to 1 if you have the header file. */ +#undef HAVE_ERRNO_H + +/* Define to 1 if you have the `fchmod' function. */ +#undef HAVE_FCHMOD + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define HAVE_FILENO if you have the fileno() macro. */ +#undef HAVE_FILENO + +/* Define HAVE_FLOAT if your compiler supports the "double" type. */ +#undef HAVE_FLOAT + +/* Define to 1 if you have the `fsync' function. */ +#undef HAVE_FSYNC + +/* GNU regex library */ +#undef HAVE_GNU_REGEX + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIMITS_H + +/* Define HAVE_LOCALE if you have locale.h and setlocale. */ +#undef HAVE_LOCALE + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define HAVE_OSPEED if your termcap library has the ospeed variable. */ +#undef HAVE_OSPEED + +/* PCRE (Perl-compatible regular expression) library */ +#undef HAVE_PCRE + +/* Define to 1 if you have the `popen' function. */ +#undef HAVE_POPEN + +/* POSIX regcomp() and regex.h */ +#undef HAVE_POSIX_REGCOMP + +/* System V regcmp() */ +#undef HAVE_REGCMP + +/* */ +#undef HAVE_REGEXEC2 + +/* BSD re_comp() */ +#undef HAVE_RE_COMP + +/* Define HAVE_SIGEMPTYSET if you have the sigemptyset macro. */ +#undef HAVE_SIGEMPTYSET + +/* Define to 1 if you have the `sigprocmask' function. */ +#undef HAVE_SIGPROCMASK + +/* Define to 1 if you have the `sigsetmask' function. */ +#undef HAVE_SIGSETMASK + +/* Define to 1 if the system has the type `sigset_t'. */ +#undef HAVE_SIGSET_T + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + +/* Define to 1 if you have the `stat' function. */ +#undef HAVE_STAT + +/* Define HAVE_STAT_INO if your struct stat has st_ino and st_dev. */ +#undef HAVE_STAT_INO + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define HAVE_STRERROR if you have the strerror() function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `system' function. */ +#undef HAVE_SYSTEM + +/* Define HAVE_SYS_ERRLIST if you have the sys_errlist[] variable. */ +#undef HAVE_SYS_ERRLIST + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STREAM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_TERMCAP_H + +/* Define HAVE_TERMIOS_FUNCS if you have tcgetattr/tcsetattr. */ +#undef HAVE_TERMIOS_FUNCS + +/* Define to 1 if you have the header file. */ +#undef HAVE_TERMIOS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_TERMIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_TIME_H + +/* Define HAVE_TIME_T if your system supports the "time_t" type. */ +#undef HAVE_TIME_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define HAVE_UPPER_LOWER if you have isupper, islower, toupper, tolower. */ +#undef HAVE_UPPER_LOWER + +/* Henry Spencer V8 regcomp() and regexp.h */ +#undef HAVE_V8_REGCOMP + +/* Define to 1 if you have the header file. */ +#undef HAVE_VALUES_H + +/* Define HAVE_VOID if your compiler supports the "void" type. */ +#undef HAVE_VOID + +/* Define HAVE_WCTYPE if you have iswupper, iswlower, towupper, towlower. */ +#undef HAVE_WCTYPE + +/* Define to 1 if you have the header file. */ +#undef HAVE_WCTYPE_H + +/* Define to 1 if you have the `_setjmp' function. */ +#undef HAVE__SETJMP + +/* Define MUST_DEFINE_ERRNO if you have errno but it is not define in errno.h. + */ +#undef MUST_DEFINE_ERRNO + +/* Define MUST_DEFINE_OSPEED if you have ospeed but it is not defined in + termcap.h. */ +#undef MUST_DEFINE_OSPEED + +/* pattern matching is supported, but without metacharacters. */ +#undef NO_REGEX + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define as the return type of signal handlers (`int' or `void'). */ +#undef RETSIGTYPE + +/* Define SECURE_COMPILE=1 to build a secure version of less. */ +#undef SECURE_COMPILE + +/* Define to 1 if the `S_IS*' macros in do not work properly. */ +#undef STAT_MACROS_BROKEN + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `long int' if does not define. */ +#undef off_t + +/* Define to `unsigned int' if does not define. */ +#undef size_t diff --git a/files/Sources/files/less/defines.o2 b/files/Sources/files/less/defines.o2 new file mode 100644 index 00000000..615686b8 --- /dev/null +++ b/files/Sources/files/less/defines.o2 @@ -0,0 +1,339 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* OS/2 definition file for less. */ +/* + * This file has 2 sections: + * User preferences. + * Settings always true for the emx compiler for OS/2 systems. + */ + + +/* User preferences. */ + +/* + * SECURE is 1 if you wish to disable a bunch of features in order to + * be safe to run by unprivileged users. + */ +#define SECURE 0 + +/* + * SHELL_ESCAPE is 1 if you wish to allow shell escapes. + * (This is possible only if your system supplies the system() function.) + */ +#define SHELL_ESCAPE (!SECURE) + +/* + * EXAMINE is 1 if you wish to allow examining files by name from within less. + */ +#define EXAMINE (!SECURE) + +/* + * TAB_COMPLETE_FILENAME is 1 if you wish to allow the TAB key + * to complete filenames at prompts. + */ +#define TAB_COMPLETE_FILENAME (!SECURE) + +/* + * CMD_HISTORY is 1 if you wish to allow keys to cycle through + * previous commands at prompts. + */ +#define CMD_HISTORY 1 + +/* + * HILITE_SEARCH is 1 if you wish to have search targets to be + * displayed in standout mode. + */ +#define HILITE_SEARCH 1 + +/* + * EDITOR is 1 if you wish to allow editor invocation (the "v" command). + * (This is possible only if your system supplies the system() function.) + * EDIT_PGM is the name of the (default) editor to be invoked. + */ +#define EDITOR (!SECURE) +#define EDIT_PGM "vi" + +/* + * TAGS is 1 if you wish to support tag files. + */ +#define TAGS (!SECURE) + +/* + * USERFILE is 1 if you wish to allow a .less file to specify + * user-defined key bindings. + */ +#define USERFILE (!SECURE) + +/* + * GLOB is 1 if you wish to have shell metacharacters expanded in filenames. + * This will generally work if your system provides the "popen" function + * and the "echo" shell command. + */ +#define GLOB (!SECURE) + +/* + * PIPEC is 1 if you wish to have the "|" command + * which allows the user to pipe data into a shell command. + */ +#define PIPEC (!SECURE) + +/* + * LOGFILE is 1 if you wish to allow the -l option (to create log files). + */ +#define LOGFILE (!SECURE) + +/* + * GNU_OPTIONS is 1 if you wish to support the GNU-style command + * line options --help and --version. + */ +#define GNU_OPTIONS 1 + +/* + * ONLY_RETURN is 1 if you want RETURN to be the only input which + * will continue past an error message. + * Otherwise, any key will continue past an error message. + */ +#define ONLY_RETURN 0 + +/* + * LESSKEYFILE is the filename of the default lesskey output file + * (in the HOME directory). + * LESSKEYFILE_SYS is the filename of the system-wide lesskey output file. + * DEF_LESSKEYINFILE is the filename of the default lesskey input + * (in the HOME directory). + * LESSHISTFILE is the filename of the history file + * (in the HOME directory). + */ +#define LESSKEYFILE "less.ini" +#define LESSKEYFILE_SYS "C:\\sysless.ini" +#define DEF_LESSKEYINFILE "lesskey.ini" +#define LESSHISTFILE "lesshst.ini" + + +/* Settings always true for the emx compiler for OS/2 systems. */ +#define OS2 1 + +/* + * Pathname separator character. + */ +#define PATHNAME_SEP "\\" + +/* + * HAVE_SYS_TYPES_H is 1 if your system has . + */ +#define HAVE_SYS_TYPES_H 1 + +/* + * Define if you have the header file. + */ +#define HAVE_SGSTAT_H 0 + +/* + * HAVE_PERROR is 1 if your system has the perror() call. + * (Actually, if it has sys_errlist, sys_nerr and errno.) + */ +#define HAVE_PERROR 1 + +/* + * HAVE_TIME is 1 if your system has the time() call. + */ +#define HAVE_TIME 1 + +/* + * HAVE_SHELL is 1 if your system supports a SHELL command interpreter. + */ +#define HAVE_SHELL 0 + +/* + * Default shell metacharacters and meta-escape character. + */ +#define DEF_METACHARS "; *?\t\n'\"()<>|&" +#define DEF_METAESCAPE "" + +/* + * HAVE_DUP is 1 if your system has the dup() call. + */ +#define HAVE_DUP 1 + +/* + * Sizes of various buffers. + */ +#if 0 /* old sizes for small memory machines +#define CMDBUF_SIZE 512 /* Buffer for multichar commands */ +#define UNGOT_SIZE 100 /* Max chars to unget() */ +#define LINEBUF_SIZE 1024 /* Max size of line in input file */ +#define OUTBUF_SIZE 1024 /* Output buffer */ +#define PROMPT_SIZE 200 /* Max size of prompt string */ +#define TERMBUF_SIZE 2048 /* Termcap buffer for tgetent */ +#define TERMSBUF_SIZE 1024 /* Buffer to hold termcap strings */ +#define TAGLINE_SIZE 512 /* Max size of line in tags file */ +#define TABSTOP_MAX 32 /* Max number of custom tab stops */ +#else /* more reasonable sizes for modern machines */ +#define CMDBUF_SIZE 2048 /* Buffer for multichar commands */ +#define UNGOT_SIZE 200 /* Max chars to unget() */ +#define LINEBUF_SIZE 1024 /* Initial max size of line in input file */ +#define OUTBUF_SIZE 1024 /* Output buffer */ +#define PROMPT_SIZE 2048 /* Max size of prompt string */ +#define TERMBUF_SIZE 2048 /* Termcap buffer for tgetent */ +#define TERMSBUF_SIZE 1024 /* Buffer to hold termcap strings */ +#define TAGLINE_SIZE 1024 /* Max size of line in tags file */ +#define TABSTOP_MAX 128 /* Max number of custom tab stops */ +#endif + +/* Define to `long' if doesn't define. */ +/* #define off_t long */ + +/* Define if you need to in order for stat and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + + +/* + * Regular expression library. + * Define exactly one of the following to be 1: + * HAVE_POSIX_REGCOMP: POSIX regcomp() and regex.h + * HAVE_RE_COMP: BSD re_comp() + * HAVE_REGCMP: System V regcmp() + * HAVE_V8_REGCOMP: Henry Spencer V8 regcomp() and regexp.h + * NO_REGEX: pattern matching is supported, but without metacharacters. + */ +/* #undef HAVE_POSIX_REGCOMP */ +/* #undef HAVE_RE_COMP */ +/* #undef HAVE_REGCMP */ +#define HAVE_V8_REGCOMP 1 +/* #undef NO_REGEX */ +#define HAVE_REGEXEC2 1 + +/* Define HAVE_VOID if your compiler supports the "void" type. */ +#define HAVE_VOID 1 + +/* Define HAVE_CONST if your compiler supports the "const" modifier. */ +#define HAVE_CONST 1 + +/* Define HAVE_TIME_T if your system supports the "time_t" type. */ +#define HAVE_TIME_T 1 + +/* Define HAVE_STRERROR if you have the strerror() function. */ +#define HAVE_STRERROR 1 + +/* Define HAVE_FILENO if you have the fileno() macro. */ +#define HAVE_FILENO 1 + +/* Define HAVE_ERRNO if you have the errno variable */ +/* Define MUST_DEFINE_ERRNO if you have errno but it is not define + * in errno.h */ +#define HAVE_ERRNO 1 +/* #undef MUST_DEFINE_ERRNO */ + +/* Define HAVE_SYS_ERRLIST if you have the sys_errlist[] variable */ +#define HAVE_SYS_ERRLIST 1 + +/* Define HAVE_OSPEED if your termcap library has the ospeed variable */ +#define HAVE_OSPEED 1 +/* Define MUST_DEFINE_OSPEED if you have ospeed but it is not defined + * in termcap.h. */ +#define MUST_DEFINE_OSPEED 0 + +/* Define HAVE_LOCALE if you have locale.h and setlocale. */ +#define HAVE_LOCALE 1 + +/* Define HAVE_TERMIOS_FUNCS if you have tcgetattr/tcsetattr */ +#define HAVE_TERMIOS_FUNCS 1 + +/* Define HAVE_UPPER_LOWER if you have isupper, islower, toupper, tolower */ +#define HAVE_UPPER_LOWER 1 + +/* Define if you have the _setjmp function. */ +#define HAVE__SETJMP 0 + +/* Define if you have the memcpy function. */ +#define HAVE_MEMCPY 1 + +/* Define if you have the popen function. */ +#define HAVE_POPEN 1 + +/* Define if you have the sigsetmask function. */ +#define HAVE_SIGSETMASK 0 + +/* Define if you have the sigprocmask function. */ +#define HAVE_SIGPROCMASK 1 + +/* Define if you have the sigset_t type and sigemptyset macro */ +#define HAVE_SIGSET_T 1 +#define HAVE_SIGEMPTYSET 1 + +/* Define if you have the stat function. */ +#define HAVE_STAT 1 + +/* Define if you have the strchr function. */ +#define HAVE_STRCHR 1 + +/* Define if you have the strstr function. */ +#define HAVE_STRSTR 1 + +/* Define if you have the system function. */ +#define HAVE_SYSTEM 1 + +/* Define if you have the snprintf function. */ +#define HAVE_SNPRINTF 0 + +/* Define if you have the header file. */ +#define HAVE_CTYPE_H 1 + +/* Define if you have the header file. */ +#define HAVE_WCTYPE_H 0 + +/* Define if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDIO_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_PTEM_H 0 + +/* Define if you have the header file. */ +#define HAVE_SYS_STREAM_H 0 + +/* Define if you have the header file. */ +#define HAVE_TERMCAP_H 1 + +/* Define if you have the header file. */ +#define HAVE_TERMIO_H 1 + +/* Define if you have the header file. */ +#define HAVE_TERMIOS_H 1 + +/* Define if you have the header file. */ +#define HAVE_TIME_H 1 + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if you have the header file. */ +#define HAVE_VALUES_H 0 diff --git a/files/Sources/files/less/defines.o9 b/files/Sources/files/less/defines.o9 new file mode 100644 index 00000000..2e7b9574 --- /dev/null +++ b/files/Sources/files/less/defines.o9 @@ -0,0 +1,351 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* OS/9 definition file for less. */ +/* + * This file has 2 sections: + * User preferences. + * Settings always true for OS-9 systems. + */ + +/* User preferences. */ + +/* + * SECURE is 1 if you wish to disable a bunch of features in order to + * be safe to run by unprivileged users. + */ +#define SECURE 0 + +/* + * SHELL_ESCAPE is 1 if you wish to allow shell escapes. + * (This is possible only if your system supplies the system() function.) + */ +#define SHELL_ESCAPE (!SECURE) + +/* + * EXAMINE is 1 if you wish to allow examining files by name from within less. + */ +#define EXAMINE (!SECURE) + +/* + * TAB_COMPLETE_FILENAME is 1 if you wish to allow the TAB key + * to complete filenames at prompts. + */ +#define TAB_COMPLETE_FILENAME 1 + +/* + * CMD_HISTORY is 1 if you wish to allow keys to cycle through + * previous commands at prompts. + */ +#define CMD_HISTORY 1 + +/* + * HILITE_SEARCH is 1 if you wish to have search targets to be + * displayed in standout mode. + */ +#define HILITE_SEARCH 1 + +/* + * EDITOR is 1 if you wish to allow editor invocation (the "v" command). + * (This is possible only if your system supplies the system() function.) + * EDIT_PGM is the name of the (default) editor to be invoked. + */ +#define EDITOR (!SECURE) +#define EDIT_PGM "umacs" + +/* + * TAGS is 1 if you wish to support tag files. + */ +#define TAGS (!SECURE) + +/* + * USERFILE is 1 if you wish to allow a .less file to specify + * user-defined key bindings. + */ +#define USERFILE (!SECURE) + +/* + * GLOB is 1 if you wish to have shell metacharacters expanded in filenames. + * This will generally work if your system provides the "popen" function + * and the "echo" shell command. + */ +#define GLOB (!SECURE) + +/* + * PIPEC is 1 if you wish to have the "|" command + * which allows the user to pipe data into a shell command. + */ +#define PIPEC (!SECURE) + +/* + * LOGFILE is 1 if you wish to allow the -l option (to create log files). + */ +#define LOGFILE (!SECURE) + +/* + * GNU_OPTIONS is 1 if you wish to support the GNU-style command + * line options --help and --version. + */ +#define GNU_OPTIONS 1 + +/* + * ONLY_RETURN is 1 if you want RETURN to be the only input which + * will continue past an error message. + * Otherwise, any key will continue past an error message. + */ +#define ONLY_RETURN 0 + +/* + * LESSKEYFILE is the filename of the default lesskey output file + * (in the HOME directory). + * LESSKEYFILE_SYS is the filename of the system-wide lesskey output file. + * DEF_LESSKEYINFILE is the filename of the default lesskey input + * (in the HOME directory). + * LESSHISTFILE is the filename of the history file + * (in the HOME directory). + */ +#define LESSKEYFILE ".less" +#define LESSKEYFILE_SYS "/.sysless" +#define DEF_LESSKEYINFILE ".lesskey" +#define LESSHISTFILE ".lesshst" + + +/* Settings always true for OS-9. */ + +/* This is not needed; it is defined by the compiler. */ +/* #define _OSK 1 */ +#define OS2 0 +#define MSDOS_COMPILER 0 + +/* + * Pathname separator character. + */ +#define PATHNAME_SEP "/" + +/* + * HAVE_SYS_TYPES_H is 1 if your system has . + */ +#define HAVE_SYS_TYPES_H 0 + +/* + * Define if you have the header file. + */ +#define HAVE_SGSTAT_H 1 + +/* + * HAVE_PERROR is 1 if your system has the perror() call. + * (Actually, if it has sys_errlist, sys_nerr and errno.) + */ +#if _OSK_MWC32 +#define HAVE_PERROR 0 +#else +#define HAVE_PERROR 1 +#endif + +/* + * HAVE_TIME is 1 if your system has the time() call. + */ +#define HAVE_TIME 1 + +/* + * HAVE_SHELL is 1 if your system supports a SHELL command interpreter. + */ +#define HAVE_SHELL 0 + +/* + * Default shell metacharacters and meta-escape character. + */ +#define DEF_METACHARS "; \t\n'\"()<>|&^`#\\" +#define DEF_METAESCAPE "\\" + +/* + * HAVE_DUP is 1 if your system has the dup() call. + */ +#define HAVE_DUP 0 + +/* + * Sizes of various buffers. + */ +#if 0 /* old sizes for small memory machines +#define CMDBUF_SIZE 512 /* Buffer for multichar commands */ +#define UNGOT_SIZE 100 /* Max chars to unget() */ +#define LINEBUF_SIZE 1024 /* Max size of line in input file */ +#define OUTBUF_SIZE 1024 /* Output buffer */ +#define PROMPT_SIZE 200 /* Max size of prompt string */ +#define TERMBUF_SIZE 2048 /* Termcap buffer for tgetent */ +#define TERMSBUF_SIZE 1024 /* Buffer to hold termcap strings */ +#define TAGLINE_SIZE 512 /* Max size of line in tags file */ +#define TABSTOP_MAX 32 /* Max number of custom tab stops */ +#else /* more reasonable sizes for modern machines */ +#define CMDBUF_SIZE 2048 /* Buffer for multichar commands */ +#define UNGOT_SIZE 200 /* Max chars to unget() */ +#define LINEBUF_SIZE 1024 /* Initial max size of line in input file */ +#define OUTBUF_SIZE 1024 /* Output buffer */ +#define PROMPT_SIZE 2048 /* Max size of prompt string */ +#define TERMBUF_SIZE 2048 /* Termcap buffer for tgetent */ +#define TERMSBUF_SIZE 1024 /* Buffer to hold termcap strings */ +#define TAGLINE_SIZE 1024 /* Max size of line in tags file */ +#define TABSTOP_MAX 128 /* Max number of custom tab stops */ +#endif + +/* Define to `long' if doesn't define. */ +#define off_t long + +/* Define if you need to in order for stat and other things to work. */ +#define _POSIX_SOURCE 0 + +/* Define as the return type of signal handlers (int or void). */ +#if _OSK_MWC32 +#define RETSIGTYPE int +#else +#define RETSIGTYPE void +#endif + + +/* + * Regular expression library. + * Define exactly one of the following to be 1: + * HAVE_POSIX_REGCOMP: POSIX regcomp() and regex.h + * HAVE_RE_COMP: BSD re_comp() + * HAVE_REGCMP: System V regcmp() + * HAVE_V8_REGCOMP: Henry Spencer V8 regcomp() and regexp.h + * NO_REGEX: pattern matching is supported, but without metacharacters. + */ +#define HAVE_POSIX_REGCOMP 0 +#define HAVE_RE_COMP 0 +#define HAVE_REGCMP 0 +#define HAVE_V8_REGCOMP 1 +#define NO_REGEX 0 +#define HAVE_REGEXEC2 1 + +/* Define HAVE_VOID if your compiler supports the "void" type. */ +#define HAVE_VOID 1 + +/* Define HAVE_CONST if your compiler supports the "const" modifier. */ +#define HAVE_CONST 0 + +/* Define HAVE_TIME_T if your system supports the "time_t" type. */ +#define HAVE_TIME_T 1 + +/* Define HAVE_STRERROR if you have the strerror() function. */ +#define HAVE_STRERROR 0 + +/* Define HAVE_FILENO if you have the fileno() macro. */ +#define HAVE_FILENO 1 + +/* Define HAVE_ERRNO if you have the errno variable */ +/* Define MUST_DEFINE_ERRNO if you have errno but it is not define + * in errno.h */ +#define HAVE_ERRNO 1 +#define MUST_DEFINE_ERRNO 0 + +/* Define HAVE_SYS_ERRLIST if you have the sys_errlist[] variable */ +#define HAVE_SYS_ERRLIST 0 + +/* Define HAVE_OSPEED if your termcap library has the ospeed variable */ +/* Define MUST_DEFINE_OSPEED if you have ospeed but it is not defined + * in termcap.h. */ +#define HAVE_OSPEED 0 +#define MUST_DEFINE_OSPEED 0 + +/* Define HAVE_LOCALE if you have locale.h and setlocale. */ +#define HAVE_LOCALE 0 + +/* Define HAVE_TERMIOS_FUNCS if you have tcgetattr/tcsetattr */ +#define HAVE_TERMIOS_FUNCS 0 + +/* Define HAVE_UPPER_LOWER if you have isupper, islower, toupper, tolower */ +#define HAVE_UPPER_LOWER 1 + +/* Define if you have the _setjmp function. */ +#define HAVE__SETJMP 1 + +/* Define if you have the memcpy function. */ +#define HAVE_MEMCPY 1 + +/* Define if you have the popen function. */ +#define HAVE_POPEN 1 + +/* Define if you have the sigsetmask function. */ +#define HAVE_SIGSETMASK 0 + +/* Define if you have the sigprocmask function. */ +#define HAVE_SIGPROCMASK 0 + +/* Define if you have the sigset_t type and sigemptyset macro */ +#define HAVE_SIGSET_T 0 +#define HAVE_SIGEMPTYSET 0 + +/* Define if you have the stat function. */ +#define HAVE_STAT 0 + +/* Define if you have the strchr function. */ +#define HAVE_STRCHR 0 + +/* Define if you have the system function. */ +#define HAVE_SYSTEM 1 + +/* Define if you have the snprintf function. */ +#define HAVE_SNPRINTF 0 + +/* Define if you have the header file. */ +#define HAVE_CTYPE_H 1 + +/* Define if you have the header file. */ +#define HAVE_WCTYPE_H 0 + +/* Define if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 0 + +/* Define if you have the header file. */ +#define HAVE_LIMITS_H 0 + +/* Define if you have the header file. */ +#define HAVE_STDIO_H 1 + +/* Define if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the header file. */ +#if _OSK_MWC32 +#define HAVE_STDLIB_H 0 +#else +#define HAVE_STDLIB_H 1 +#endif + +/* Define if you have the header file. */ +#define HAVE_SYS_IOCTL_H 0 + +/* Define if you have the header file. */ +#define HAVE_SYS_PTEM_H 0 + +/* Define if you have the header file. */ +#define HAVE_SYS_STREAM_H 0 + +/* Define if you have the header file. */ +#define HAVE_TERMCAP_H 1 + +/* Define if you have the header file. */ +#define HAVE_TERMIO_H 0 + +/* Define if you have the header file. */ +#define HAVE_TERMIOS_H 0 + +/* Define if you have the header file. */ +#define HAVE_TIME_H 1 + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H 0 + +/* Define if you have the header file. */ +#define HAVE_VALUES_H 0 diff --git a/files/Sources/files/less/defines.wn b/files/Sources/files/less/defines.wn new file mode 100644 index 00000000..6d6e242d --- /dev/null +++ b/files/Sources/files/less/defines.wn @@ -0,0 +1,352 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* Windows definition file for less. */ +/* + * This file has 2 sections: + * User preferences. + * Settings always true for Windows systems. + */ + + +/* User preferences. */ + +/* + * SECURE is 1 if you wish to disable a bunch of features in order to + * be safe to run by unprivileged users. + */ +#define SECURE 0 + +/* + * SHELL_ESCAPE is 1 if you wish to allow shell escapes. + * (This is possible only if your system supplies the system() function.) + */ +#define SHELL_ESCAPE (!SECURE) + +/* + * EXAMINE is 1 if you wish to allow examining files by name from within less. + */ +#define EXAMINE (!SECURE) + +/* + * TAB_COMPLETE_FILENAME is 1 if you wish to allow the TAB key + * to complete filenames at prompts. + */ +#define TAB_COMPLETE_FILENAME (!SECURE) + +/* + * CMD_HISTORY is 1 if you wish to allow keys to cycle through + * previous commands at prompts. + */ +#define CMD_HISTORY 1 + +/* + * HILITE_SEARCH is 1 if you wish to have search targets to be + * displayed in standout mode. + */ +#define HILITE_SEARCH 1 + +/* + * EDITOR is 1 if you wish to allow editor invocation (the "v" command). + * (This is possible only if your system supplies the system() function.) + * EDIT_PGM is the name of the (default) editor to be invoked. + */ +#define EDITOR (!SECURE) +#define EDIT_PGM "edit" + +/* + * TAGS is 1 if you wish to support tag files. + */ +#define TAGS (!SECURE) + +/* + * USERFILE is 1 if you wish to allow a .less file to specify + * user-defined key bindings. + */ +#define USERFILE (!SECURE) + +/* + * GLOB is 1 if you wish to have shell metacharacters expanded in filenames. + * This will generally work if your system provides the "popen" function + * and the "echo" shell command. + */ +#define GLOB 0 + +/* + * PIPEC is 1 if you wish to have the "|" command + * which allows the user to pipe data into a shell command. + */ +#define PIPEC 1 + +/* + * LOGFILE is 1 if you wish to allow the -l option (to create log files). + */ +#define LOGFILE (!SECURE) + +/* + * GNU_OPTIONS is 1 if you wish to support the GNU-style command + * line options --help and --version. + */ +#define GNU_OPTIONS 1 + +/* + * ONLY_RETURN is 1 if you want RETURN to be the only input which + * will continue past an error message. + * Otherwise, any key will continue past an error message. + */ +#define ONLY_RETURN 0 + +/* + * LESSKEYFILE is the filename of the default lesskey output file + * (in the HOME directory). + * LESSKEYFILE_SYS is the filename of the system-wide lesskey output file. + * DEF_LESSKEYINFILE is the filename of the default lesskey input + * (in the HOME directory). + * LESSHISTFILE is the filename of the history file + * (in the HOME directory). + */ +#define LESSKEYFILE "_less" +#define LESSKEYFILE_SYS "c:\\_sysless" +#define DEF_LESSKEYINFILE "_lesskey" +#define LESSHISTFILE "_lesshst" + + +/* Settings always true for Windows systems. */ + +#define MSDOS_COMPILER WIN32C + +/* + * Pathname separator character. + */ +#define PATHNAME_SEP "\\" + +/* + * HAVE_SYS_TYPES_H is 1 if your system has . + */ +#define HAVE_SYS_TYPES_H 1 + +/* + * Define if you have the header file. + */ +#define HAVE_SGSTAT_H 0 + +/* + * HAVE_PERROR is 1 if your system has the perror() call. + * (Actually, if it has sys_errlist, sys_nerr and errno.) + */ +#define HAVE_PERROR 1 + +/* + * HAVE_TIME is 1 if your system has the time() call. + */ +#define HAVE_TIME 1 + +/* + * HAVE_SHELL is 1 if your system supports a SHELL command interpreter. + */ +#define HAVE_SHELL 0 + +/* + * Default shell metacharacters and meta-escape character. + */ +#define DEF_METACHARS "; *?\t\n'\"()<>|&" +#define DEF_METAESCAPE "" + +/* + * HAVE_DUP is 1 if your system has the dup() call. + */ +#define HAVE_DUP 1 + +/* + * Sizes of various buffers. + */ +#if 0 /* old sizes for small memory machines +#define CMDBUF_SIZE 512 /* Buffer for multichar commands */ +#define UNGOT_SIZE 100 /* Max chars to unget() */ +#define LINEBUF_SIZE 1024 /* Max size of line in input file */ +#define OUTBUF_SIZE 1024 /* Output buffer */ +#define PROMPT_SIZE 200 /* Max size of prompt string */ +#define TERMBUF_SIZE 2048 /* Termcap buffer for tgetent */ +#define TERMSBUF_SIZE 1024 /* Buffer to hold termcap strings */ +#define TAGLINE_SIZE 512 /* Max size of line in tags file */ +#define TABSTOP_MAX 32 /* Max number of custom tab stops */ +#else /* more reasonable sizes for modern machines */ +#define CMDBUF_SIZE 2048 /* Buffer for multichar commands */ +#define UNGOT_SIZE 200 /* Max chars to unget() */ +#define LINEBUF_SIZE 1024 /* Initial max size of line in input file */ +#define OUTBUF_SIZE 1024 /* Output buffer */ +#define PROMPT_SIZE 2048 /* Max size of prompt string */ +#define TERMBUF_SIZE 2048 /* Termcap buffer for tgetent */ +#define TERMSBUF_SIZE 1024 /* Buffer to hold termcap strings */ +#define TAGLINE_SIZE 1024 /* Max size of line in tags file */ +#define TABSTOP_MAX 128 /* Max number of custom tab stops */ +#endif + +/* Define to `long' if doesn't define. */ +/* #define off_t long */ + +/* Define if you need to in order for stat and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + + +/* + * Regular expression library. + * Define exactly one of the following to be 1: + * HAVE_POSIX_REGCOMP: POSIX regcomp() and regex.h + * HAVE_RE_COMP: BSD re_comp() + * HAVE_REGCMP: System V regcmp() + * HAVE_V8_REGCOMP: Henry Spencer V8 regcomp() and regexp.h + * NO_REGEX: pattern matching is supported, but without metacharacters. + */ +/* #undef HAVE_POSIX_REGCOMP */ +/* #undef HAVE_RE_COMP */ +/* #undef HAVE_REGCMP */ +#define HAVE_V8_REGCOMP 1 +/* #undef NO_REGEX */ +#define HAVE_REGEXEC2 1 + +/* Define HAVE_VOID if your compiler supports the "void" type. */ +#define HAVE_VOID 1 + +/* Define HAVE_CONST if your compiler supports the "const" modifier. */ +#define HAVE_CONST 1 + +/* Define HAVE_TIME_T if your system supports the "time_t" type. */ +#define HAVE_TIME_T 1 + +/* Define HAVE_STRERROR if you have the strerror() function. */ +#define HAVE_STRERROR 1 + +/* Define HAVE_FILENO if you have the fileno() macro. */ +#define HAVE_FILENO 1 + +/* Define HAVE_ERRNO if you have the errno variable */ +/* Define MUST_DEFINE_ERRNO if you have errno but it is not define + * in errno.h */ +#define HAVE_ERRNO 1 +#define MUST_DEFINE_ERRNO 1 + +/* Define HAVE_SYS_ERRLIST if you have the sys_errlist[] variable */ +#define HAVE_SYS_ERRLIST 1 + +/* Define HAVE_OSPEED if your termcap library has the ospeed variable */ +#define HAVE_OSPEED 0 +/* Define MUST_DEFINE_OSPEED if you have ospeed but it is not defined + * in termcap.h. */ +#define MUST_DEFINE_OSPEED 0 + +/* Define HAVE_LOCALE if you have locale.h and setlocale. */ +#define HAVE_LOCALE 0 + +/* Define HAVE_TERMIOS_FUNCS if you have tcgetattr/tcsetattr */ +#define HAVE_TERMIOS_FUNCS 0 + +/* Define HAVE_UPPER_LOWER if you have isupper, islower, toupper, tolower */ +#define HAVE_UPPER_LOWER 1 + +/* Define if you have the _setjmp function. */ +#define HAVE__SETJMP 1 + +/* Define if you have the memcpy function. */ +#define HAVE_MEMCPY 1 + +/* Define if you have the popen function. */ +#define HAVE_POPEN 1 + +/* Define if you have the sigsetmask function. */ +#define HAVE_SIGSETMASK 0 + +/* Define if you have the sigprocmask function. */ +#define HAVE_SIGPROCMASK 0 + +/* Define if you have the sigset_t type and sigemptyset macro */ +#define HAVE_SIGSET_T 0 +#define HAVE_SIGEMPTYSET 0 + +/* Define if you have the stat function. */ +#define HAVE_STAT 1 + +/* Define if you have the strchr function. */ +#define HAVE_STRCHR 1 + +/* Define if you have the system function. */ +#define HAVE_SYSTEM 1 + +/* Define if you have the snprintf function. */ +#define HAVE_SNPRINTF 1 + +/* Define if you have the header file. */ +#define HAVE_CTYPE_H 1 + +/* Define if you have the header file. */ +#define HAVE_WCTYPE_H 1 + +/* Define if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define HAVE_FLOAT if your compiler supports the "double" type. */ +#define HAVE_FLOAT 1 + +/* Define if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDIO_H 1 + +/* Define if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_IOCTL_H 0 + +/* Define if you have the header file. */ +#define HAVE_SYS_PTEM_H 0 + +/* Define if you have the header file. */ +#define HAVE_SYS_STREAM_H 0 + +/* Define if you have the header file. */ +#define HAVE_TERMCAP_H 0 + +/* Define if you have the header file. */ +#define HAVE_TERMIO_H 0 + +/* Define if you have the header file. */ +#define HAVE_TERMIOS_H 0 + +/* Define if you have the header file. */ +#define HAVE_TIME_H 1 + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H 0 + +/* Define if you have the header file. */ +#ifdef _MSC_VER +#define HAVE_VALUES_H 0 +#else +#define HAVE_VALUES_H 1 +#endif + +#define popen _popen +#define pclose _pclose +#if !defined(_MSC_VER) || (_MSC_VER < 1900) +#define snprintf _snprintf +#endif + +#pragma warning(disable:4996) diff --git a/files/Sources/files/less/edit.c b/files/Sources/files/less/edit.c new file mode 100644 index 00000000..5eec3b5f --- /dev/null +++ b/files/Sources/files/less/edit.c @@ -0,0 +1,838 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +#include "less.h" +#if HAVE_STAT +#include +#endif + +public int fd0 = 0; + +extern int new_file; +extern int errmsgs; +extern int cbufs; +extern char *every_first_cmd; +extern int any_display; +extern int force_open; +extern int is_tty; +extern int sigs; +extern IFILE curr_ifile; +extern IFILE old_ifile; +extern struct scrpos initial_scrpos; +extern void constant *ml_examine; +#if SPACES_IN_FILENAMES +extern char openquote; +extern char closequote; +#endif + +extern int file_errors; +extern int unix2003_compat; +extern char * active_dashp_command; +extern char * dashp_commands; + +#if LOGFILE +extern int logfile; +extern int force_logfile; +extern char *namelogfile; +#endif + +#if HAVE_STAT_INO +public dev_t curr_dev; +public ino_t curr_ino; +#endif + +char *curr_altfilename = NULL; +static void *curr_altpipe; + + +/* + * Textlist functions deal with a list of words separated by spaces. + * init_textlist sets up a textlist structure. + * forw_textlist uses that structure to iterate thru the list of + * words, returning each one as a standard null-terminated string. + * back_textlist does the same, but runs thru the list backwards. + */ + public void +init_textlist(tlist, str) + struct textlist *tlist; + char *str; +{ + char *s; +#if SPACES_IN_FILENAMES + int meta_quoted = 0; + int delim_quoted = 0; + char *esc = get_meta_escape(); + int esclen = (int) strlen(esc); +#endif + + tlist->string = skipsp(str); + tlist->endstring = tlist->string + strlen(tlist->string); + for (s = str; s < tlist->endstring; s++) + { +#if SPACES_IN_FILENAMES + if (meta_quoted) + { + meta_quoted = 0; + } else if (esclen > 0 && s + esclen < tlist->endstring && + strncmp(s, esc, esclen) == 0) + { + meta_quoted = 1; + s += esclen - 1; + } else if (delim_quoted) + { + if (*s == closequote) + delim_quoted = 0; + } else /* (!delim_quoted) */ + { + if (*s == openquote) + delim_quoted = 1; + else if (*s == ' ') + *s = '\0'; + } +#else + if (*s == ' ') + *s = '\0'; +#endif + } +} + + public char * +forw_textlist(tlist, prev) + struct textlist *tlist; + char *prev; +{ + char *s; + + /* + * prev == NULL means return the first word in the list. + * Otherwise, return the word after "prev". + */ + if (prev == NULL) + s = tlist->string; + else + s = prev + strlen(prev); + if (s >= tlist->endstring) + return (NULL); + while (*s == '\0') + s++; + if (s >= tlist->endstring) + return (NULL); + return (s); +} + + public char * +back_textlist(tlist, prev) + struct textlist *tlist; + char *prev; +{ + char *s; + + /* + * prev == NULL means return the last word in the list. + * Otherwise, return the word before "prev". + */ + if (prev == NULL) + s = tlist->endstring; + else if (prev <= tlist->string) + return (NULL); + else + s = prev - 1; + while (*s == '\0') + s--; + if (s <= tlist->string) + return (NULL); + while (s[-1] != '\0' && s > tlist->string) + s--; + return (s); +} + +/* + * Close the current input file. + */ + static void +close_file() +{ + struct scrpos scrpos; + + if (curr_ifile == NULL_IFILE) + return; + + /* + * Save the current position so that we can return to + * the same position if we edit this file again. + */ + get_scrpos(&scrpos); + if (scrpos.pos != NULL_POSITION) + { + store_pos(curr_ifile, &scrpos); + lastmark(); + } + /* + * Close the file descriptor, unless it is a pipe. + */ + ch_close(); + /* + * If we opened a file using an alternate name, + * do special stuff to close it. + */ + if (curr_altfilename != NULL) + { + close_altfile(curr_altfilename, get_filename(curr_ifile), + curr_altpipe); + free(curr_altfilename); + curr_altfilename = NULL; + } + curr_ifile = NULL_IFILE; +#if HAVE_STAT_INO + curr_ino = curr_dev = 0; +#endif +} + +/* + * Edit a new file (given its name). + * Filename == "-" means standard input. + * Filename == NULL means just close the current file. + */ + public int +edit(filename) + char *filename; +{ + if (filename == NULL) + return (edit_ifile(NULL_IFILE)); + return (edit_ifile(get_ifile(filename, curr_ifile))); +} + +/* + * Edit a new file (given its IFILE). + * ifile == NULL means just close the current file. + */ + public int +edit_ifile(ifile) + IFILE ifile; +{ + int f; + int answer; + int no_display; + int chflags; + char *filename; + char *open_filename; + char *qopen_filename; + char *alt_filename; + void *alt_pipe; + IFILE was_curr_ifile; + PARG parg; + + if (ifile == curr_ifile) + { + /* + * Already have the correct file open. + */ + return (0); + } + + /* + * We must close the currently open file now. + * This is necessary to make the open_altfile/close_altfile pairs + * nest properly (or rather to avoid nesting at all). + * {{ Some stupid implementations of popen() mess up if you do: + * fA = popen("A"); fB = popen("B"); pclose(fA); pclose(fB); }} + */ +#if LOGFILE + end_logfile(); +#endif + was_curr_ifile = save_curr_ifile(); + if (curr_ifile != NULL_IFILE) + { + chflags = ch_getflags(); + close_file(); + if ((chflags & CH_HELPFILE) && held_ifile(was_curr_ifile) <= 1) + { + /* + * Don't keep the help file in the ifile list. + */ + del_ifile(was_curr_ifile); + was_curr_ifile = old_ifile; + } + } + + if (ifile == NULL_IFILE) + { + /* + * No new file to open. + * (Don't set old_ifile, because if you call edit_ifile(NULL), + * you're supposed to have saved curr_ifile yourself, + * and you'll restore it if necessary.) + */ + unsave_ifile(was_curr_ifile); + return (0); + } + + filename = save(get_filename(ifile)); + /* + * See if LESSOPEN specifies an "alternate" file to open. + */ + alt_pipe = NULL; + alt_filename = open_altfile(filename, &f, &alt_pipe); + open_filename = (alt_filename != NULL) ? alt_filename : filename; + qopen_filename = shell_unquote(open_filename); + + chflags = 0; + if (alt_pipe != NULL) + { + /* + * The alternate "file" is actually a pipe. + * f has already been set to the file descriptor of the pipe + * in the call to open_altfile above. + * Keep the file descriptor open because it was opened + * via popen(), and pclose() wants to close it. + */ + chflags |= CH_POPENED; + } else if (strcmp(open_filename, "-") == 0) + { + /* + * Use standard input. + * Keep the file descriptor open because we can't reopen it. + */ + f = fd0; + chflags |= CH_KEEPOPEN; + /* + * Must switch stdin to BINARY mode. + */ + SET_BINARY(f); +#if MSDOS_COMPILER==DJGPPC + /* + * Setting stdin to binary by default causes + * Ctrl-C to not raise SIGINT. We must undo + * that side-effect. + */ + __djgpp_set_ctrl_c(1); +#endif + } else if (strcmp(open_filename, FAKE_EMPTYFILE) == 0) + { + f = -1; + chflags |= CH_NODATA; + } else if (strcmp(open_filename, FAKE_HELPFILE) == 0) + { + f = -1; + chflags |= CH_HELPFILE; + } else if ((parg.p_string = bad_file(open_filename)) != NULL) + { + /* + * It looks like a bad file. Don't try to open it. + */ + error("%s", &parg); + free(parg.p_string); + err1: + if (alt_filename != NULL) + { + close_altfile(alt_filename, filename, alt_pipe); + free(alt_filename); + } + del_ifile(ifile); + free(qopen_filename); + free(filename); + /* + * Re-open the current file. + */ + if (was_curr_ifile == ifile) + { + /* + * Whoops. The "current" ifile is the one we just deleted. + * Just give up. + */ + quit(QUIT_ERROR); + } + reedit_ifile(was_curr_ifile); + return (1); + } else if ((f = open(qopen_filename, OPEN_READ)) < 0) + { + /* + * Got an error trying to open it. + */ + parg.p_string = errno_message(filename); + error("%s", &parg); + free(parg.p_string); + goto err1; + } else + { + chflags |= CH_CANSEEK; + if (!force_open && !opened(ifile) && bin_file(f)) + { + /* + * Looks like a binary file. + * Ask user if we should proceed. + */ + parg.p_string = filename; + answer = query("\"%s\" may be a binary file. See it anyway? ", + &parg); + if (answer != 'y' && answer != 'Y') + { + close(f); + goto err1; + } + } + } + + /* + * Get the new ifile. + * Get the saved position for the file. + */ + if (was_curr_ifile != NULL_IFILE) + { + old_ifile = was_curr_ifile; + unsave_ifile(was_curr_ifile); + } + curr_ifile = ifile; + curr_altfilename = alt_filename; + curr_altpipe = alt_pipe; + set_open(curr_ifile); /* File has been opened */ + if (unix2003_compat) { + /* support the -p command line option */ + if (dashp_commands) { + active_dashp_command = dashp_commands; + } + } + get_pos(curr_ifile, &initial_scrpos); + new_file = TRUE; + ch_init(f, chflags); + + if (!(chflags & CH_HELPFILE)) + { +#if LOGFILE + if (namelogfile != NULL && is_tty) + use_logfile(namelogfile); +#endif +#if HAVE_STAT_INO + /* Remember the i-number and device of the opened file. */ + { + struct stat statbuf; + int r = stat(qopen_filename, &statbuf); + if (r == 0) + { + curr_ino = statbuf.st_ino; + curr_dev = statbuf.st_dev; + } + } +#endif + if (every_first_cmd != NULL) + { + ungetcc(CHAR_END_COMMAND); + ungetsc(every_first_cmd); + } + } + + free(qopen_filename); + no_display = !any_display; + flush(); + any_display = TRUE; + + if (is_tty) + { + /* + * Output is to a real tty. + */ + + /* + * Indicate there is nothing displayed yet. + */ + pos_clear(); + clr_linenum(); +#if HILITE_SEARCH + clr_hilite(); +#endif + if (strcmp(filename, FAKE_HELPFILE) && strcmp(filename, FAKE_EMPTYFILE)) + cmd_addhist(ml_examine, filename, 1); + if (no_display && errmsgs > 0) + { + /* + * We displayed some messages on error output + * (file descriptor 2; see error() function). + * Before erasing the screen contents, + * display the file name and wait for a keystroke. + */ + parg.p_string = filename; + error("%s", &parg); + } + } + free(filename); + return (0); +} + +/* + * Edit a space-separated list of files. + * For each filename in the list, enter it into the ifile list. + * Then edit the first one. + */ + public int +edit_list(filelist) + char *filelist; +{ + IFILE save_ifile; + char *good_filename; + char *filename; + char *gfilelist; + char *gfilename; + struct textlist tl_files; + struct textlist tl_gfiles; + + save_ifile = save_curr_ifile(); + good_filename = NULL; + + /* + * Run thru each filename in the list. + * Try to glob the filename. + * If it doesn't expand, just try to open the filename. + * If it does expand, try to open each name in that list. + */ + init_textlist(&tl_files, filelist); + filename = NULL; + while ((filename = forw_textlist(&tl_files, filename)) != NULL) + { + gfilelist = lglob(filename); + init_textlist(&tl_gfiles, gfilelist); + gfilename = NULL; + while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL) + { + if (edit(gfilename) == 0 && good_filename == NULL) + good_filename = get_filename(curr_ifile); + } + free(gfilelist); + } + /* + * Edit the first valid filename in the list. + */ + if (good_filename == NULL) + { + unsave_ifile(save_ifile); + return (1); + } + if (get_ifile(good_filename, curr_ifile) == curr_ifile) + { + /* + * Trying to edit the current file; don't reopen it. + */ + unsave_ifile(save_ifile); + return (0); + } + reedit_ifile(save_ifile); + return (edit(good_filename)); +} + +/* + * Edit the first file in the command line (ifile) list. + */ + public int +edit_first() +{ + curr_ifile = NULL_IFILE; + return (edit_next(1)); +} + +/* + * Edit the last file in the command line (ifile) list. + */ + public int +edit_last() +{ + curr_ifile = NULL_IFILE; + return (edit_prev(1)); +} + + +/* + * Edit the n-th next or previous file in the command line (ifile) list. + */ + static int +edit_istep(h, n, dir) + IFILE h; + int n; + int dir; +{ + IFILE next; + + /* + * Skip n filenames, then try to edit each filename. + */ + for (;;) + { + next = (dir > 0) ? next_ifile(h) : prev_ifile(h); + if (--n < 0) + { + if (edit_ifile(h) == 0) + break; + file_errors++; + } + if (next == NULL_IFILE) + { + /* + * Reached end of the ifile list. + */ + return (1); + } + if (ABORT_SIGS()) + { + /* + * Interrupt breaks out, if we're in a long + * list of files that can't be opened. + */ + return (1); + } + h = next; + } + /* + * Found a file that we can edit. + */ + return (0); +} + + static int +edit_inext(h, n) + IFILE h; + int n; +{ + return (edit_istep(h, n, +1)); +} + + public int +edit_next(n) + int n; +{ + return edit_istep(curr_ifile, n, +1); +} + + static int +edit_iprev(h, n) + IFILE h; + int n; +{ + return (edit_istep(h, n, -1)); +} + + public int +edit_prev(n) + int n; +{ + return edit_istep(curr_ifile, n, -1); +} + +/* + * Edit a specific file in the command line (ifile) list. + */ + public int +edit_index(n) + int n; +{ + IFILE h; + + h = NULL_IFILE; + do + { + if ((h = next_ifile(h)) == NULL_IFILE) + { + /* + * Reached end of the list without finding it. + */ + return (1); + } + } while (get_index(h) != n); + + return (edit_ifile(h)); +} + + public IFILE +save_curr_ifile() +{ + if (curr_ifile != NULL_IFILE) + hold_ifile(curr_ifile, 1); + return (curr_ifile); +} + + public void +unsave_ifile(save_ifile) + IFILE save_ifile; +{ + if (save_ifile != NULL_IFILE) + hold_ifile(save_ifile, -1); +} + +/* + * Reedit the ifile which was previously open. + */ + public void +reedit_ifile(save_ifile) + IFILE save_ifile; +{ + IFILE next; + IFILE prev; + + /* + * Try to reopen the ifile. + * Note that opening it may fail (maybe the file was removed), + * in which case the ifile will be deleted from the list. + * So save the next and prev ifiles first. + */ + unsave_ifile(save_ifile); + next = next_ifile(save_ifile); + prev = prev_ifile(save_ifile); + if (edit_ifile(save_ifile) == 0) + return; + /* + * If can't reopen it, open the next input file in the list. + */ + if (next != NULL_IFILE && edit_inext(next, 0) == 0) + return; + /* + * If can't open THAT one, open the previous input file in the list. + */ + if (prev != NULL_IFILE && edit_iprev(prev, 0) == 0) + return; + /* + * If can't even open that, we're stuck. Just quit. + */ + quit(QUIT_ERROR); +} + + public void +reopen_curr_ifile() +{ + IFILE save_ifile = save_curr_ifile(); + close_file(); + reedit_ifile(save_ifile); +} + +/* + * Edit standard input. + */ + public int +edit_stdin() +{ + if (ios_isatty(fd0)) + { + error("Missing filename (\"less --help\" for help)", NULL_PARG); + quit(QUIT_OK); + } + return (edit("-")); +} + +/* + * Copy a file directly to standard output. + * Used if standard output is not a tty. + */ + public void +cat_file() +{ + register int c; + + while ((c = ch_forw_get()) != EOI) + putchr(c); + flush(); +} + +#if LOGFILE + +/* + * If the user asked for a log file and our input file + * is standard input, create the log file. + * We take care not to blindly overwrite an existing file. + */ + public void +use_logfile(filename) + char *filename; +{ + register int exists; + register int answer; + PARG parg; + + if (ch_getflags() & CH_CANSEEK) + /* + * Can't currently use a log file on a file that can seek. + */ + return; + + /* + * {{ We could use access() here. }} + */ + filename = shell_unquote(filename); + exists = open(filename, OPEN_READ); + if (exists >= 0) + close(exists); + exists = (exists >= 0); + + /* + * Decide whether to overwrite the log file or append to it. + * If it doesn't exist we "overwrite" it. + */ + if (!exists || force_logfile) + { + /* + * Overwrite (or create) the log file. + */ + answer = 'O'; + } else + { + /* + * Ask user what to do. + */ + parg.p_string = filename; + answer = query("Warning: \"%s\" exists; Overwrite, Append or Don't log? ", &parg); + } + +loop: + switch (answer) + { + case 'O': case 'o': + /* + * Overwrite: create the file. + */ + logfile = creat(filename, 0644); + break; + case 'A': case 'a': + /* + * Append: open the file and seek to the end. + */ + logfile = open(filename, OPEN_APPEND); + if (lseek(logfile, (off_t)0, SEEK_END) == BAD_LSEEK) + { + close(logfile); + logfile = -1; + } + break; + case 'D': case 'd': + /* + * Don't do anything. + */ + free(filename); + return; + case 'q': + quit(QUIT_OK); + /*NOTREACHED*/ + default: + /* + * Eh? + */ + answer = query("Overwrite, Append, or Don't log? (Type \"O\", \"A\", \"D\" or \"q\") ", NULL_PARG); + goto loop; + } + + if (logfile < 0) + { + /* + * Error in opening logfile. + */ + parg.p_string = filename; + error("Cannot write to \"%s\"", &parg); + free(filename); + return; + } + free(filename); + SET_BINARY(logfile); +} + +#endif diff --git a/files/Sources/files/less/filename.c b/files/Sources/files/less/filename.c new file mode 100644 index 00000000..4c0dc633 --- /dev/null +++ b/files/Sources/files/less/filename.c @@ -0,0 +1,1135 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Routines to mess around with filenames (and files). + * Much of this is very OS dependent. + */ + +#include "less.h" +#include "lglob.h" +#if MSDOS_COMPILER +#include +#if MSDOS_COMPILER==WIN32C && !defined(_MSC_VER) +#include +#endif +#if MSDOS_COMPILER==DJGPPC +#include +#include +#define _MAX_PATH PATH_MAX +#endif +#endif +#ifdef _OSK +#include +#ifndef _OSK_MWC32 +#include +#endif +#endif +#if OS2 +#include +#endif + +#if HAVE_STAT +#include +#ifndef S_ISDIR +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#ifndef S_ISREG +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif +#endif + + +extern int force_open; +extern int secure; +extern int use_lessopen; +extern int ctldisp; +extern int utf_mode; +extern IFILE curr_ifile; +extern IFILE old_ifile; +#if SPACES_IN_FILENAMES +extern char openquote; +extern char closequote; +#endif + +/* + * Remove quotes around a filename. + */ + public char * +shell_unquote(str) + char *str; +{ + char *name; + char *p; + + name = p = (char *) ecalloc(strlen(str)+1, sizeof(char)); + if (*str == openquote) + { + str++; + while (*str != '\0') + { + if (*str == closequote) + { + if (str[1] != closequote) + break; + str++; + } + *p++ = *str++; + } + } else + { + char *esc = get_meta_escape(); + int esclen = (int) strlen(esc); + while (*str != '\0') + { + if (esclen > 0 && strncmp(str, esc, esclen) == 0) + str += esclen; + *p++ = *str++; + } + } + *p = '\0'; + return (name); +} + +/* + * Get the shell's escape character. + */ + public char * +get_meta_escape() +{ + char *s; + + s = lgetenv("LESSMETAESCAPE"); + if (s == NULL) + s = DEF_METAESCAPE; + return (s); +} + +/* + * Get the characters which the shell considers to be "metacharacters". + */ + static char * +metachars() +{ + static char *mchars = NULL; + + if (mchars == NULL) + { + mchars = lgetenv("LESSMETACHARS"); + if (mchars == NULL) + mchars = DEF_METACHARS; + } + return (mchars); +} + +/* + * Is this a shell metacharacter? + */ + static int +metachar(c) + char c; +{ + return (strchr(metachars(), c) != NULL); +} + +/* + * Insert a backslash before each metacharacter in a string. + */ + public char * +shell_quote(s) + char *s; +{ + char *p; + char *newstr; + int len; + char *esc = get_meta_escape(); + int esclen = (int) strlen(esc); + int use_quotes = 0; + int have_quotes = 0; + + /* + * Determine how big a string we need to allocate. + */ + len = 1; /* Trailing null byte */ + for (p = s; *p != '\0'; p++) + { + len++; + if (*p == openquote || *p == closequote) + have_quotes = 1; + if (metachar(*p)) + { + if (esclen == 0) + { + /* + * We've got a metachar, but this shell + * doesn't support escape chars. Use quotes. + */ + use_quotes = 1; + } else + { + /* + * Allow space for the escape char. + */ + len += esclen; + } + } + } + if (use_quotes) + { + if (have_quotes) + /* + * We can't quote a string that contains quotes. + */ + return (NULL); + len = (int) strlen(s) + 3; + } + /* + * Allocate and construct the new string. + */ + newstr = p = (char *) ecalloc(len, sizeof(char)); + if (use_quotes) + { + SNPRINTF3(newstr, len, "%c%s%c", openquote, s, closequote); + } else + { + while (*s != '\0') + { + if (metachar(*s)) + { + /* + * Add the escape char. + */ + strcpy(p, esc); + p += esclen; + } + *p++ = *s++; + } + *p = '\0'; + } + return (newstr); +} + +/* + * Return a pathname that points to a specified file in a specified directory. + * Return NULL if the file does not exist in the directory. + */ + static char * +dirfile(dirname, filename) + char *dirname; + char *filename; +{ + char *pathname; + char *qpathname; + int len; + int f; + + if (dirname == NULL || *dirname == '\0') + return (NULL); + /* + * Construct the full pathname. + */ + len = (int) (strlen(dirname) + strlen(filename) + 2); + pathname = (char *) calloc(len, sizeof(char)); + if (pathname == NULL) + return (NULL); + SNPRINTF3(pathname, len, "%s%s%s", dirname, PATHNAME_SEP, filename); + /* + * Make sure the file exists. + */ + qpathname = shell_unquote(pathname); + f = open(qpathname, OPEN_READ); + if (f < 0) + { + free(pathname); + pathname = NULL; + } else + { + close(f); + } + free(qpathname); + return (pathname); +} + +/* + * Return the full pathname of the given file in the "home directory". + */ + public char * +homefile(filename) + char *filename; +{ + register char *pathname; + + /* + * Try $HOME/filename. + */ + // iOS: HOME is not read-write accessible + char homeiOS[PATH_MAX]; + sprintf(homeiOS, "%s/Documents", lgetenv("HOME")); + pathname = dirfile(homeiOS, filename); + // pathname = dirfile(lgetenv("HOME"), filename); + if (pathname != NULL) + return (pathname); +#if OS2 + /* + * Try $INIT/filename. + */ + pathname = dirfile(lgetenv("INIT"), filename); + if (pathname != NULL) + return (pathname); +#endif +#if MSDOS_COMPILER || OS2 + /* + * Look for the file anywhere on search path. + */ + pathname = (char *) calloc(_MAX_PATH, sizeof(char)); +#if MSDOS_COMPILER==DJGPPC + { + char *res = searchpath(filename); + if (res == 0) + *pathname = '\0'; + else + strcpy(pathname, res); + } +#else + _searchenv(filename, "PATH", pathname); +#endif + if (*pathname != '\0') + return (pathname); + free(pathname); +#endif + return (NULL); +} + +/* + * Expand a string, substituting any "%" with the current filename, + * and any "#" with the previous filename. + * But a string of N "%"s is just replaced with N-1 "%"s. + * Likewise for a string of N "#"s. + * {{ This is a lot of work just to support % and #. }} + */ + public char * +fexpand(s) + char *s; +{ + register char *fr, *to; + register int n; + register char *e; + IFILE ifile; + +#define fchar_ifile(c) \ + ((c) == '%' ? curr_ifile : \ + (c) == '#' ? old_ifile : NULL_IFILE) + + /* + * Make one pass to see how big a buffer we + * need to allocate for the expanded string. + */ + n = 0; + for (fr = s; *fr != '\0'; fr++) + { + switch (*fr) + { + case '%': + case '#': + if (fr > s && fr[-1] == *fr) + { + /* + * Second (or later) char in a string + * of identical chars. Treat as normal. + */ + n++; + } else if (fr[1] != *fr) + { + /* + * Single char (not repeated). Treat specially. + */ + ifile = fchar_ifile(*fr); + if (ifile == NULL_IFILE) + n++; + else + n += (int) strlen(get_filename(ifile)); + } + /* + * Else it is the first char in a string of + * identical chars. Just discard it. + */ + break; + default: + n++; + break; + } + } + + e = (char *) ecalloc(n+1, sizeof(char)); + + /* + * Now copy the string, expanding any "%" or "#". + */ + to = e; + for (fr = s; *fr != '\0'; fr++) + { + switch (*fr) + { + case '%': + case '#': + if (fr > s && fr[-1] == *fr) + { + *to++ = *fr; + } else if (fr[1] != *fr) + { + ifile = fchar_ifile(*fr); + if (ifile == NULL_IFILE) + *to++ = *fr; + else + { + strcpy(to, get_filename(ifile)); + to += strlen(to); + } + } + break; + default: + *to++ = *fr; + break; + } + } + *to = '\0'; + return (e); +} + + +#if TAB_COMPLETE_FILENAME + +/* + * Return a blank-separated list of filenames which "complete" + * the given string. + */ + public char * +fcomplete(s) + char *s; +{ + char *fpat; + char *qs; + + if (secure) + return (NULL); + /* + * Complete the filename "s" by globbing "s*". + */ +#if MSDOS_COMPILER && (MSDOS_COMPILER == MSOFTC || MSDOS_COMPILER == BORLANDC) + /* + * But in DOS, we have to glob "s*.*". + * But if the final component of the filename already has + * a dot in it, just do "s*". + * (Thus, "FILE" is globbed as "FILE*.*", + * but "FILE.A" is globbed as "FILE.A*"). + */ + { + char *slash; + int len; + for (slash = s+strlen(s)-1; slash > s; slash--) + if (*slash == *PATHNAME_SEP || *slash == '/') + break; + len = (int) strlen(s) + 4; + fpat = (char *) ecalloc(len, sizeof(char)); + if (strchr(slash, '.') == NULL) + SNPRINTF1(fpat, len, "%s*.*", s); + else + SNPRINTF1(fpat, len, "%s*", s); + } +#else + { + int len = (int) strlen(s) + 2; + fpat = (char *) ecalloc(len, sizeof(char)); + SNPRINTF1(fpat, len, "%s*", s); + } +#endif + qs = lglob(fpat); + s = shell_unquote(qs); + if (strcmp(s,fpat) == 0) + { + /* + * The filename didn't expand. + */ + free(qs); + qs = NULL; + } + free(s); + free(fpat); + return (qs); +} +#endif + +/* + * Try to determine if a file is "binary". + * This is just a guess, and we need not try too hard to make it accurate. + */ + public int +bin_file(f) + int f; +{ + int n; + int bin_count = 0; + char data[256]; + char* p; + char* pend; + + if (!seekable(f)) + return (0); + if (lseek(f, (off_t)0, SEEK_SET) == BAD_LSEEK) + return (0); + n = read(f, data, sizeof(data)); + if (n <= 0) + return (0); + if (utf_mode) + { + bin_count = utf_bin_count(data, n); + } else + { + pend = &data[n]; + for (p = data; p < pend; ) + { + LWCHAR c = step_char(&p, +1, pend); + if (ctldisp == OPT_ONPLUS && IS_CSI_START(c)) + { + do { + c = step_char(&p, +1, pend); + } while (p < pend && is_ansi_middle(c)); + } else if (binary_char(c)) + bin_count++; + } + } + /* + * Call it a binary file if there are more than 5 binary characters + * in the first 256 bytes of the file. + */ + return (bin_count > 5); +} + +/* + * Try to determine the size of a file by seeking to the end. + */ + static POSITION +seek_filesize(f) + int f; +{ + off_t spos; + + spos = lseek(f, (off_t)0, SEEK_END); + if (spos == BAD_LSEEK) + return (NULL_POSITION); + return ((POSITION) spos); +} + +/* + * Read a string from a file. + * Return a pointer to the string in memory. + */ + static char * +readfd(fd) + FILE *fd; +{ + int len; + int ch; + char *buf; + char *p; + + /* + * Make a guess about how many chars in the string + * and allocate a buffer to hold it. + */ + len = 100; + buf = (char *) ecalloc(len, sizeof(char)); + for (p = buf; ; p++) + { + if ((ch = getc(fd)) == '\n' || ch == EOF) + break; + if (p - buf >= len-1) + { + /* + * The string is too big to fit in the buffer we have. + * Allocate a new buffer, twice as big. + */ + len *= 2; + *p = '\0'; + p = (char *) ecalloc(len, sizeof(char)); + strcpy(p, buf); + free(buf); + buf = p; + p = buf + strlen(buf); + } + *p = ch; + } + *p = '\0'; + return (buf); +} + + + +#if HAVE_POPEN + +FILE *popen(); + +/* + * Execute a shell command. + * Return a pointer to a pipe connected to the shell command's standard output. + */ + static FILE * +shellcmd(cmd) + char *cmd; +{ + FILE *fd; + +#if HAVE_SHELL + char *shell; + + shell = lgetenv("SHELL"); + if (shell != NULL && *shell != '\0') + { + char *scmd; + char *esccmd; + + /* + * Read the output of <$SHELL -c cmd>. + * Escape any metacharacters in the command. + */ + esccmd = shell_quote(cmd); + if (esccmd == NULL) + { + fd = popen(cmd, "r"); + } else + { + int len = (int) (strlen(shell) + strlen(esccmd) + 5); + scmd = (char *) ecalloc(len, sizeof(char)); + SNPRINTF3(scmd, len, "%s %s %s", shell, shell_coption(), esccmd); + free(esccmd); + fd = popen(scmd, "r"); + free(scmd); + } + } else +#endif + { + fd = popen(cmd, "r"); + } + /* + * Redirection in `popen' might have messed with the + * standard devices. Restore binary input mode. + */ + SET_BINARY(0); + return (fd); +} + +#endif /* HAVE_POPEN */ + + +/* + * Expand a filename, doing any system-specific metacharacter substitutions. + */ + public char * +lglob(filename) + char *filename; +{ + char *gfilename; + char *ofilename; + + ofilename = fexpand(filename); + if (secure) + return (ofilename); + filename = shell_unquote(ofilename); + +#ifdef DECL_GLOB_LIST +{ + /* + * The globbing function returns a list of names. + */ + int length; + char *p; + char *qfilename; + DECL_GLOB_LIST(list) + + GLOB_LIST(filename, list); + if (GLOB_LIST_FAILED(list)) + { + free(filename); + return (ofilename); + } + length = 1; /* Room for trailing null byte */ + for (SCAN_GLOB_LIST(list, p)) + { + INIT_GLOB_LIST(list, p); + qfilename = shell_quote(p); + if (qfilename != NULL) + { + length += strlen(qfilename) + 1; + free(qfilename); + } + } + gfilename = (char *) ecalloc(length, sizeof(char)); + for (SCAN_GLOB_LIST(list, p)) + { + INIT_GLOB_LIST(list, p); + qfilename = shell_quote(p); + if (qfilename != NULL) + { + sprintf(gfilename + strlen(gfilename), "%s ", qfilename); + free(qfilename); + } + } + /* + * Overwrite the final trailing space with a null terminator. + */ + *--p = '\0'; + GLOB_LIST_DONE(list); +} +#else +#ifdef DECL_GLOB_NAME +{ + /* + * The globbing function returns a single name, and + * is called multiple times to walk thru all names. + */ + register char *p; + register int len; + register int n; + char *pathname; + char *qpathname; + DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle) + + GLOB_FIRST_NAME(filename, &fnd, handle); + if (GLOB_FIRST_FAILED(handle)) + { + free(filename); + return (ofilename); + } + + _splitpath(filename, drive, dir, fname, ext); + len = 100; + gfilename = (char *) ecalloc(len, sizeof(char)); + p = gfilename; + do { + n = (int) (strlen(drive) + strlen(dir) + strlen(fnd.GLOB_NAME) + 1); + pathname = (char *) ecalloc(n, sizeof(char)); + SNPRINTF3(pathname, n, "%s%s%s", drive, dir, fnd.GLOB_NAME); + qpathname = shell_quote(pathname); + free(pathname); + if (qpathname != NULL) + { + n = (int) strlen(qpathname); + while (p - gfilename + n + 2 >= len) + { + /* + * No room in current buffer. + * Allocate a bigger one. + */ + len *= 2; + *p = '\0'; + p = (char *) ecalloc(len, sizeof(char)); + strcpy(p, gfilename); + free(gfilename); + gfilename = p; + p = gfilename + strlen(gfilename); + } + strcpy(p, qpathname); + free(qpathname); + p += n; + *p++ = ' '; + } + } while (GLOB_NEXT_NAME(handle, &fnd) == 0); + + /* + * Overwrite the final trailing space with a null terminator. + */ + *--p = '\0'; + GLOB_NAME_DONE(handle); +} +#else +#if HAVE_POPEN +{ + /* + * We get the shell to glob the filename for us by passing + * an "echo" command to the shell and reading its output. + */ + FILE *fd; + char *s; + char *lessecho; + char *cmd; + char *esc; + int len; + + esc = get_meta_escape(); + if (strlen(esc) == 0) + esc = "-"; + esc = shell_quote(esc); + if (esc == NULL) + { + free(filename); + return (ofilename); + } + lessecho = lgetenv("LESSECHO"); + if (lessecho == NULL || *lessecho == '\0') + lessecho = "lessecho"; + /* + * Invoke lessecho, and read its output (a globbed list of filenames). + */ + len = (int) (strlen(lessecho) + strlen(ofilename) + (7*strlen(metachars())) + 24); + cmd = (char *) ecalloc(len, sizeof(char)); + SNPRINTF4(cmd, len, "%s -p0x%x -d0x%x -e%s ", lessecho, openquote, closequote, esc); + free(esc); + for (s = metachars(); *s != '\0'; s++) + sprintf(cmd + strlen(cmd), "-n0x%x ", *s); + sprintf(cmd + strlen(cmd), "-- %s", ofilename); + fd = shellcmd(cmd); + free(cmd); + if (fd == NULL) + { + /* + * Cannot create the pipe. + * Just return the original (fexpanded) filename. + */ + free(filename); + return (ofilename); + } + gfilename = readfd(fd); + pclose(fd); + if (*gfilename == '\0') + { + free(gfilename); + free(filename); + return (ofilename); + } +} +#else + /* + * No globbing functions at all. Just use the fexpanded filename. + */ + gfilename = save(filename); +#endif +#endif +#endif + free(filename); + free(ofilename); + return (gfilename); +} + +/* + * Return number of %s escapes in a string. + * Return a large number if there are any other % escapes besides %s. + */ + static int +num_pct_s(lessopen) + char *lessopen; +{ + int num = 0; + + while (*lessopen != '\0') + { + if (*lessopen == '%') + { + if (lessopen[1] == '%') + ++lessopen; + else if (lessopen[1] == 's') + ++num; + else + return (999); + } + ++lessopen; + } + return (num); +} + +/* + * See if we should open a "replacement file" + * instead of the file we're about to open. + */ + public char * +open_altfile(filename, pf, pfd) + char *filename; + int *pf; + void **pfd; +{ +#if !HAVE_POPEN + return (NULL); +#else + char *lessopen; + char *cmd; + int len; + FILE *fd; +#if HAVE_FILENO + int returnfd = 0; +#endif + + if (!use_lessopen || secure) + return (NULL); + ch_ungetchar(-1); + if ((lessopen = lgetenv("LESSOPEN")) == NULL) + return (NULL); + while (*lessopen == '|') + { + /* + * If LESSOPEN starts with a |, it indicates + * a "pipe preprocessor". + */ +#if !HAVE_FILENO + error("LESSOPEN pipe is not supported", NULL_PARG); + return (NULL); +#else + lessopen++; + returnfd++; +#endif + } + if (*lessopen == '-') { + /* + * Lessopen preprocessor will accept "-" as a filename. + */ + lessopen++; + } else { + if (strcmp(filename, "-") == 0) + return (NULL); + } + if (num_pct_s(lessopen) > 1) + { + error("Invalid LESSOPEN variable", NULL_PARG); + return (NULL); + } + + len = (int) (strlen(lessopen) + strlen(filename) + 2); + cmd = (char *) ecalloc(len, sizeof(char)); + SNPRINTF1(cmd, len, lessopen, filename); + fd = shellcmd(cmd); + free(cmd); + if (fd == NULL) + { + /* + * Cannot create the pipe. + */ + return (NULL); + } +#if HAVE_FILENO + if (returnfd) + { + int f; + char c; + + /* + * Read one char to see if the pipe will produce any data. + * If it does, push the char back on the pipe. + */ + f = fileno(fd); + SET_BINARY(f); + if (read(f, &c, 1) != 1) + { + /* + * Pipe is empty. + * If more than 1 pipe char was specified, + * the exit status tells whether the file itself + * is empty, or if there is no alt file. + * If only one pipe char, just assume no alt file. + */ + int status = pclose(fd); + if (returnfd > 1 && status == 0) { + *pfd = NULL; + *pf = -1; + return (save(FAKE_EMPTYFILE)); + } + return (NULL); + } + ch_ungetchar(c); + *pfd = (void *) fd; + *pf = f; + return (save("-")); + } +#endif + cmd = readfd(fd); + pclose(fd); + if (*cmd == '\0') + /* + * Pipe is empty. This means there is no alt file. + */ + return (NULL); + return (cmd); +#endif /* HAVE_POPEN */ +} + +/* + * Close a replacement file. + */ + public void +close_altfile(altfilename, filename, pipefd) + char *altfilename; + char *filename; + void *pipefd; +{ +#if HAVE_POPEN + char *lessclose; + FILE *fd; + char *cmd; + int len; + + if (secure) + return; + if (pipefd != NULL) + { +#if OS2 + /* + * The pclose function of OS/2 emx sometimes fails. + * Send SIGINT to the piped process before closing it. + */ + kill(((FILE*)pipefd)->_pid, SIGINT); +#endif + pclose((FILE*) pipefd); + } + if ((lessclose = lgetenv("LESSCLOSE")) == NULL) + return; + if (num_pct_s(lessclose) > 2) + { + error("Invalid LESSCLOSE variable", NULL_PARG); + return; + } + len = (int) (strlen(lessclose) + strlen(filename) + strlen(altfilename) + 2); + cmd = (char *) ecalloc(len, sizeof(char)); + SNPRINTF2(cmd, len, lessclose, filename, altfilename); + fd = shellcmd(cmd); + free(cmd); + if (fd != NULL) + pclose(fd); +#endif +} + +/* + * Is the specified file a directory? + */ + public int +is_dir(filename) + char *filename; +{ + int isdir = 0; + + filename = shell_unquote(filename); +#if HAVE_STAT +{ + int r; + struct stat statbuf; + + r = stat(filename, &statbuf); + isdir = (r >= 0 && S_ISDIR(statbuf.st_mode)); +} +#else +#ifdef _OSK +{ + register int f; + + f = open(filename, S_IREAD | S_IFDIR); + if (f >= 0) + close(f); + isdir = (f >= 0); +} +#endif +#endif + free(filename); + return (isdir); +} + +/* + * Returns NULL if the file can be opened and + * is an ordinary file, otherwise an error message + * (if it cannot be opened or is a directory, etc.) + */ + public char * +bad_file(filename) + char *filename; +{ + register char *m = NULL; + + filename = shell_unquote(filename); + if (!force_open && is_dir(filename)) + { + static char is_a_dir[] = " is a directory"; + + m = (char *) ecalloc(strlen(filename) + sizeof(is_a_dir), + sizeof(char)); + strcpy(m, filename); + strcat(m, is_a_dir); + } else + { +#if HAVE_STAT + int r; + struct stat statbuf; + + r = stat(filename, &statbuf); + if (r < 0) + { + m = errno_message(filename); + } else if (force_open) + { + m = NULL; + } else if (!S_ISREG(statbuf.st_mode)) + { + static char not_reg[] = " is not a regular file (use -f to see it)"; + m = (char *) ecalloc(strlen(filename) + sizeof(not_reg), + sizeof(char)); + strcpy(m, filename); + strcat(m, not_reg); + } +#endif + } + free(filename); + return (m); +} + +/* + * Return the size of a file, as cheaply as possible. + * In Unix, we can stat the file. + */ + public POSITION +filesize(f) + int f; +{ +#if HAVE_STAT + struct stat statbuf; + + if (fstat(f, &statbuf) >= 0) + return ((POSITION) statbuf.st_size); +#else +#ifdef _OSK + long size; + + if ((size = (long) _gs_size(f)) >= 0) + return ((POSITION) size); +#endif +#endif + return (seek_filesize(f)); +} + +/* + * + */ + public char * +shell_coption() +{ + return ("-c"); +} + +/* + * Return last component of a pathname. + */ + public char * +last_component(name) + char *name; +{ + char *slash; + + for (slash = name + strlen(name); slash > name; ) + { + --slash; + if (*slash == *PATHNAME_SEP || *slash == '/') + return (slash + 1); + } + return (name); +} + diff --git a/files/Sources/files/less/forwback.c b/files/Sources/files/less/forwback.c new file mode 100644 index 00000000..f4bc0e67 --- /dev/null +++ b/files/Sources/files/less/forwback.c @@ -0,0 +1,452 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Primitives for displaying the file on the screen, + * scrolling either forward or backward. + */ + +#include "less.h" +#include "position.h" + +public int screen_trashed; +public int squished; +public int no_back_scroll = 0; +public int forw_prompt; +public int same_pos_bell = 1; + +public int display_next_file_or_exit = 0; + +extern int sigs; +extern int top_scroll; +extern int quiet; +extern int sc_width, sc_height; +extern int plusoption; +extern int forw_scroll; +extern int back_scroll; +extern int ignore_eoi; +extern int clear_bg; +extern int final_attr; +extern int oldbot; +#if HILITE_SEARCH +extern int size_linebuf; +extern int hilite_search; +extern int status_col; +#endif + +extern int unix2003_compat; +#if TAGS +extern char *tagoption; +#endif + +/* + * Sound the bell to indicate user is trying to move past end of file. + */ + static void +eof_bell() +{ + if (quiet == NOT_QUIET) + bell(); + else + vbell(); +} + +/* + * Check to see if the end of file is currently displayed. + */ + public int +eof_displayed() +{ + POSITION pos; + + if (ignore_eoi) + return (0); + + if (ch_length() == NULL_POSITION) + /* + * If the file length is not known, + * we can't possibly be displaying EOF. + */ + return (0); + + /* + * If the bottom line is empty, we are at EOF. + * If the bottom line ends at the file length, + * we must be just at EOF. + */ + pos = position(BOTTOM_PLUS_ONE); + return (pos == NULL_POSITION || pos == ch_length()); +} + +/* + * Check to see if the entire file is currently displayed. + */ + public int +entire_file_displayed() +{ + POSITION pos; + + /* Make sure last line of file is displayed. */ + if (!eof_displayed()) + return (0); + + /* Make sure first line of file is displayed. */ + pos = position(0); + return (pos == NULL_POSITION || pos == 0); +} + +/* + * If the screen is "squished", repaint it. + * "Squished" means the first displayed line is not at the top + * of the screen; this can happen when we display a short file + * for the first time. + */ + public void +squish_check() +{ + if (!squished) + return; + squished = 0; + repaint(); +} + +/* + * Display n lines, scrolling forward, + * starting at position pos in the input file. + * "force" means display the n lines even if we hit end of file. + * "only_last" means display only the last screenful if n > screen size. + * "nblank" is the number of blank lines to draw before the first + * real line. If nblank > 0, the pos must be NULL_POSITION. + * The first real line after the blanks will start at ch_zero(). + */ + public void +forw(n, pos, force, only_last, nblank) + register int n; + POSITION pos; + int force; + int only_last; + int nblank; +{ + int nlines = 0; + int do_repaint; + static int first_time = 1; + + squish_check(); + + /* + * do_repaint tells us not to display anything till the end, + * then just repaint the entire screen. + * We repaint if we are supposed to display only the last + * screenful and the request is for more than a screenful. + * Also if the request exceeds the forward scroll limit + * (but not if the request is for exactly a screenful, since + * repainting itself involves scrolling forward a screenful). + */ + do_repaint = (only_last && n > sc_height-1) || + (forw_scroll >= 0 && n > forw_scroll && n != sc_height-1); + +#if HILITE_SEARCH + if (hilite_search == OPT_ONPLUS || is_filtering() || status_col) { + prep_hilite(pos, pos + 4*size_linebuf, ignore_eoi ? 1 : -1); + pos = next_unfiltered(pos); + } +#endif + + if (!do_repaint) + { + if (top_scroll && n >= sc_height - 1 && pos != ch_length()) + { + /* + * Start a new screen. + * {{ This is not really desirable if we happen + * to hit eof in the middle of this screen, + * but we don't yet know if that will happen. }} + */ + pos_clear(); + add_forw_pos(pos); + force = 1; + if (!unix2003_compat) { + clear(); + home(); + } + } + + if (pos != position(BOTTOM_PLUS_ONE) || empty_screen()) + { + /* + * This is not contiguous with what is + * currently displayed. Clear the screen image + * (position table) and start a new screen. + */ + pos_clear(); + add_forw_pos(pos); + force = 1; + if (top_scroll) + { + clear(); + home(); + } else if (!first_time) + { + putstr("...skipping...\n"); + } + } + } + + while (--n >= 0) + { + /* + * Read the next line of input. + */ + if (nblank > 0) + { + /* + * Still drawing blanks; don't get a line + * from the file yet. + * If this is the last blank line, get ready to + * read a line starting at ch_zero() next time. + */ + if (--nblank == 0) + pos = ch_zero(); + } else + { + /* + * Get the next line from the file. + */ + pos = forw_line(pos); +#if HILITE_SEARCH + pos = next_unfiltered(pos); +#endif + if (pos == NULL_POSITION) + { + /* + * End of file: stop here unless the top line + * is still empty, or "force" is true. + * Even if force is true, stop when the last + * line in the file reaches the top of screen. + */ + if (!force && position(TOP) != NULL_POSITION) + break; + if (!empty_lines(0, 0) && + !empty_lines(1, 1) && + empty_lines(2, sc_height-1)) + break; + } + } + /* + * Add the position of the next line to the position table. + * Display the current line on the screen. + */ + add_forw_pos(pos); + nlines++; + if (do_repaint) + continue; + /* + * If this is the first screen displayed and + * we hit an early EOF (i.e. before the requested + * number of lines), we "squish" the display down + * at the bottom of the screen. + * But don't do this if a + option or a -t option + * was given. These options can cause us to + * start the display after the beginning of the file, + * and it is not appropriate to squish in that case. + */ + if (first_time && pos == NULL_POSITION && !top_scroll && +#if TAGS + tagoption == NULL && +#endif + !plusoption) + { + squished = 1; + continue; + } + put_line(); +#if 0 + /* {{ + * Can't call clear_eol here. The cursor might be at end of line + * on an ignaw terminal, so clear_eol would clear the last char + * of the current line instead of all of the next line. + * If we really need to do this on clear_bg terminals, we need + * to find a better way. + * }} + */ + if (clear_bg && apply_at_specials(final_attr) != AT_NORMAL) + { + /* + * Writing the last character on the last line + * of the display may have scrolled the screen. + * If we were in standout mode, clear_bg terminals + * will fill the new line with the standout color. + * Now we're in normal mode again, so clear the line. + */ + clear_eol(); + } +#endif + forw_prompt = 1; + } + + if (nlines == 0 && same_pos_bell) + eof_bell(); + else if (do_repaint) + repaint(); + first_time = 0; + (void) currline(BOTTOM); +} + +/* + * Display n lines, scrolling backward. + */ + public void +back(n, pos, force, only_last) + register int n; + POSITION pos; + int force; + int only_last; +{ + int nlines = 0; + int do_repaint; + + squish_check(); + do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1)); +#if HILITE_SEARCH + if (hilite_search == OPT_ONPLUS || is_filtering() || status_col) { + prep_hilite((pos < 3*size_linebuf) ? 0 : pos - 3*size_linebuf, pos, -1); + } +#endif + while (--n >= 0) + { + /* + * Get the previous line of input. + */ +#if HILITE_SEARCH + pos = prev_unfiltered(pos); +#endif + + pos = back_line(pos); + if (pos == NULL_POSITION) + { + /* + * Beginning of file: stop here unless "force" is true. + */ + if (!force) + break; + } + /* + * Add the position of the previous line to the position table. + * Display the line on the screen. + */ + add_back_pos(pos); + nlines++; + if (!do_repaint) + { + home(); + add_line(); + put_line(); + } + } + + if (nlines == 0 && same_pos_bell) + eof_bell(); + else if (do_repaint) + repaint(); + else if (!oldbot) + lower_left(); + (void) currline(BOTTOM); +} + +/* + * Display n more lines, forward. + * Start just after the line currently displayed at the bottom of the screen. + */ + public void +forward(n, force, only_last) + int n; + int force; + int only_last; +{ + POSITION pos; + + if (get_quit_at_eof() && eof_displayed() && !(ch_getflags() & CH_HELPFILE)) + { + /* + * If the -e flag is set and we're trying to go + * forward from end-of-file, go on to the next file. + */ + if (edit_next(1)) + quit(QUIT_OK); + return; + } + + pos = position(BOTTOM_PLUS_ONE); + if (pos == NULL_POSITION && (!force || empty_lines(2, sc_height-1))) + { + if (ignore_eoi) + { + /* + * ignore_eoi is to support A_F_FOREVER. + * Back up until there is a line at the bottom + * of the screen. + */ + if (empty_screen()) + pos = ch_zero(); + else + { + do + { + back(1, position(TOP), 1, 0); + pos = position(BOTTOM_PLUS_ONE); + } while (pos == NULL_POSITION); + } + } else + { + eof_bell(); + return; + } + } + forw(n, pos, force, only_last, 0); +} + +/* + * Display n more lines, backward. + * Start just before the line currently displayed at the top of the screen. + */ + public void +backward(n, force, only_last) + int n; + int force; + int only_last; +{ + POSITION pos; + + pos = position(TOP); + if (pos == NULL_POSITION && (!force || position(BOTTOM) == 0)) + { + eof_bell(); + return; + } + back(n, pos, force, only_last); +} + +/* + * Get the backwards scroll limit. + * Must call this function instead of just using the value of + * back_scroll, because the default case depends on sc_height and + * top_scroll, as well as back_scroll. + */ + public int +get_back_scroll() +{ + if (no_back_scroll) + return (0); + if (back_scroll >= 0) + return (back_scroll); + if (top_scroll) + return (sc_height - 2); + return (10000); /* infinity */ +} diff --git a/files/Sources/files/less/funcs.h b/files/Sources/files/less/funcs.h new file mode 100644 index 00000000..cd8f57f1 --- /dev/null +++ b/files/Sources/files/less/funcs.h @@ -0,0 +1,302 @@ + public char * save (char *s); + public VOID_POINTER ecalloc (int count, unsigned int size); + public char * skipsp (register char *s); + public int sprefix (char *ps, char *s, int uppercase); + public void quit (int status); + public void raw_mode (int on); + public void scrsize (void); + public char * special_key_str (int key); + public void get_term (void); + public void init (void); + public void deinit (void); + public void home (void); + public void add_line (void); + public void remove_top (int n); + public void win32_scroll_up (int n); + public void lower_left (void); + public void line_left (void); + public void check_winch (void); + public void goto_line (int slinenum); + public void vbell (void); + public void bell (void); + public void clear (void); + public void clear_eol (void); + public void clear_bot (void); + public void at_enter (int attr); + public void at_exit (void); + public void at_switch (int attr); + public int is_at_equiv (int attr1, int attr2); + public int apply_at_specials (int attr); + public void backspace (void); + public void putbs (void); + public char WIN32getch (void); + public void WIN32setcolors (void); + public void WIN32textout (void); + public void match_brac (register int obrac, register int cbrac, int forwdir, int n); + public void ch_ungetchar (int c); + public void end_logfile (void); + public void sync_logfile (void); + public int ch_seek (register POSITION pos); + public int ch_end_seek (void); + public int ch_end_buffer_seek (void); + public int ch_beg_seek (void); + public POSITION ch_length (void); + public POSITION ch_tell (void); + public int ch_forw_get (void); + public int ch_back_get (void); + public void ch_setbufspace (int bufspace); + public void ch_flush (void); + public int seekable (int f); + public void ch_set_eof (void); + public void ch_init (int f, int flags); + public void ch_close (void); + public int ch_getflags (void); + public void ch_dump (void); + public void init_charset (void); + public int binary_char (LWCHAR c); + public int control_char (LWCHAR c); + public char * prchar (LWCHAR c); + public char * prutfchar (LWCHAR ch); + public int utf_len (char ch); + public int is_utf8_well_formed (unsigned char *s, int slen); + public int utf_bin_count (unsigned char *data, int len); + public LWCHAR get_wchar (char *p); + public void put_wchar (char **pp, LWCHAR ch); + public LWCHAR step_char (char **pp, signed int dir, char *limit); + public int is_composing_char (LWCHAR ch); + public int is_ubin_char (LWCHAR ch); + public int is_wide_char (LWCHAR ch); + public int is_combining_char (LWCHAR ch1, LWCHAR ch2); + public void cmd_reset (void); + public void clear_cmd (void); + public void cmd_putstr (char *s); + public int len_cmdbuf (void); + public void set_mlist (void *mlist, int cmdflags); + public void cmd_addhist (); + public void cmd_accept (void); + public int cmd_char (int c); + public LINENUM cmd_int (long *frac); + public char * get_cmdbuf (void); + public char * cmd_lastpattern (void); + public void init_cmdhist (void); + public void save_cmdhist (void); + public int in_mca (void); + public void dispversion (void); + public int getcc (void); + public void ungetcc (int c); + public void ungetsc (char *s); + public void commands (void); + public int cvt_length (int len, int ops); + public int * cvt_alloc_chpos (int len); + public void cvt_text (char *odst, char *osrc, int *chpos, int *lenp, int ops); + public void init_cmds (void); + public void add_UNIX03cmd_table (char *buf, int len); + public void add_fcmd_table (char *buf, int len); + public void add_ecmd_table (char *buf, int len); + public int fcmd_decode (char *cmd, char **sp); + public int ecmd_decode (char *cmd, char **sp); + public char * lgetenv (char *var); + public int lesskey (void); + public void add_hometable (void); + public int editchar (int c, int flags); + public void init_textlist (); + public char * forw_textlist (); + public char * back_textlist (); + public int edit (char *filename); + public int edit_ifile (IFILE ifile); + public int edit_list (char *filelist); + public int edit_first (void); + public int edit_last (void); + public int edit_next (int n); + public int edit_prev (int n); + public int edit_index (int n); + public IFILE save_curr_ifile (void); + public void unsave_ifile (IFILE save_ifile); + public void reedit_ifile (IFILE save_ifile); + public void reopen_curr_ifile (void); + public int edit_stdin (void); + public void cat_file (void); + public void use_logfile (char *filename); + public char * shell_unquote (char *str); + public char * get_meta_escape (void); + public char * shell_quote (char *s); + public char * homefile (char *filename); + public char * fexpand (char *s); + public char * fcomplete (char *s); + public int bin_file (int f); + public char * lglob (char *filename); + public char * open_altfile (char *filename, int *pf, void **pfd); + public void close_altfile (char *altfilename, char *filename, void *pipefd); + public int is_dir (char *filename); + public char * bad_file (char *filename); + public POSITION filesize (int f); + public char * shell_coption (void); + public char * last_component (char *name); + public int eof_displayed (void); + public int entire_file_displayed (void); + public void squish_check (void); + public void forw (register int n, POSITION pos, int force, int only_last, int nblank); + public void back (register int n, POSITION pos, int force, int only_last); + public void forward (int n, int force, int only_last); + public void backward (int n, int force, int only_last); + public int get_back_scroll (void); + public void del_ifile (IFILE h); + public IFILE next_ifile (IFILE h); + public IFILE prev_ifile (IFILE h); + public IFILE getoff_ifile (IFILE ifile); + public int nifile (void); + public IFILE get_ifile (char *filename, IFILE prev); + public char * get_filename (IFILE ifile); + public int get_index (IFILE ifile); + public void store_pos (IFILE ifile, struct scrpos *scrpos); + public void get_pos (IFILE ifile, struct scrpos *scrpos); + public void set_open (IFILE ifile); + public int opened (IFILE ifile); + public void hold_ifile (IFILE ifile, int incr); + public int held_ifile (IFILE ifile); + public void * get_filestate (IFILE ifile); + public void set_filestate (IFILE ifile, void *filestate); + public void if_dump (void); + public POSITION forw_line (POSITION curr_pos); + public POSITION back_line (POSITION curr_pos); + public void set_attnpos (POSITION pos); + public void jump_forw (void); + public void jump_forw_buffered (void); + public void jump_back (LINENUM linenum); + public void repaint (void); + public void jump_percent (int percent, long fraction); + public void jump_line_loc (POSITION pos, int sline); + public void jump_loc (POSITION pos, int sline); + public void init_line (void); + public int is_ascii_char (LWCHAR ch); + public void prewind (void); + public void plinenum (POSITION pos); + public void pshift_all (void); + public int is_ansi_end (LWCHAR ch); + public int is_ansi_middle (LWCHAR ch); + public int pappend (unsigned char c, POSITION pos); + public int pflushmbc (void); + public void pdone (int endline, int forw); + public void set_status_col (char c); + public int gline (register int i, register int *ap); + public void null_line (void); + public POSITION forw_raw_line (POSITION curr_pos, char **linep, int *line_lenp); + public POSITION back_raw_line (POSITION curr_pos, char **linep, int *line_lenp); + public int rrshift (void); + public void clr_linenum (void); + public void add_lnum (LINENUM linenum, POSITION pos); + public LINENUM find_linenum (POSITION pos); + public POSITION find_pos (LINENUM linenum); + public LINENUM currline (int where); + public void lsystem (char *cmd, char *donemsg); + public int pipe_mark (int c, char *cmd); + public int pipe_data (char *cmd, POSITION spos, POSITION epos); + public void init_mark (void); + public int badmark (int c); + public void setmark (int c); + public void lastmark (void); + public void gomark (int c); + public POSITION markpos (int c); + public void unmark (IFILE ifile); + public void opt_o (int type, char *s); + public void opt__O (int type, char *s); + public void opt_j (int type, char *s); + public void calc_jump_sline (void); + public void opt_shift (int type, char *s); + public void calc_shift_count (void); + public void opt_k (int type, char *s); + public void opt_t (int type, char *s); + public void opt__T (int type, char *s); + public void opt_p (int type, char *s); + public void opt__P (int type, char *s); + public void opt_b (int type, char *s); + public void opt_i (int type, char *s); + public void opt__V (int type, char *s); + public void opt_D (int type, char *s); + public void opt_x (int type, register char *s); + public void opt_quote (int type, register char *s); + public void opt_query (int type, char *s); + public int get_swindow (void); + public char * propt (int c); + public void scan_option (char *s); + public void toggle_option (); + public int opt_has_param (); + public char * opt_prompt (); + public int isoptpending (void); + public void nopendopt (void); + public int getnum (char **sp, char *printopt, int *errp); + public long getfraction (char **sp, char *printopt, int *errp); + public int get_quit_at_eof (void); + public void init_option (void); + public struct loption * findopt (int c); + public struct loption * findopt_name (char **p_optname, char **p_oname, int *p_err); + public int iread (int fd, char *buf, unsigned int len); + public void intread (void); + public time_type get_time (void); + public char * errno_message (char *filename); + public int percentage (POSITION num, POSITION den); + public POSITION percent_pos (POSITION pos, int percent, long fraction); + public int os9_signal (void); + public void put_line (void); + public void flush (void); + public int putchr (int c); + public void putstr (register char *s); + public void get_return (void); + public void error (char *fmt, PARG *parg); + public void ierror (char *fmt, PARG *parg); + public int query (char *fmt, PARG *parg); + public int compile_pattern (char *pattern, int search_type, void **comp_pattern); + public void uncompile_pattern (void **pattern); + public int valid_pattern (char *pattern); + public int is_null_pattern (void *pattern); + public int match_pattern (void *pattern, char *tpattern, char *line, int line_len, char **sp, char **ep, int notbol, int search_type); + public POSITION position (int where); + public void add_forw_pos (POSITION pos); + public void add_back_pos (POSITION pos); + public void pos_clear (void); + public void pos_init (void); + public int onscreen (POSITION pos); + public int empty_screen (void); + public int empty_lines (int s, int e); + public void get_scrpos (struct scrpos *scrpos); + public int adjsline (int sline); + public void init_prompt (void); + public char * pr_expand (constant char *proto, int maxwidth); + public char * eq_message (void); + public char * pr_string (void); + public char * wait_message (void); + public void init_search (); + public void repaint_hilite (int on); + public void clear_attn (void); + public void undo_search (void); + public void clr_hlist (); + public void clr_hilite (void); + public void clr_filter (void); + public int is_filtered (POSITION pos); + public POSITION next_unfiltered (POSITION pos); + public POSITION prev_unfiltered (POSITION pos); + public int is_hilited (POSITION pos, POSITION epos, int nohide, int *p_matches); + public void chg_hilite (void); + public void chg_caseless (void); + public int search (int search_type, char *pattern, int n); + public void prep_hilite (POSITION spos, POSITION epos, int maxlines); + public void set_filter_pattern (char *pattern, int search_type); + public int is_filtering (void); + public RETSIGTYPE winch (int type); + public void init_signals (int on); + public void psignals (void); + public void cleantags (void); + public int gettagtype (void); + public void findtag (register char *tag); + public POSITION tagsearch (void); + public char * nexttag (int n); + public char * prevtag (int n); + public int ntags (void); + public int curr_tag (void); + public int edit_tagfile (void); + public void open_getchr (void); + public void close_getchr (void); + public int getchr (void); + public void opt_dashp (int type, char *s); + // iOS: + public void clean_cmds(void); diff --git a/files/Sources/files/less/help.c b/files/Sources/files/less/help.c new file mode 100644 index 00000000..3b0d1bd9 --- /dev/null +++ b/files/Sources/files/less/help.c @@ -0,0 +1,241 @@ +/* This file was generated by mkhelp from less.hlp */ +#include "less.h" +constant char helpdata[] = { +'\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','\b','S','U','\b','U','M','\b','M','M','\b','M','A','\b','A','R','\b','R','Y','\b','Y',' ','O','\b','O','F','\b','F',' ','L','\b','L','E','\b','E','S','\b','S','S','\b','S',' ','C','\b','C','O','\b','O','M','\b','M','M','\b','M','A','\b','A','N','\b','N','D','\b','D','S','\b','S','\n', +'\n', +' ',' ',' ',' ',' ',' ','C','o','m','m','a','n','d','s',' ','m','a','r','k','e','d',' ','w','i','t','h',' ','*',' ','m','a','y',' ','b','e',' ','p','r','e','c','e','d','e','d',' ','b','y',' ','a',' ','n','u','m','b','e','r',',',' ','_','\b','N','.','\n', +' ',' ',' ',' ',' ',' ','N','o','t','e','s',' ','i','n',' ','p','a','r','e','n','t','h','e','s','e','s',' ','i','n','d','i','c','a','t','e',' ','t','h','e',' ','b','e','h','a','v','i','o','r',' ','i','f',' ','_','\b','N',' ','i','s',' ','g','i','v','e','n','.','\n', +' ',' ',' ',' ',' ',' ','A',' ','k','e','y',' ','p','r','e','c','e','d','e','d',' ','b','y',' ','a',' ','c','a','r','e','t',' ','i','n','d','i','c','a','t','e','s',' ','t','h','e',' ','C','t','r','l',' ','k','e','y',';',' ','t','h','u','s',' ','^','K',' ','i','s',' ','c','t','r','l','-','K','.','\n', +'\n', +' ',' ','h',' ',' ','H',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','i','s','p','l','a','y',' ','t','h','i','s',' ','h','e','l','p','.','\n', +' ',' ','q',' ',' ',':','q',' ',' ','Q',' ',' ',':','Q',' ',' ','Z','Z',' ',' ',' ',' ',' ','E','x','i','t','.','\n', +' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n', +'\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','M','\b','M','O','\b','O','V','\b','V','I','\b','I','N','\b','N','G','\b','G','\n', +'\n', +' ',' ','e',' ',' ','^','E',' ',' ','j',' ',' ','^','N',' ',' ','C','R',' ',' ','*',' ',' ','F','o','r','w','a','r','d',' ',' ','o','n','e',' ','l','i','n','e',' ',' ',' ','(','o','r',' ','_','\b','N',' ','l','i','n','e','s',')','.','\n', +' ',' ','y',' ',' ','^','Y',' ',' ','k',' ',' ','^','K',' ',' ','^','P',' ',' ','*',' ',' ','B','a','c','k','w','a','r','d',' ','o','n','e',' ','l','i','n','e',' ',' ',' ','(','o','r',' ','_','\b','N',' ','l','i','n','e','s',')','.','\n', +' ',' ','f',' ',' ','^','F',' ',' ','^','V',' ',' ','S','P','A','C','E',' ',' ','*',' ',' ','F','o','r','w','a','r','d',' ',' ','o','n','e',' ','w','i','n','d','o','w',' ','(','o','r',' ','_','\b','N',' ','l','i','n','e','s',')','.','\n', +' ',' ','b',' ',' ','^','B',' ',' ','E','S','C','-','v',' ',' ',' ',' ',' ',' ','*',' ',' ','B','a','c','k','w','a','r','d',' ','o','n','e',' ','w','i','n','d','o','w',' ','(','o','r',' ','_','\b','N',' ','l','i','n','e','s',')','.','\n', +' ',' ','z',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','F','o','r','w','a','r','d',' ',' ','o','n','e',' ','w','i','n','d','o','w',' ','(','a','n','d',' ','s','e','t',' ','w','i','n','d','o','w',' ','t','o',' ','_','\b','N',')','.','\n', +' ',' ','w',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','B','a','c','k','w','a','r','d',' ','o','n','e',' ','w','i','n','d','o','w',' ','(','a','n','d',' ','s','e','t',' ','w','i','n','d','o','w',' ','t','o',' ','_','\b','N',')','.','\n', +' ',' ','E','S','C','-','S','P','A','C','E',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','F','o','r','w','a','r','d',' ',' ','o','n','e',' ','w','i','n','d','o','w',',',' ','b','u','t',' ','d','o','n','\'','t',' ','s','t','o','p',' ','a','t',' ','e','n','d','-','o','f','-','f','i','l','e','.','\n', +' ',' ','d',' ',' ','^','D',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','F','o','r','w','a','r','d',' ',' ','o','n','e',' ','h','a','l','f','-','w','i','n','d','o','w',' ','(','a','n','d',' ','s','e','t',' ','h','a','l','f','-','w','i','n','d','o','w',' ','t','o',' ','_','\b','N',')','.','\n', +' ',' ','u',' ',' ','^','U',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','B','a','c','k','w','a','r','d',' ','o','n','e',' ','h','a','l','f','-','w','i','n','d','o','w',' ','(','a','n','d',' ','s','e','t',' ','h','a','l','f','-','w','i','n','d','o','w',' ','t','o',' ','_','\b','N',')','.','\n', +' ',' ','E','S','C','-',')',' ',' ','R','i','g','h','t','A','r','r','o','w',' ','*',' ',' ','R','i','g','h','t',' ','o','n','e',' ','h','a','l','f',' ','s','c','r','e','e','n',' ','w','i','d','t','h',' ','(','o','r',' ','_','\b','N',' ','p','o','s','i','t','i','o','n','s',')','.','\n', +' ',' ','E','S','C','-','(',' ',' ','L','e','f','t','A','r','r','o','w',' ',' ','*',' ',' ','L','e','f','t',' ',' ','o','n','e',' ','h','a','l','f',' ','s','c','r','e','e','n',' ','w','i','d','t','h',' ','(','o','r',' ','_','\b','N',' ','p','o','s','i','t','i','o','n','s',')','.','\n', +' ',' ','E','S','C','-','}',' ',' ','^','R','i','g','h','t','A','r','r','o','w',' ',' ',' ','R','i','g','h','t',' ','t','o',' ','l','a','s','t',' ','c','o','l','u','m','n',' ','d','i','s','p','l','a','y','e','d','.','\n', +' ',' ','E','S','C','-','{',' ',' ','^','L','e','f','t','A','r','r','o','w',' ',' ',' ',' ','L','e','f','t',' ',' ','t','o',' ','f','i','r','s','t',' ','c','o','l','u','m','n','.','\n', +' ',' ','F',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','F','o','r','w','a','r','d',' ','f','o','r','e','v','e','r',';',' ','l','i','k','e',' ','"','t','a','i','l',' ','-','f','"','.','\n', +' ',' ','E','S','C','-','F',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','L','i','k','e',' ','F',' ','b','u','t',' ','s','t','o','p',' ','w','h','e','n',' ','s','e','a','r','c','h',' ','p','a','t','t','e','r','n',' ','i','s',' ','f','o','u','n','d','.','\n', +' ',' ','r',' ',' ','^','R',' ',' ','^','L',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','R','e','p','a','i','n','t',' ','s','c','r','e','e','n','.','\n', +' ',' ','R',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','R','e','p','a','i','n','t',' ','s','c','r','e','e','n',',',' ','d','i','s','c','a','r','d','i','n','g',' ','b','u','f','f','e','r','e','d',' ','i','n','p','u','t','.','\n', +' ',' ',' ',' ',' ',' ',' ',' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n', +' ',' ',' ',' ',' ',' ',' ',' ','D','e','f','a','u','l','t',' ','"','w','i','n','d','o','w','"',' ','i','s',' ','t','h','e',' ','s','c','r','e','e','n',' ','h','e','i','g','h','t','.','\n', +' ',' ',' ',' ',' ',' ',' ',' ','D','e','f','a','u','l','t',' ','"','h','a','l','f','-','w','i','n','d','o','w','"',' ','i','s',' ','h','a','l','f',' ','o','f',' ','t','h','e',' ','s','c','r','e','e','n',' ','h','e','i','g','h','t','.','\n', +' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n', +'\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','\b','S','E','\b','E','A','\b','A','R','\b','R','C','\b','C','H','\b','H','I','\b','I','N','\b','N','G','\b','G','\n', +'\n', +' ',' ','/','_','\b','p','_','\b','a','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','n',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','S','e','a','r','c','h',' ','f','o','r','w','a','r','d',' ','f','o','r',' ','(','_','\b','N','-','t','h',')',' ','m','a','t','c','h','i','n','g',' ','l','i','n','e','.','\n', +' ',' ','?','_','\b','p','_','\b','a','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','n',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','S','e','a','r','c','h',' ','b','a','c','k','w','a','r','d',' ','f','o','r',' ','(','_','\b','N','-','t','h',')',' ','m','a','t','c','h','i','n','g',' ','l','i','n','e','.','\n', +' ',' ','n',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','R','e','p','e','a','t',' ','p','r','e','v','i','o','u','s',' ','s','e','a','r','c','h',' ','(','f','o','r',' ','_','\b','N','-','t','h',' ','o','c','c','u','r','r','e','n','c','e',')','.','\n', +' ',' ','N',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','R','e','p','e','a','t',' ','p','r','e','v','i','o','u','s',' ','s','e','a','r','c','h',' ','i','n',' ','r','e','v','e','r','s','e',' ','d','i','r','e','c','t','i','o','n','.','\n', +' ',' ','E','S','C','-','n',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','R','e','p','e','a','t',' ','p','r','e','v','i','o','u','s',' ','s','e','a','r','c','h',',',' ','s','p','a','n','n','i','n','g',' ','f','i','l','e','s','.','\n', +' ',' ','E','S','C','-','N',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','R','e','p','e','a','t',' ','p','r','e','v','i','o','u','s',' ','s','e','a','r','c','h',',',' ','r','e','v','e','r','s','e',' ','d','i','r','.',' ','&',' ','s','p','a','n','n','i','n','g',' ','f','i','l','e','s','.','\n', +' ',' ','E','S','C','-','u',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','U','n','d','o',' ','(','t','o','g','g','l','e',')',' ','s','e','a','r','c','h',' ','h','i','g','h','l','i','g','h','t','i','n','g','.','\n', +' ',' ','&','_','\b','p','_','\b','a','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','n',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','D','i','s','p','l','a','y',' ','o','n','l','y',' ','m','a','t','c','h','i','n','g',' ','l','i','n','e','s','\n', +' ',' ',' ',' ',' ',' ',' ',' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n', +' ',' ',' ',' ',' ',' ',' ',' ','A',' ','s','e','a','r','c','h',' ','p','a','t','t','e','r','n',' ','m','a','y',' ','b','e',' ','p','r','e','c','e','d','e','d',' ','b','y',' ','o','n','e',' ','o','r',' ','m','o','r','e',' ','o','f',':','\n', +' ',' ',' ',' ',' ',' ',' ',' ','^','N',' ','o','r',' ','!',' ',' ','S','e','a','r','c','h',' ','f','o','r',' ','N','O','N','-','m','a','t','c','h','i','n','g',' ','l','i','n','e','s','.','\n', +' ',' ',' ',' ',' ',' ',' ',' ','^','E',' ','o','r',' ','*',' ',' ','S','e','a','r','c','h',' ','m','u','l','t','i','p','l','e',' ','f','i','l','e','s',' ','(','p','a','s','s',' ','t','h','r','u',' ','E','N','D',' ','O','F',' ','F','I','L','E',')','.','\n', +' ',' ',' ',' ',' ',' ',' ',' ','^','F',' ','o','r',' ','@',' ',' ','S','t','a','r','t',' ','s','e','a','r','c','h',' ','a','t',' ','F','I','R','S','T',' ','f','i','l','e',' ','(','f','o','r',' ','/',')',' ','o','r',' ','l','a','s','t',' ','f','i','l','e',' ','(','f','o','r',' ','?',')','.','\n', +' ',' ',' ',' ',' ',' ',' ',' ','^','K',' ',' ',' ',' ',' ',' ',' ','H','i','g','h','l','i','g','h','t',' ','m','a','t','c','h','e','s',',',' ','b','u','t',' ','d','o','n','\'','t',' ','m','o','v','e',' ','(','K','E','E','P',' ','p','o','s','i','t','i','o','n',')','.','\n', +' ',' ',' ',' ',' ',' ',' ',' ','^','R',' ',' ',' ',' ',' ',' ',' ','D','o','n','\'','t',' ','u','s','e',' ','R','E','G','U','L','A','R',' ','E','X','P','R','E','S','S','I','O','N','S','.','\n', +' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n', +'\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','J','\b','J','U','\b','U','M','\b','M','P','\b','P','I','\b','I','N','\b','N','G','\b','G','\n', +'\n', +' ',' ','g',' ',' ','<',' ',' ','E','S','C','-','<',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','G','o',' ','t','o',' ','f','i','r','s','t',' ','l','i','n','e',' ','i','n',' ','f','i','l','e',' ','(','o','r',' ','l','i','n','e',' ','_','\b','N',')','.','\n', +' ',' ','G',' ',' ','>',' ',' ','E','S','C','-','>',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','G','o',' ','t','o',' ','l','a','s','t',' ','l','i','n','e',' ','i','n',' ','f','i','l','e',' ','(','o','r',' ','l','i','n','e',' ','_','\b','N',')','.','\n', +' ',' ','p',' ',' ','%',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','G','o',' ','t','o',' ','b','e','g','i','n','n','i','n','g',' ','o','f',' ','f','i','l','e',' ','(','o','r',' ','_','\b','N',' ','p','e','r','c','e','n','t',' ','i','n','t','o',' ','f','i','l','e',')','.','\n', +' ',' ','t',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','G','o',' ','t','o',' ','t','h','e',' ','(','_','\b','N','-','t','h',')',' ','n','e','x','t',' ','t','a','g','.','\n', +' ',' ','T',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','G','o',' ','t','o',' ','t','h','e',' ','(','_','\b','N','-','t','h',')',' ','p','r','e','v','i','o','u','s',' ','t','a','g','.','\n', +' ',' ','{',' ',' ','(',' ',' ','[',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','F','i','n','d',' ','c','l','o','s','e',' ','b','r','a','c','k','e','t',' ','}',' ',')',' ',']','.','\n', +' ',' ','}',' ',' ',')',' ',' ',']',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','F','i','n','d',' ','o','p','e','n',' ','b','r','a','c','k','e','t',' ','{',' ','(',' ','[','.','\n', +' ',' ','E','S','C','-','^','F',' ','_','\b','<','_','\b','c','_','\b','1','_','\b','>',' ','_','\b','<','_','\b','c','_','\b','2','_','\b','>',' ',' ','*',' ',' ','F','i','n','d',' ','c','l','o','s','e',' ','b','r','a','c','k','e','t',' ','_','\b','<','_','\b','c','_','\b','2','_','\b','>','.','\n', +' ',' ','E','S','C','-','^','B',' ','_','\b','<','_','\b','c','_','\b','1','_','\b','>',' ','_','\b','<','_','\b','c','_','\b','2','_','\b','>',' ',' ','*',' ',' ','F','i','n','d',' ','o','p','e','n',' ','b','r','a','c','k','e','t',' ','_','\b','<','_','\b','c','_','\b','1','_','\b','>',' ','\n', +' ',' ',' ',' ',' ',' ',' ',' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n', +' ',' ',' ',' ',' ',' ',' ',' ','E','a','c','h',' ','"','f','i','n','d',' ','c','l','o','s','e',' ','b','r','a','c','k','e','t','"',' ','c','o','m','m','a','n','d',' ','g','o','e','s',' ','f','o','r','w','a','r','d',' ','t','o',' ','t','h','e',' ','c','l','o','s','e',' ','b','r','a','c','k','e','t',' ','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','m','a','t','c','h','i','n','g',' ','t','h','e',' ','(','_','\b','N','-','t','h',')',' ','o','p','e','n',' ','b','r','a','c','k','e','t',' ','i','n',' ','t','h','e',' ','t','o','p',' ','l','i','n','e','.','\n', +' ',' ',' ',' ',' ',' ',' ',' ','E','a','c','h',' ','"','f','i','n','d',' ','o','p','e','n',' ','b','r','a','c','k','e','t','"',' ','c','o','m','m','a','n','d',' ','g','o','e','s',' ','b','a','c','k','w','a','r','d',' ','t','o',' ','t','h','e',' ','o','p','e','n',' ','b','r','a','c','k','e','t',' ','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','m','a','t','c','h','i','n','g',' ','t','h','e',' ','(','_','\b','N','-','t','h',')',' ','c','l','o','s','e',' ','b','r','a','c','k','e','t',' ','i','n',' ','t','h','e',' ','b','o','t','t','o','m',' ','l','i','n','e','.','\n', +'\n', +' ',' ','m','_','\b','<','_','\b','l','_','\b','e','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','>',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','M','a','r','k',' ','t','h','e',' ','c','u','r','r','e','n','t',' ','p','o','s','i','t','i','o','n',' ','w','i','t','h',' ','<','l','e','t','t','e','r','>','.','\n', +' ',' ','\'','_','\b','<','_','\b','l','_','\b','e','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','>',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','G','o',' ','t','o',' ','a',' ','p','r','e','v','i','o','u','s','l','y',' ','m','a','r','k','e','d',' ','p','o','s','i','t','i','o','n','.','\n', +' ',' ','\'','\'',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','G','o',' ','t','o',' ','t','h','e',' ','p','r','e','v','i','o','u','s',' ','p','o','s','i','t','i','o','n','.','\n', +' ',' ','^','X','^','X',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','a','m','e',' ','a','s',' ','\'','.','\n', +' ',' ',' ',' ',' ',' ',' ',' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n', +' ',' ',' ',' ',' ',' ',' ',' ','A',' ','m','a','r','k',' ','i','s',' ','a','n','y',' ','u','p','p','e','r','-','c','a','s','e',' ','o','r',' ','l','o','w','e','r','-','c','a','s','e',' ','l','e','t','t','e','r','.','\n', +' ',' ',' ',' ',' ',' ',' ',' ','C','e','r','t','a','i','n',' ','m','a','r','k','s',' ','a','r','e',' ','p','r','e','d','e','f','i','n','e','d',':','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','^',' ',' ','m','e','a','n','s',' ',' ','b','e','g','i','n','n','i','n','g',' ','o','f',' ','t','h','e',' ','f','i','l','e','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','$',' ',' ','m','e','a','n','s',' ',' ','e','n','d',' ','o','f',' ','t','h','e',' ','f','i','l','e','\n', +' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n', +'\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','C','\b','C','H','\b','H','A','\b','A','N','\b','N','G','\b','G','I','\b','I','N','\b','N','G','\b','G',' ','F','\b','F','I','\b','I','L','\b','L','E','\b','E','S','\b','S','\n', +'\n', +' ',' ',':','e',' ','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','x','a','m','i','n','e',' ','a',' ','n','e','w',' ','f','i','l','e','.','\n', +' ',' ','^','X','^','V',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','a','m','e',' ','a','s',' ',':','e','.','\n', +' ',' ',':','n',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','E','x','a','m','i','n','e',' ','t','h','e',' ','(','_','\b','N','-','t','h',')',' ','n','e','x','t',' ','f','i','l','e',' ','f','r','o','m',' ','t','h','e',' ','c','o','m','m','a','n','d',' ','l','i','n','e','.','\n', +' ',' ',':','p',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','E','x','a','m','i','n','e',' ','t','h','e',' ','(','_','\b','N','-','t','h',')',' ','p','r','e','v','i','o','u','s',' ','f','i','l','e',' ','f','r','o','m',' ','t','h','e',' ','c','o','m','m','a','n','d',' ','l','i','n','e','.','\n', +' ',' ',':','x',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','*',' ',' ','E','x','a','m','i','n','e',' ','t','h','e',' ','f','i','r','s','t',' ','(','o','r',' ','_','\b','N','-','t','h',')',' ','f','i','l','e',' ','f','r','o','m',' ','t','h','e',' ','c','o','m','m','a','n','d',' ','l','i','n','e','.','\n', +' ',' ',':','d',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','e','l','e','t','e',' ','t','h','e',' ','c','u','r','r','e','n','t',' ','f','i','l','e',' ','f','r','o','m',' ','t','h','e',' ','c','o','m','m','a','n','d',' ','l','i','n','e',' ','l','i','s','t','.','\n', +' ',' ','=',' ',' ','^','G',' ',' ',':','f',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','P','r','i','n','t',' ','c','u','r','r','e','n','t',' ','f','i','l','e',' ','n','a','m','e','.','\n', +' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n', +'\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','M','\b','M','I','\b','I','S','\b','S','C','\b','C','E','\b','E','L','\b','L','L','\b','L','A','\b','A','N','\b','N','E','\b','E','O','\b','O','U','\b','U','S','\b','S',' ','C','\b','C','O','\b','O','M','\b','M','M','\b','M','A','\b','A','N','\b','N','D','\b','D','S','\b','S','\n', +'\n', +' ',' ','-','_','\b','<','_','\b','f','_','\b','l','_','\b','a','_','\b','g','_','\b','>',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','T','o','g','g','l','e',' ','a',' ','c','o','m','m','a','n','d',' ','l','i','n','e',' ','o','p','t','i','o','n',' ','[','s','e','e',' ','O','P','T','I','O','N','S',' ','b','e','l','o','w',']','.','\n', +' ',' ','-','-','_','\b','<','_','\b','n','_','\b','a','_','\b','m','_','\b','e','_','\b','>',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','T','o','g','g','l','e',' ','a',' ','c','o','m','m','a','n','d',' ','l','i','n','e',' ','o','p','t','i','o','n',',',' ','b','y',' ','n','a','m','e','.','\n', +' ',' ','_','_','\b','<','_','\b','f','_','\b','l','_','\b','a','_','\b','g','_','\b','>',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','i','s','p','l','a','y',' ','t','h','e',' ','s','e','t','t','i','n','g',' ','o','f',' ','a',' ','c','o','m','m','a','n','d',' ','l','i','n','e',' ','o','p','t','i','o','n','.','\n', +' ',' ','_','_','_','\b','<','_','\b','n','_','\b','a','_','\b','m','_','\b','e','_','\b','>',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','i','s','p','l','a','y',' ','t','h','e',' ','s','e','t','t','i','n','g',' ','o','f',' ','a','n',' ','o','p','t','i','o','n',',',' ','b','y',' ','n','a','m','e','.','\n', +' ',' ','+','_','\b','c','_','\b','m','_','\b','d',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','x','e','c','u','t','e',' ','t','h','e',' ','l','e','s','s',' ','c','m','d',' ','e','a','c','h',' ','t','i','m','e',' ','a',' ','n','e','w',' ','f','i','l','e',' ','i','s',' ','e','x','a','m','i','n','e','d','.','\n', +'\n', +' ',' ','!','_','\b','c','_','\b','o','_','\b','m','_','\b','m','_','\b','a','_','\b','n','_','\b','d',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','x','e','c','u','t','e',' ','t','h','e',' ','s','h','e','l','l',' ','c','o','m','m','a','n','d',' ','w','i','t','h',' ','$','S','H','E','L','L','.','\n', +' ',' ','|','X','\b','X','_','\b','c','_','\b','o','_','\b','m','_','\b','m','_','\b','a','_','\b','n','_','\b','d',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','P','i','p','e',' ','f','i','l','e',' ','b','e','t','w','e','e','n',' ','c','u','r','r','e','n','t',' ','p','o','s',' ','&',' ','m','a','r','k',' ','X','\b','X',' ','t','o',' ','s','h','e','l','l',' ','c','o','m','m','a','n','d','.','\n', +' ',' ','s',' ','_','\b','f','_','\b','i','_','\b','l','_','\b','e',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','a','v','e',' ','i','n','p','u','t',' ','t','o',' ','a',' ','f','i','l','e','.','\n', +' ',' ','v',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','d','i','t',' ','t','h','e',' ','c','u','r','r','e','n','t',' ','f','i','l','e',' ','w','i','t','h',' ','$','V','I','S','U','A','L',' ','o','r',' ','$','E','D','I','T','O','R','.','\n', +' ',' ','V',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','P','r','i','n','t',' ','v','e','r','s','i','o','n',' ','n','u','m','b','e','r',' ','o','f',' ','"','l','e','s','s','"','.','\n', +' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n', +'\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','O','\b','O','P','\b','P','T','\b','T','I','\b','I','O','\b','O','N','\b','N','S','\b','S','\n', +'\n', +' ',' ',' ',' ',' ',' ',' ',' ','M','o','s','t',' ','o','p','t','i','o','n','s',' ','m','a','y',' ','b','e',' ','c','h','a','n','g','e','d',' ','e','i','t','h','e','r',' ','o','n',' ','t','h','e',' ','c','o','m','m','a','n','d',' ','l','i','n','e',',','\n', +' ',' ',' ',' ',' ',' ',' ',' ','o','r',' ','f','r','o','m',' ','w','i','t','h','i','n',' ','l','e','s','s',' ','b','y',' ','u','s','i','n','g',' ','t','h','e',' ','-',' ','o','r',' ','-','-',' ','c','o','m','m','a','n','d','.','\n', +' ',' ',' ',' ',' ',' ',' ',' ','O','p','t','i','o','n','s',' ','m','a','y',' ','b','e',' ','g','i','v','e','n',' ','i','n',' ','o','n','e',' ','o','f',' ','t','w','o',' ','f','o','r','m','s',':',' ','e','i','t','h','e','r',' ','a',' ','s','i','n','g','l','e','\n', +' ',' ',' ',' ',' ',' ',' ',' ','c','h','a','r','a','c','t','e','r',' ','p','r','e','c','e','d','e','d',' ','b','y',' ','a',' ','-',',',' ','o','r',' ','a',' ','n','a','m','e',' ','p','r','e','c','e','d','e','d',' ','b','y',' ','-','-','.','\n', +'\n', +' ',' ','-','?',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','h','e','l','p','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','i','s','p','l','a','y',' ','h','e','l','p',' ','(','f','r','o','m',' ','c','o','m','m','a','n','d',' ','l','i','n','e',')','.','\n', +' ',' ','-','a',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','s','e','a','r','c','h','-','s','k','i','p','-','s','c','r','e','e','n','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','a','r','c','h',' ','s','k','i','p','s',' ','c','u','r','r','e','n','t',' ','s','c','r','e','e','n','.','\n', +' ',' ','-','A',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','S','E','A','R','C','H','-','S','K','I','P','-','S','C','R','E','E','N','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','a','r','c','h',' ','s','t','a','r','t','s',' ','j','u','s','t',' ','a','f','t','e','r',' ','t','a','r','g','e','t',' ','l','i','n','e','.','\n', +' ',' ','-','b',' ','[','_','\b','N',']',' ',' ','.','.','.','.',' ',' ','-','-','b','u','f','f','e','r','s','=','[','_','\b','N',']','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','N','u','m','b','e','r',' ','o','f',' ','b','u','f','f','e','r','s','.','\n', +' ',' ','-','B',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','a','u','t','o','-','b','u','f','f','e','r','s','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','o','n','\'','t',' ','a','u','t','o','m','a','t','i','c','a','l','l','y',' ','a','l','l','o','c','a','t','e',' ','b','u','f','f','e','r','s',' ','f','o','r',' ','p','i','p','e','s','.','\n', +' ',' ','-','c',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','c','l','e','a','r','-','s','c','r','e','e','n','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','R','e','p','a','i','n','t',' ','b','y',' ','c','l','e','a','r','i','n','g',' ','r','a','t','h','e','r',' ','t','h','a','n',' ','s','c','r','o','l','l','i','n','g','.','\n', +' ',' ','-','d',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','d','u','m','b','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','u','m','b',' ','t','e','r','m','i','n','a','l','.','\n', +' ',' ','-','D',' ','[','_','\b','x','_','\b','n','_','\b','.','_','\b','n',']',' ',' ','.',' ',' ','-','-','c','o','l','o','r','=','_','\b','x','_','\b','n','_','\b','.','_','\b','n','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','t',' ','s','c','r','e','e','n',' ','c','o','l','o','r','s','.',' ','(','M','S','-','D','O','S',' ','o','n','l','y',')','\n', +' ',' ','-','e',' ',' ','-','E',' ',' ','.','.','.','.',' ',' ','-','-','q','u','i','t','-','a','t','-','e','o','f',' ',' ','-','-','Q','U','I','T','-','A','T','-','E','O','F','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','Q','u','i','t',' ','a','t',' ','e','n','d',' ','o','f',' ','f','i','l','e','.','\n', +' ',' ','-','f',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','f','o','r','c','e','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','F','o','r','c','e',' ','o','p','e','n',' ','n','o','n','-','r','e','g','u','l','a','r',' ','f','i','l','e','s','.','\n', +' ',' ','-','F',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','q','u','i','t','-','i','f','-','o','n','e','-','s','c','r','e','e','n','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','Q','u','i','t',' ','i','f',' ','e','n','t','i','r','e',' ','f','i','l','e',' ','f','i','t','s',' ','o','n',' ','f','i','r','s','t',' ','s','c','r','e','e','n','.','\n', +' ',' ','-','g',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','h','i','l','i','t','e','-','s','e','a','r','c','h','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','H','i','g','h','l','i','g','h','t',' ','o','n','l','y',' ','l','a','s','t',' ','m','a','t','c','h',' ','f','o','r',' ','s','e','a','r','c','h','e','s','.','\n', +' ',' ','-','G',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','H','I','L','I','T','E','-','S','E','A','R','C','H','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','o','n','\'','t',' ','h','i','g','h','l','i','g','h','t',' ','a','n','y',' ','m','a','t','c','h','e','s',' ','f','o','r',' ','s','e','a','r','c','h','e','s','.','\n', +' ',' ','-','h',' ','[','_','\b','N',']',' ',' ','.','.','.','.',' ',' ','-','-','m','a','x','-','b','a','c','k','-','s','c','r','o','l','l','=','[','_','\b','N',']','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','B','a','c','k','w','a','r','d',' ','s','c','r','o','l','l',' ','l','i','m','i','t','.','\n', +' ',' ','-','i',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','i','g','n','o','r','e','-','c','a','s','e','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','I','g','n','o','r','e',' ','c','a','s','e',' ','i','n',' ','s','e','a','r','c','h','e','s',' ','t','h','a','t',' ','d','o',' ','n','o','t',' ','c','o','n','t','a','i','n',' ','u','p','p','e','r','c','a','s','e','.','\n', +' ',' ','-','I',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','I','G','N','O','R','E','-','C','A','S','E','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','I','g','n','o','r','e',' ','c','a','s','e',' ','i','n',' ','a','l','l',' ','s','e','a','r','c','h','e','s','.','\n', +' ',' ','-','j',' ','[','_','\b','N',']',' ',' ','.','.','.','.',' ',' ','-','-','j','u','m','p','-','t','a','r','g','e','t','=','[','_','\b','N',']','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','c','r','e','e','n',' ','p','o','s','i','t','i','o','n',' ','o','f',' ','t','a','r','g','e','t',' ','l','i','n','e','s','.','\n', +' ',' ','-','J',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','s','t','a','t','u','s','-','c','o','l','u','m','n','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','i','s','p','l','a','y',' ','a',' ','s','t','a','t','u','s',' ','c','o','l','u','m','n',' ','a','t',' ','l','e','f','t',' ','e','d','g','e',' ','o','f',' ','s','c','r','e','e','n','.','\n', +' ',' ','-','k',' ','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']',' ',' ','.',' ',' ','-','-','l','e','s','s','k','e','y','-','f','i','l','e','=','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','U','s','e',' ','a',' ','l','e','s','s','k','e','y',' ','f','i','l','e','.','\n', +' ',' ','-','K',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','-','-','q','u','i','t','-','o','n','-','i','n','t','r','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E','x','i','t',' ','l','e','s','s',' ','i','n',' ','r','e','s','p','o','n','s','e',' ','t','o',' ','c','t','r','l','-','C','.','\n', +' ',' ','-','L',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','n','o','-','l','e','s','s','o','p','e','n','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','I','g','n','o','r','e',' ','t','h','e',' ','L','E','S','S','O','P','E','N',' ','e','n','v','i','r','o','n','m','e','n','t',' ','v','a','r','i','a','b','l','e','.','\n', +' ',' ','-','m',' ',' ','-','M',' ',' ','.','.','.','.',' ',' ','-','-','l','o','n','g','-','p','r','o','m','p','t',' ',' ','-','-','L','O','N','G','-','P','R','O','M','P','T','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','t',' ','p','r','o','m','p','t',' ','s','t','y','l','e','.','\n', +' ',' ','-','n',' ',' ','-','N',' ',' ','.','.','.','.',' ',' ','-','-','l','i','n','e','-','n','u','m','b','e','r','s',' ',' ','-','-','L','I','N','E','-','N','U','M','B','E','R','S','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','o','n','\'','t',' ','u','s','e',' ','l','i','n','e',' ','n','u','m','b','e','r','s','.','\n', +' ',' ','-','o',' ','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']',' ',' ','.',' ',' ','-','-','l','o','g','-','f','i','l','e','=','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','C','o','p','y',' ','t','o',' ','l','o','g',' ','f','i','l','e',' ','(','s','t','a','n','d','a','r','d',' ','i','n','p','u','t',' ','o','n','l','y',')','.','\n', +' ',' ','-','O',' ','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']',' ',' ','.',' ',' ','-','-','L','O','G','-','F','I','L','E','=','[','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','C','o','p','y',' ','t','o',' ','l','o','g',' ','f','i','l','e',' ','(','u','n','c','o','n','d','i','t','i','o','n','a','l','l','y',' ','o','v','e','r','w','r','i','t','e',')','.','\n', +' ',' ','-','p',' ','[','_','\b','p','_','\b','a','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','n',']',' ',' ','-','-','p','a','t','t','e','r','n','=','[','_','\b','p','_','\b','a','_','\b','t','_','\b','t','_','\b','e','_','\b','r','_','\b','n',']','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','t','a','r','t',' ','a','t',' ','p','a','t','t','e','r','n',' ','(','f','r','o','m',' ','c','o','m','m','a','n','d',' ','l','i','n','e',')','.','\n', +' ',' ','-','P',' ','[','_','\b','p','_','\b','r','_','\b','o','_','\b','m','_','\b','p','_','\b','t',']',' ',' ',' ','-','-','p','r','o','m','p','t','=','[','_','\b','p','_','\b','r','_','\b','o','_','\b','m','_','\b','p','_','\b','t',']','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','e','f','i','n','e',' ','n','e','w',' ','p','r','o','m','p','t','.','\n', +' ',' ','-','q',' ',' ','-','Q',' ',' ','.','.','.','.',' ',' ','-','-','q','u','i','e','t',' ',' ','-','-','Q','U','I','E','T',' ',' ','-','-','s','i','l','e','n','t',' ','-','-','S','I','L','E','N','T','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','Q','u','i','e','t',' ','t','h','e',' ','t','e','r','m','i','n','a','l',' ','b','e','l','l','.','\n', +' ',' ','-','r',' ',' ','-','R',' ',' ','.','.','.','.',' ',' ','-','-','r','a','w','-','c','o','n','t','r','o','l','-','c','h','a','r','s',' ',' ','-','-','R','A','W','-','C','O','N','T','R','O','L','-','C','H','A','R','S','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','O','u','t','p','u','t',' ','"','r','a','w','"',' ','c','o','n','t','r','o','l',' ','c','h','a','r','a','c','t','e','r','s','.','\n', +' ',' ','-','s',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','s','q','u','e','e','z','e','-','b','l','a','n','k','-','l','i','n','e','s','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','q','u','e','e','z','e',' ','m','u','l','t','i','p','l','e',' ','b','l','a','n','k',' ','l','i','n','e','s','.','\n', +' ',' ','-','S',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','c','h','o','p','-','l','o','n','g','-','l','i','n','e','s','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','C','h','o','p',' ','(','t','r','u','n','c','a','t','e',')',' ','l','o','n','g',' ','l','i','n','e','s',' ','r','a','t','h','e','r',' ','t','h','a','n',' ','w','r','a','p','p','i','n','g','.','\n', +' ',' ','-','t',' ','[','_','\b','t','_','\b','a','_','\b','g',']',' ',' ','.','.',' ',' ','-','-','t','a','g','=','[','_','\b','t','_','\b','a','_','\b','g',']','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','F','i','n','d',' ','a',' ','t','a','g','.','\n', +' ',' ','-','T',' ','[','_','\b','t','_','\b','a','_','\b','g','_','\b','s','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']',' ','-','-','t','a','g','-','f','i','l','e','=','[','_','\b','t','_','\b','a','_','\b','g','_','\b','s','_','\b','f','_','\b','i','_','\b','l','_','\b','e',']','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','U','s','e',' ','a','n',' ','a','l','t','e','r','n','a','t','e',' ','t','a','g','s',' ','f','i','l','e','.','\n', +' ',' ','-','u',' ',' ','-','U',' ',' ','.','.','.','.',' ',' ','-','-','u','n','d','e','r','l','i','n','e','-','s','p','e','c','i','a','l',' ',' ','-','-','U','N','D','E','R','L','I','N','E','-','S','P','E','C','I','A','L','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','C','h','a','n','g','e',' ','h','a','n','d','l','i','n','g',' ','o','f',' ','b','a','c','k','s','p','a','c','e','s','.','\n', +' ',' ','-','V',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','v','e','r','s','i','o','n','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','i','s','p','l','a','y',' ','t','h','e',' ','v','e','r','s','i','o','n',' ','n','u','m','b','e','r',' ','o','f',' ','"','l','e','s','s','"','.','\n', +' ',' ','-','w',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','h','i','l','i','t','e','-','u','n','r','e','a','d','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','H','i','g','h','l','i','g','h','t',' ','f','i','r','s','t',' ','n','e','w',' ','l','i','n','e',' ','a','f','t','e','r',' ','f','o','r','w','a','r','d','-','s','c','r','e','e','n','.','\n', +' ',' ','-','W',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','H','I','L','I','T','E','-','U','N','R','E','A','D','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','H','i','g','h','l','i','g','h','t',' ','f','i','r','s','t',' ','n','e','w',' ','l','i','n','e',' ','a','f','t','e','r',' ','a','n','y',' ','f','o','r','w','a','r','d',' ','m','o','v','e','m','e','n','t','.','\n', +' ',' ','-','x',' ','[','_','\b','N','[',',','.','.','.',']',']',' ',' ','-','-','t','a','b','s','=','[','_','\b','N','[',',','.','.','.',']',']','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','t',' ','t','a','b',' ','s','t','o','p','s','.','\n', +' ',' ','-','X',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','n','o','-','i','n','i','t','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','o','n','\'','t',' ','u','s','e',' ','t','e','r','m','c','a','p',' ','i','n','i','t','/','d','e','i','n','i','t',' ','s','t','r','i','n','g','s','.','\n', +' ',' ','-','y',' ','[','_','\b','N',']',' ',' ','.','.','.','.',' ',' ','-','-','m','a','x','-','f','o','r','w','-','s','c','r','o','l','l','=','[','_','\b','N',']','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','F','o','r','w','a','r','d',' ','s','c','r','o','l','l',' ','l','i','m','i','t','.','\n', +' ',' ','-','z',' ','[','_','\b','N',']',' ',' ','.','.','.','.',' ',' ','-','-','w','i','n','d','o','w','=','[','_','\b','N',']','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','t',' ','s','i','z','e',' ','o','f',' ','w','i','n','d','o','w','.','\n', +' ',' ','-','"',' ','[','_','\b','c','[','_','\b','c',']',']',' ',' ','.',' ',' ','-','-','q','u','o','t','e','s','=','[','_','\b','c','[','_','\b','c',']',']','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','e','t',' ','s','h','e','l','l',' ','q','u','o','t','e',' ','c','h','a','r','a','c','t','e','r','s','.','\n', +' ',' ','-','~',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','t','i','l','d','e','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','o','n','\'','t',' ','d','i','s','p','l','a','y',' ','t','i','l','d','e','s',' ','a','f','t','e','r',' ','e','n','d',' ','o','f',' ','f','i','l','e','.','\n', +' ',' ','-','#',' ','[','_','\b','N',']',' ',' ','.','.','.','.',' ',' ','-','-','s','h','i','f','t','=','[','_','\b','N',']','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','H','o','r','i','z','o','n','t','a','l',' ','s','c','r','o','l','l',' ','a','m','o','u','n','t',' ','(','0',' ','=',' ','o','n','e',' ','h','a','l','f',' ','s','c','r','e','e','n',' ','w','i','d','t','h',')','\n', +' ',' ',' ',' ',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','n','o','-','k','e','y','p','a','d','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','D','o','n','\'','t',' ','s','e','n','d',' ','t','e','r','m','c','a','p',' ','k','e','y','p','a','d',' ','i','n','i','t','/','d','e','i','n','i','t',' ','s','t','r','i','n','g','s','.','\n', +' ',' ',' ',' ',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','f','o','l','l','o','w','-','n','a','m','e','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','T','h','e',' ','F',' ','c','o','m','m','a','n','d',' ','c','h','a','n','g','e','s',' ','f','i','l','e','s',' ','i','f',' ','t','h','e',' ','i','n','p','u','t',' ','f','i','l','e',' ','i','s',' ','r','e','n','a','m','e','d','.','\n', +' ',' ',' ',' ',' ',' ','.','.','.','.','.','.','.','.',' ',' ','-','-','u','s','e','-','b','a','c','k','s','l','a','s','h','\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','S','u','b','s','e','q','u','e','n','t',' ','o','p','t','i','o','n','s',' ','u','s','e',' ','b','a','c','k','s','l','a','s','h',' ','a','s',' ','e','s','c','a','p','e',' ','c','h','a','r','.','\n', +'\n', +'\n', +' ','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\n', +'\n', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','L','\b','L','I','\b','I','N','\b','N','E','\b','E',' ','E','\b','E','D','\b','D','I','\b','I','T','\b','T','I','\b','I','N','\b','N','G','\b','G','\n', +'\n', +' ',' ',' ',' ',' ',' ',' ',' ','T','h','e','s','e',' ','k','e','y','s',' ','c','a','n',' ','b','e',' ','u','s','e','d',' ','t','o',' ','e','d','i','t',' ','t','e','x','t',' ','b','e','i','n','g',' ','e','n','t','e','r','e','d',' ','\n', +' ',' ',' ',' ',' ',' ',' ',' ','o','n',' ','t','h','e',' ','"','c','o','m','m','a','n','d',' ','l','i','n','e','"',' ','a','t',' ','t','h','e',' ','b','o','t','t','o','m',' ','o','f',' ','t','h','e',' ','s','c','r','e','e','n','.','\n', +'\n', +' ','R','i','g','h','t','A','r','r','o','w',' ','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',' ','E','S','C','-','l',' ','.','.','.',' ','M','o','v','e',' ','c','u','r','s','o','r',' ','r','i','g','h','t',' ','o','n','e',' ','c','h','a','r','a','c','t','e','r','.','\n', +' ','L','e','f','t','A','r','r','o','w',' ','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',' ','E','S','C','-','h',' ','.','.','.',' ','M','o','v','e',' ','c','u','r','s','o','r',' ','l','e','f','t',' ','o','n','e',' ','c','h','a','r','a','c','t','e','r','.','\n', +' ','c','t','r','l','-','R','i','g','h','t','A','r','r','o','w',' ',' ','E','S','C','-','R','i','g','h','t','A','r','r','o','w',' ',' ','E','S','C','-','w',' ','.','.','.',' ','M','o','v','e',' ','c','u','r','s','o','r',' ','r','i','g','h','t',' ','o','n','e',' ','w','o','r','d','.','\n', +' ','c','t','r','l','-','L','e','f','t','A','r','r','o','w',' ',' ',' ','E','S','C','-','L','e','f','t','A','r','r','o','w',' ',' ',' ','E','S','C','-','b',' ','.','.','.',' ','M','o','v','e',' ','c','u','r','s','o','r',' ','l','e','f','t',' ','o','n','e',' ','w','o','r','d','.','\n', +' ','H','O','M','E',' ','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',' ','E','S','C','-','0',' ','.','.','.',' ','M','o','v','e',' ','c','u','r','s','o','r',' ','t','o',' ','s','t','a','r','t',' ','o','f',' ','l','i','n','e','.','\n', +' ','E','N','D',' ','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',' ','E','S','C','-','$',' ','.','.','.',' ','M','o','v','e',' ','c','u','r','s','o','r',' ','t','o',' ','e','n','d',' ','o','f',' ','l','i','n','e','.','\n', +' ','B','A','C','K','S','P','A','C','E',' ','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',' ','D','e','l','e','t','e',' ','c','h','a','r',' ','t','o',' ','l','e','f','t',' ','o','f',' ','c','u','r','s','o','r','.','\n', +' ','D','E','L','E','T','E',' ','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',' ','E','S','C','-','x',' ','.','.','.',' ','D','e','l','e','t','e',' ','c','h','a','r',' ','u','n','d','e','r',' ','c','u','r','s','o','r','.','\n', +' ','c','t','r','l','-','B','A','C','K','S','P','A','C','E',' ',' ',' ','E','S','C','-','B','A','C','K','S','P','A','C','E',' ','.','.','.','.','.','.','.','.','.','.','.',' ','D','e','l','e','t','e',' ','w','o','r','d',' ','t','o',' ','l','e','f','t',' ','o','f',' ','c','u','r','s','o','r','.','\n', +' ','c','t','r','l','-','D','E','L','E','T','E',' ','.','.','.','.',' ','E','S','C','-','D','E','L','E','T','E',' ','.','.','.','.',' ','E','S','C','-','X',' ','.','.','.',' ','D','e','l','e','t','e',' ','w','o','r','d',' ','u','n','d','e','r',' ','c','u','r','s','o','r','.','\n', +' ','c','t','r','l','-','U',' ','.','.','.','.','.','.','.','.','.',' ','E','S','C',' ','(','M','S','-','D','O','S',' ','o','n','l','y',')',' ','.','.','.','.','.','.','.',' ','D','e','l','e','t','e',' ','e','n','t','i','r','e',' ','l','i','n','e','.','\n', +' ','U','p','A','r','r','o','w',' ','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',' ','E','S','C','-','k',' ','.','.','.',' ','R','e','t','r','i','e','v','e',' ','p','r','e','v','i','o','u','s',' ','c','o','m','m','a','n','d',' ','l','i','n','e','.','\n', +' ','D','o','w','n','A','r','r','o','w',' ','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',' ','E','S','C','-','j',' ','.','.','.',' ','R','e','t','r','i','e','v','e',' ','n','e','x','t',' ','c','o','m','m','a','n','d',' ','l','i','n','e','.','\n', +' ','T','A','B',' ','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',' ','C','o','m','p','l','e','t','e',' ','f','i','l','e','n','a','m','e',' ','&',' ','c','y','c','l','e','.','\n', +' ','S','H','I','F','T','-','T','A','B',' ','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',' ','E','S','C','-','T','A','B',' ',' ',' ','C','o','m','p','l','e','t','e',' ','f','i','l','e','n','a','m','e',' ','&',' ','r','e','v','e','r','s','e',' ','c','y','c','l','e','.','\n', +' ','c','t','r','l','-','L',' ','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',' ','C','o','m','p','l','e','t','e',' ','f','i','l','e','n','a','m','e',',',' ','l','i','s','t',' ','a','l','l','.','\n', +'\n', +'\n', + 0 }; +constant int size_helpdata = sizeof(helpdata) - 1; diff --git a/files/Sources/files/less/ifile.c b/files/Sources/files/less/ifile.c new file mode 100644 index 00000000..ad5f4b37 --- /dev/null +++ b/files/Sources/files/less/ifile.c @@ -0,0 +1,351 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * An IFILE represents an input file. + * + * It is actually a pointer to an ifile structure, + * but is opaque outside this module. + * Ifile structures are kept in a linked list in the order they + * appear on the command line. + * Any new file which does not already appear in the list is + * inserted after the current file. + */ + +#include "less.h" + +extern IFILE curr_ifile; +extern int unix2003_compat; + +struct ifile { + struct ifile *h_next; /* Links for command line list */ + struct ifile *h_prev; + char *h_filename; /* Name of the file */ + void *h_filestate; /* File state (used in ch.c) */ + int h_index; /* Index within command line list */ + int h_hold; /* Hold count */ + char h_opened; /* Has this ifile been opened? */ + struct scrpos h_scrpos; /* Saved position within the file */ +}; + +/* + * Convert an IFILE (external representation) + * to a struct file (internal representation), and vice versa. + */ +#define int_ifile(h) ((struct ifile *)(h)) +#define ext_ifile(h) ((IFILE)(h)) + +/* + * Anchor for linked list. + */ +static struct ifile anchor = { &anchor, &anchor, NULL, NULL, 0, 0, '\0', + { NULL_POSITION, 0 } }; +static int ifiles = 0; + + static void +incr_index(p, incr) + register struct ifile *p; + int incr; +{ + for (; p != &anchor; p = p->h_next) + p->h_index += incr; +} + +/* + * Link an ifile into the ifile list. + */ + static void +link_ifile(p, prev) + struct ifile *p; + struct ifile *prev; +{ + /* + * Link into list. + */ + if (prev == NULL) + prev = &anchor; + p->h_next = prev->h_next; + p->h_prev = prev; + prev->h_next->h_prev = p; + prev->h_next = p; + /* + * Calculate index for the new one, + * and adjust the indexes for subsequent ifiles in the list. + */ + p->h_index = prev->h_index + 1; + incr_index(p->h_next, 1); + ifiles++; +} + +/* + * Unlink an ifile from the ifile list. + */ + static void +unlink_ifile(p) + struct ifile *p; +{ + p->h_next->h_prev = p->h_prev; + p->h_prev->h_next = p->h_next; + incr_index(p->h_next, -1); + ifiles--; +} + +/* + * Allocate a new ifile structure and stick a filename in it. + * It should go after "prev" in the list + * (or at the beginning of the list if "prev" is NULL). + * Return a pointer to the new ifile structure. + */ + static struct ifile * +new_ifile(filename, prev) + char *filename; + struct ifile *prev; +{ + register struct ifile *p; + + /* + * Allocate and initialize structure. + */ + p = (struct ifile *) ecalloc(1, sizeof(struct ifile)); + p->h_filename = save(filename); + p->h_scrpos.pos = NULL_POSITION; + p->h_opened = 0; + p->h_hold = 0; + p->h_filestate = NULL; + link_ifile(p, prev); + return (p); +} + +/* + * Delete an existing ifile structure. + */ + public void +del_ifile(h) + IFILE h; +{ + register struct ifile *p; + + if (h == NULL_IFILE) + return; + /* + * If the ifile we're deleting is the currently open ifile, + * move off it. + */ + unmark(h); + if (h == curr_ifile) + curr_ifile = getoff_ifile(curr_ifile); + p = int_ifile(h); + unlink_ifile(p); + free(p->h_filename); + free(p); +} + +/* + * Get the ifile after a given one in the list. + */ + public IFILE +next_ifile(h) + IFILE h; +{ + register struct ifile *p; + + p = (h == NULL_IFILE) ? &anchor : int_ifile(h); + if (p->h_next == &anchor) + return (NULL_IFILE); + return (ext_ifile(p->h_next)); +} + +/* + * Get the ifile before a given one in the list. + */ + public IFILE +prev_ifile(h) + IFILE h; +{ + register struct ifile *p; + + p = (h == NULL_IFILE) ? &anchor : int_ifile(h); + if (p->h_prev == &anchor) + return (NULL_IFILE); + return (ext_ifile(p->h_prev)); +} + +/* + * Return a different ifile from the given one. + */ + public IFILE +getoff_ifile(ifile) + IFILE ifile; +{ + IFILE newifile; + + if ((newifile = prev_ifile(ifile)) != NULL_IFILE) + return (newifile); + if ((newifile = next_ifile(ifile)) != NULL_IFILE) + return (newifile); + return (NULL_IFILE); +} + +/* + * Return the number of ifiles. + */ + public int +nifile() +{ + return (ifiles); +} + +/* + * Find an ifile structure, given a filename. + */ + static struct ifile * +find_ifile(filename) + char *filename; +{ + register struct ifile *p; + + for (p = anchor.h_next; p != &anchor; p = p->h_next) + if (strcmp(filename, p->h_filename) == 0) + return (p); + return (NULL); +} + +/* + * Get the ifile associated with a filename. + * If the filename has not been seen before, + * insert the new ifile after "prev" in the list. + */ + public IFILE +get_ifile(filename, prev) + char *filename; + IFILE prev; +{ + register struct ifile *p; + + if (unix2003_compat) { + /* don't exclude duplicates in list */ + p = new_ifile(filename, int_ifile(prev)); + } else { + if ((p = find_ifile(filename)) == NULL) + p = new_ifile(filename, int_ifile(prev)); + } + return (ext_ifile(p)); +} + +/* + * Get the filename associated with a ifile. + */ + public char * +get_filename(ifile) + IFILE ifile; +{ + if (ifile == NULL) + return (NULL); + return (int_ifile(ifile)->h_filename); +} + +/* + * Get the index of the file associated with a ifile. + */ + public int +get_index(ifile) + IFILE ifile; +{ + return (int_ifile(ifile)->h_index); +} + +/* + * Save the file position to be associated with a given file. + */ + public void +store_pos(ifile, scrpos) + IFILE ifile; + struct scrpos *scrpos; +{ + int_ifile(ifile)->h_scrpos = *scrpos; +} + +/* + * Recall the file position associated with a file. + * If no position has been associated with the file, return NULL_POSITION. + */ + public void +get_pos(ifile, scrpos) + IFILE ifile; + struct scrpos *scrpos; +{ + *scrpos = int_ifile(ifile)->h_scrpos; +} + +/* + * Mark the ifile as "opened". + */ + public void +set_open(ifile) + IFILE ifile; +{ + int_ifile(ifile)->h_opened = 1; +} + +/* + * Return whether the ifile has been opened previously. + */ + public int +opened(ifile) + IFILE ifile; +{ + return (int_ifile(ifile)->h_opened); +} + + public void +hold_ifile(ifile, incr) + IFILE ifile; + int incr; +{ + int_ifile(ifile)->h_hold += incr; +} + + public int +held_ifile(ifile) + IFILE ifile; +{ + return (int_ifile(ifile)->h_hold); +} + + public void * +get_filestate(ifile) + IFILE ifile; +{ + return (int_ifile(ifile)->h_filestate); +} + + public void +set_filestate(ifile, filestate) + IFILE ifile; + void *filestate; +{ + int_ifile(ifile)->h_filestate = filestate; +} + +#if 0 + public void +if_dump() +{ + register struct ifile *p; + + for (p = anchor.h_next; p != &anchor; p = p->h_next) + { + printf("%x: %d. <%s> pos %d,%x\n", + p, p->h_index, p->h_filename, + p->h_scrpos.ln, p->h_scrpos.pos); + ch_dump(p->h_filestate); + } +} +#endif diff --git a/files/Sources/files/less/input.c b/files/Sources/files/less/input.c new file mode 100644 index 00000000..26ab7c02 --- /dev/null +++ b/files/Sources/files/less/input.c @@ -0,0 +1,463 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * High level routines dealing with getting lines of input + * from the file being viewed. + * + * When we speak of "lines" here, we mean PRINTABLE lines; + * lines processed with respect to the screen width. + * We use the term "raw line" to refer to lines simply + * delimited by newlines; not processed with respect to screen width. + */ + +#include "less.h" + +extern int squeeze; +extern int chopline; +extern int hshift; +extern int quit_if_one_screen; +extern int sigs; +extern int ignore_eoi; +extern int status_col; +extern POSITION start_attnpos; +extern POSITION end_attnpos; +#if HILITE_SEARCH +extern int hilite_search; +extern int size_linebuf; +#endif + +/* + * Get the next line. + * A "current" position is passed and a "new" position is returned. + * The current position is the position of the first character of + * a line. The new position is the position of the first character + * of the NEXT line. The line obtained is the line starting at curr_pos. + */ + public POSITION +forw_line(curr_pos) + POSITION curr_pos; +{ + POSITION base_pos; + POSITION new_pos; + register int c; + int blankline; + int endline; + int backchars; + +get_forw_line: + if (curr_pos == NULL_POSITION) + { + null_line(); + return (NULL_POSITION); + } +#if HILITE_SEARCH + if (hilite_search == OPT_ONPLUS || is_filtering() || status_col) + { + /* + * If we are ignoring EOI (command F), only prepare + * one line ahead, to avoid getting stuck waiting for + * slow data without displaying the data we already have. + * If we're not ignoring EOI, we *could* do the same, but + * for efficiency we prepare several lines ahead at once. + */ + prep_hilite(curr_pos, curr_pos + 3*size_linebuf, + ignore_eoi ? 1 : -1); + curr_pos = next_unfiltered(curr_pos); + } +#endif + if (ch_seek(curr_pos)) + { + null_line(); + return (NULL_POSITION); + } + + /* + * Step back to the beginning of the line. + */ + base_pos = curr_pos; + for (;;) + { + if (ABORT_SIGS()) + { + null_line(); + return (NULL_POSITION); + } + c = ch_back_get(); + if (c == EOI) + break; + if (c == '\n') + { + (void) ch_forw_get(); + break; + } + --base_pos; + } + + /* + * Read forward again to the position we should start at. + */ + prewind(); + plinenum(base_pos); + (void) ch_seek(base_pos); + new_pos = base_pos; + while (new_pos < curr_pos) + { + if (ABORT_SIGS()) + { + null_line(); + return (NULL_POSITION); + } + c = ch_forw_get(); + backchars = pappend(c, new_pos); + new_pos++; + if (backchars > 0) + { + pshift_all(); + new_pos -= backchars; + while (--backchars >= 0) + (void) ch_back_get(); + } + } + (void) pflushmbc(); + pshift_all(); + + /* + * Read the first character to display. + */ + c = ch_forw_get(); + if (c == EOI) + { + null_line(); + return (NULL_POSITION); + } + blankline = (c == '\n' || c == '\r'); + + /* + * Read each character in the line and append to the line buffer. + */ + for (;;) + { + if (ABORT_SIGS()) + { + null_line(); + return (NULL_POSITION); + } + if (c == '\n' || c == EOI) + { + /* + * End of the line. + */ + backchars = pflushmbc(); + new_pos = ch_tell(); + if (backchars > 0 && !chopline && hshift == 0) + { + new_pos -= backchars + 1; + endline = FALSE; + } else + endline = TRUE; + break; + } + if (c != '\r') + blankline = 0; + + /* + * Append the char to the line and get the next char. + */ + backchars = pappend(c, ch_tell()-1); + if (backchars > 0) + { + /* + * The char won't fit in the line; the line + * is too long to print in the screen width. + * End the line here. + */ + if (chopline || hshift > 0) + { + do + { + if (ABORT_SIGS()) + { + null_line(); + return (NULL_POSITION); + } + c = ch_forw_get(); + } while (c != '\n' && c != EOI); + new_pos = ch_tell(); + endline = TRUE; + quit_if_one_screen = FALSE; + } else + { + new_pos = ch_tell() - backchars; + endline = FALSE; + } + break; + } + c = ch_forw_get(); + } + + pdone(endline, 1); + +#if HILITE_SEARCH + if (is_filtered(base_pos)) + { + /* + * We don't want to display this line. + * Get the next line. + */ + curr_pos = new_pos; + goto get_forw_line; + } + + if (status_col && is_hilited(base_pos, ch_tell()-1, 1, NULL)) + set_status_col('*'); +#endif + + if (squeeze && blankline) + { + /* + * This line is blank. + * Skip down to the last contiguous blank line + * and pretend it is the one which we are returning. + */ + while ((c = ch_forw_get()) == '\n' || c == '\r') + if (ABORT_SIGS()) + { + null_line(); + return (NULL_POSITION); + } + if (c != EOI) + (void) ch_back_get(); + new_pos = ch_tell(); + } + + return (new_pos); +} + +/* + * Get the previous line. + * A "current" position is passed and a "new" position is returned. + * The current position is the position of the first character of + * a line. The new position is the position of the first character + * of the PREVIOUS line. The line obtained is the one starting at new_pos. + */ + public POSITION +back_line(curr_pos) + POSITION curr_pos; +{ + POSITION new_pos, begin_new_pos, base_pos; + int c; + int endline; + int backchars; + +get_back_line: + if (curr_pos == NULL_POSITION || curr_pos <= ch_zero()) + { + null_line(); + return (NULL_POSITION); + } +#if HILITE_SEARCH + if (hilite_search == OPT_ONPLUS || is_filtering() || status_col) + prep_hilite((curr_pos < 3*size_linebuf) ? + 0 : curr_pos - 3*size_linebuf, curr_pos, -1); +#endif + if (ch_seek(curr_pos-1)) + { + null_line(); + return (NULL_POSITION); + } + + if (squeeze) + { + /* + * Find out if the "current" line was blank. + */ + (void) ch_forw_get(); /* Skip the newline */ + c = ch_forw_get(); /* First char of "current" line */ + (void) ch_back_get(); /* Restore our position */ + (void) ch_back_get(); + + if (c == '\n' || c == '\r') + { + /* + * The "current" line was blank. + * Skip over any preceding blank lines, + * since we skipped them in forw_line(). + */ + while ((c = ch_back_get()) == '\n' || c == '\r') + if (ABORT_SIGS()) + { + null_line(); + return (NULL_POSITION); + } + if (c == EOI) + { + null_line(); + return (NULL_POSITION); + } + (void) ch_forw_get(); + } + } + + /* + * Scan backwards until we hit the beginning of the line. + */ + for (;;) + { + if (ABORT_SIGS()) + { + null_line(); + return (NULL_POSITION); + } + c = ch_back_get(); + if (c == '\n') + { + /* + * This is the newline ending the previous line. + * We have hit the beginning of the line. + */ + base_pos = ch_tell() + 1; + break; + } + if (c == EOI) + { + /* + * We have hit the beginning of the file. + * This must be the first line in the file. + * This must, of course, be the beginning of the line. + */ + base_pos = ch_tell(); + break; + } + } + + /* + * Now scan forwards from the beginning of this line. + * We keep discarding "printable lines" (based on screen width) + * until we reach the curr_pos. + * + * {{ This algorithm is pretty inefficient if the lines + * are much longer than the screen width, + * but I don't know of any better way. }} + */ + new_pos = base_pos; + if (ch_seek(new_pos)) + { + null_line(); + return (NULL_POSITION); + } + endline = FALSE; + prewind(); + plinenum(new_pos); + loop: + begin_new_pos = new_pos; + (void) ch_seek(new_pos); + + do + { + c = ch_forw_get(); + if (c == EOI || ABORT_SIGS()) + { + null_line(); + return (NULL_POSITION); + } + new_pos++; + if (c == '\n') + { + backchars = pflushmbc(); + if (backchars > 0 && !chopline && hshift == 0) + { + backchars++; + goto shift; + } + endline = TRUE; + break; + } + backchars = pappend(c, ch_tell()-1); + if (backchars > 0) + { + /* + * Got a full printable line, but we haven't + * reached our curr_pos yet. Discard the line + * and start a new one. + */ + if (chopline || hshift > 0) + { + endline = TRUE; + quit_if_one_screen = FALSE; + break; + } + shift: + pshift_all(); + while (backchars-- > 0) + { + (void) ch_back_get(); + new_pos--; + } + goto loop; + } + } while (new_pos < curr_pos); + + pdone(endline, 0); + +#if HILITE_SEARCH + if (is_filtered(base_pos)) + { + /* + * We don't want to display this line. + * Get the previous line. + */ + curr_pos = begin_new_pos; + goto get_back_line; + } + + if (status_col && curr_pos > 0 && is_hilited(base_pos, curr_pos-1, 1, NULL)) + set_status_col('*'); +#endif + + return (begin_new_pos); +} + +/* + * Set attnpos. + */ + public void +set_attnpos(pos) + POSITION pos; +{ + int c; + + if (pos != NULL_POSITION) + { + if (ch_seek(pos)) + return; + for (;;) + { + c = ch_forw_get(); + if (c == EOI) + break; + if (c == '\n' || c == '\r') + { + (void) ch_back_get(); + break; + } + pos++; + } + end_attnpos = pos; + for (;;) + { + c = ch_back_get(); + if (c == EOI || c == '\n' || c == '\r') + break; + pos--; + } + } + start_attnpos = pos; +} diff --git a/files/Sources/files/less/install.sh b/files/Sources/files/less/install.sh new file mode 100755 index 00000000..41ea84f9 --- /dev/null +++ b/files/Sources/files/less/install.sh @@ -0,0 +1,119 @@ +#!/bin/sh + +# +# install - install a program, script, or datafile +# This comes from X11R5; it is not part of GNU. +# +# $XConsortium: install.sh,v 1.2 89/12/18 14:47:22 jim Exp $ +# +# This script is compatible with the BSD install script, but was written +# from scratch. +# + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" + +instcmd="$mvprog" +chmodcmd="" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +fi + +if [ x"$dst" = x ] +then + echo "install: no destination specified" + exit 1 +fi + + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + +if [ -d $dst ] +then + dst="$dst"/`basename $src` +fi + +# Make a temp file name in the proper directory. + +dstdir=`dirname $dst` +dsttmp=$dstdir/_inst.$$_ + +# Move or copy the file name to the temp name + +$doit $instcmd $src $dsttmp + +# and set any options; do chmod last to preserve setuid bits + +if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; fi +if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; fi +if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; fi +if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; fi + +# Now rename the file to the real destination. + +$doit $rmcmd $dst +$doit $mvcmd $dsttmp $dst + + +exit 0 diff --git a/files/Sources/files/less/jump.c b/files/Sources/files/less/jump.c new file mode 100644 index 00000000..c5b5616e --- /dev/null +++ b/files/Sources/files/less/jump.c @@ -0,0 +1,334 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Routines which jump to a new location in the file. + */ + +#include "less.h" +#include "position.h" + +extern int jump_sline; +extern int squished; +extern int screen_trashed; +extern int sc_width, sc_height; +extern int show_attn; +extern int top_scroll; + +/* + * Jump to the end of the file. + */ + public void +jump_forw() +{ + POSITION pos; + POSITION end_pos; + + if (ch_end_seek()) + { + error("Cannot seek to end of file", NULL_PARG); + return; + } + /* + * Note; lastmark will be called later by jump_loc, but it fails + * because the position table has been cleared by pos_clear below. + * So call it here before calling pos_clear. + */ + lastmark(); + /* + * Position the last line in the file at the last screen line. + * Go back one line from the end of the file + * to get to the beginning of the last line. + */ + lastmark(); + pos_clear(); + end_pos = ch_tell(); + pos = back_line(end_pos); + if (pos == NULL_POSITION) + jump_loc((POSITION)0, sc_height-1); + else + { + jump_loc(pos, sc_height-1); + if (position(sc_height-1) != end_pos) + repaint(); + } +} + +/* + * Jump to the last buffered line in the file. + */ + public void +jump_forw_buffered() +{ + POSITION end; + + if (ch_end_buffer_seek()) + { + error("Cannot seek to end of buffers", NULL_PARG); + return; + } + end = ch_tell(); + if (end != NULL_POSITION && end > 0) + jump_line_loc(end-1, sc_height-1); +} + +/* + * Jump to line n in the file. + */ + public void +jump_back(linenum) + LINENUM linenum; +{ + POSITION pos; + PARG parg; + + /* + * Find the position of the specified line. + * If we can seek there, just jump to it. + * If we can't seek, but we're trying to go to line number 1, + * use ch_beg_seek() to get as close as we can. + */ + pos = find_pos(linenum); + if (pos != NULL_POSITION && ch_seek(pos) == 0) + { + if (show_attn) + set_attnpos(pos); + jump_loc(pos, jump_sline); + } else if (linenum <= 1 && ch_beg_seek() == 0) + { + jump_loc(ch_tell(), jump_sline); + error("Cannot seek to beginning of file", NULL_PARG); + } else + { + parg.p_linenum = linenum; + error("Cannot seek to line number %n", &parg); + } +} + +/* + * Repaint the screen. + */ + public void +repaint() +{ + struct scrpos scrpos; + /* + * Start at the line currently at the top of the screen + * and redisplay the screen. + */ + get_scrpos(&scrpos); + pos_clear(); + if (scrpos.pos == NULL_POSITION) + /* Screen hasn't been drawn yet. */ + jump_loc(0, 0); + else + jump_loc(scrpos.pos, scrpos.ln); +} + +/* + * Jump to a specified percentage into the file. + */ + public void +jump_percent(percent, fraction) + int percent; + long fraction; +{ + POSITION pos, len; + + /* + * Determine the position in the file + * (the specified percentage of the file's length). + */ + if ((len = ch_length()) == NULL_POSITION) + { + ierror("Determining length of file", NULL_PARG); + ch_end_seek(); + } + if ((len = ch_length()) == NULL_POSITION) + { + error("Don't know length of file", NULL_PARG); + return; + } + pos = percent_pos(len, percent, fraction); + if (pos >= len) + pos = len-1; + + jump_line_loc(pos, jump_sline); +} + +/* + * Jump to a specified position in the file. + * Like jump_loc, but the position need not be + * the first character in a line. + */ + public void +jump_line_loc(pos, sline) + POSITION pos; + int sline; +{ + int c; + + if (ch_seek(pos) == 0) + { + /* + * Back up to the beginning of the line. + */ + while ((c = ch_back_get()) != '\n' && c != EOI) + ; + if (c == '\n') + (void) ch_forw_get(); + pos = ch_tell(); + } + if (show_attn) + set_attnpos(pos); + jump_loc(pos, sline); +} + +/* + * Jump to a specified position in the file. + * The position must be the first character in a line. + * Place the target line on a specified line on the screen. + */ + public void +jump_loc(pos, sline) + POSITION pos; + int sline; +{ + register int nline; + POSITION tpos; + POSITION bpos; + + /* + * Normalize sline. + */ + sline = adjsline(sline); + + if ((nline = onscreen(pos)) >= 0) + { + /* + * The line is currently displayed. + * Just scroll there. + */ + nline -= sline; + if (nline > 0) + forw(nline, position(BOTTOM_PLUS_ONE), 1, 0, 0); + else + back(-nline, position(TOP), 1, 0); +#if HILITE_SEARCH + if (show_attn) + repaint_hilite(1); +#endif + return; + } + + /* + * Line is not on screen. + * Seek to the desired location. + */ + if (ch_seek(pos)) + { + error("Cannot seek to that file position", NULL_PARG); + return; + } + + /* + * See if the desired line is before or after + * the currently displayed screen. + */ + tpos = position(TOP); + bpos = position(BOTTOM_PLUS_ONE); + if (tpos == NULL_POSITION || pos >= tpos) + { + /* + * The desired line is after the current screen. + * Move back in the file far enough so that we can + * call forw() and put the desired line at the + * sline-th line on the screen. + */ + for (nline = 0; nline < sline; nline++) + { + if (bpos != NULL_POSITION && pos <= bpos) + { + /* + * Surprise! The desired line is + * close enough to the current screen + * that we can just scroll there after all. + */ + forw(sc_height-sline+nline-1, bpos, 1, 0, 0); +#if HILITE_SEARCH + if (show_attn) + repaint_hilite(1); +#endif + return; + } + pos = back_line(pos); + if (pos == NULL_POSITION) + { + /* + * Oops. Ran into the beginning of the file. + * Exit the loop here and rely on forw() + * below to draw the required number of + * blank lines at the top of the screen. + */ + break; + } + } + lastmark(); + squished = 0; + screen_trashed = 0; + forw(sc_height-1, pos, 1, 0, sline-nline); + } else + { + /* + * The desired line is before the current screen. + * Move forward in the file far enough so that we + * can call back() and put the desired line at the + * sline-th line on the screen. + */ + for (nline = sline; nline < sc_height - 1; nline++) + { + pos = forw_line(pos); + if (pos == NULL_POSITION) + { + /* + * Ran into end of file. + * This shouldn't normally happen, + * but may if there is some kind of read error. + */ + break; + } +#if HILITE_SEARCH + pos = next_unfiltered(pos); +#endif + if (pos >= tpos) + { + /* + * Surprise! The desired line is + * close enough to the current screen + * that we can just scroll there after all. + */ + back(nline+1, tpos, 1, 0); +#if HILITE_SEARCH + if (show_attn) + repaint_hilite(1); +#endif + return; + } + } + lastmark(); + if (!top_scroll) + clear(); + else + home(); + screen_trashed = 0; + add_back_pos(pos); + back(sc_height-1, pos, 1, 0); + } +} diff --git a/files/Sources/files/less/less.h b/files/Sources/files/less/less.h new file mode 100644 index 00000000..759b3993 --- /dev/null +++ b/files/Sources/files/less/less.h @@ -0,0 +1,536 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + +#define NEWBOT 1 + +// iOS: make sure our functions get priority: +#include "ios_error.h" + +/* + * Standard include file for "less". + */ + +/* + * Defines for MSDOS_COMPILER. + */ +#define MSOFTC 1 /* Microsoft C */ +#define BORLANDC 2 /* Borland C */ +#define WIN32C 3 /* Windows (Borland C or Microsoft C) */ +#define DJGPPC 4 /* DJGPP C */ + +/* + * Include the file of compile-time options. + * The <> make cc search for it in -I., not srcdir. + */ +#include "defines.h" + +#ifdef _SEQUENT_ +/* + * Kludge for Sequent Dynix systems that have sigsetmask, but + * it's not compatible with the way less calls it. + * {{ Do other systems need this? }} + */ +#undef HAVE_SIGSETMASK +#endif + +/* + * Language details. + */ +#if HAVE_VOID +#define VOID_POINTER void * +#else +#define VOID_POINTER char * +#define void int +#endif +#if HAVE_CONST +#define constant const +#else +#define constant +#endif + +#define public /* PUBLIC FUNCTION */ + +/* Library function declarations */ + +#if HAVE_SYS_TYPES_H +#include +#endif +#if HAVE_STDIO_H +#include +#endif +#if HAVE_FCNTL_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_CTYPE_H +#include +#endif +#if HAVE_WCTYPE_H +#include +#endif +#if HAVE_LIMITS_H +#include +#endif +#if HAVE_STDLIB_H +#include +#endif +#if HAVE_STRING_H +#include +#endif + +/* OS-specific includes */ +#ifdef _OSK +#include +#include +#endif + +#ifdef __TANDEM +#include +#endif + +#if MSDOS_COMPILER==WIN32C || OS2 +#include +#endif + +#if MSDOS_COMPILER==DJGPPC +#include +#include +#include +#include +#endif + +#if !HAVE_STDLIB_H +char *getenv(); +off_t lseek(); +VOID_POINTER calloc(); +void free(); +#endif + +/* + * Simple lowercase test which can be used during option processing + * (before options are parsed which might tell us what charset to use). + */ +#define ASCII_IS_UPPER(c) ((c) >= 'A' && (c) <= 'Z') +#define ASCII_IS_LOWER(c) ((c) >= 'a' && (c) <= 'z') +#define ASCII_TO_UPPER(c) ((c) - 'a' + 'A') +#define ASCII_TO_LOWER(c) ((c) - 'A' + 'a') + +#undef IS_UPPER +#undef IS_LOWER +#undef TO_UPPER +#undef TO_LOWER +#undef IS_SPACE +#undef IS_DIGIT + +#if HAVE_WCTYPE +#define IS_UPPER(c) iswupper(c) +#define IS_LOWER(c) iswlower(c) +#define TO_UPPER(c) towupper(c) +#define TO_LOWER(c) towlower(c) +#else +#if HAVE_UPPER_LOWER +#define IS_UPPER(c) isupper((unsigned char) (c)) +#define IS_LOWER(c) islower((unsigned char) (c)) +#define TO_UPPER(c) toupper((unsigned char) (c)) +#define TO_LOWER(c) tolower((unsigned char) (c)) +#else +#define IS_UPPER(c) ASCII_IS_UPPER(c) +#define IS_LOWER(c) ASCII_IS_LOWER(c) +#define TO_UPPER(c) ASCII_TO_UPPER(c) +#define TO_LOWER(c) ASCII_TO_LOWER(c) +#endif +#endif + +#ifdef isspace +#define IS_SPACE(c) isspace((unsigned char)(c)) +#else +#define IS_SPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r' || (c) == '\f') +#endif + +#ifdef isdigit +#define IS_DIGIT(c) isdigit((unsigned char)(c)) +#else +#define IS_DIGIT(c) ((c) >= '0' && (c) <= '9') +#endif + +#define IS_CSI_START(c) (((LWCHAR)(c)) == ESC || (((LWCHAR)(c)) == CSI)) + +#ifndef NULL +#define NULL 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#define OPT_OFF 0 +#define OPT_ON 1 +#define OPT_ONPLUS 2 + +#if !HAVE_MEMCPY +#ifndef memcpy +#define memcpy(to,from,len) bcopy((from),(to),(len)) +#endif +#endif + +#if HAVE_SNPRINTF +#define SNPRINTF1(str, size, fmt, v1) snprintf((str), (size), (fmt), (v1)) +#define SNPRINTF2(str, size, fmt, v1, v2) snprintf((str), (size), (fmt), (v1), (v2)) +#define SNPRINTF3(str, size, fmt, v1, v2, v3) snprintf((str), (size), (fmt), (v1), (v2), (v3)) +#define SNPRINTF4(str, size, fmt, v1, v2, v3, v4) snprintf((str), (size), (fmt), (v1), (v2), (v3), (v4)) +#else +/* Use unsafe sprintf if we don't have snprintf. */ +#define SNPRINTF1(str, size, fmt, v1) sprintf((str), (fmt), (v1)) +#define SNPRINTF2(str, size, fmt, v1, v2) sprintf((str), (fmt), (v1), (v2)) +#define SNPRINTF3(str, size, fmt, v1, v2, v3) sprintf((str), (fmt), (v1), (v2), (v3)) +#define SNPRINTF4(str, size, fmt, v1, v2, v3, v4) sprintf((str), (fmt), (v1), (v2), (v3), (v4)) +#endif + +#define BAD_LSEEK ((off_t)-1) + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + +/* + * Upper bound on the string length of an integer converted to string. + * 302 / 1000 is ceil (log10 (2.0)). Subtract 1 for the sign bit; + * add 1 for integer division truncation; add 1 more for a minus sign. + */ +#define INT_STRLEN_BOUND(t) ((sizeof(t) * CHAR_BIT - 1) * 302 / 1000 + 1 + 1) + +/* + * Special types and constants. + */ +typedef unsigned long LWCHAR; +typedef off_t POSITION; +typedef off_t LINENUM; +#define MIN_LINENUM_WIDTH 7 /* Min printing width of a line number */ +#define MAX_UTF_CHAR_LEN 6 /* Max bytes in one UTF-8 char */ + +#define NULL_POSITION ((POSITION)(-1)) + +/* + * Flags for open() + */ +#if MSDOS_COMPILER || OS2 +#define OPEN_READ (O_RDONLY|O_BINARY) +#else +#ifdef _OSK +#define OPEN_READ (S_IREAD) +#else +#ifdef O_RDONLY +#define OPEN_READ (O_RDONLY) +#else +#define OPEN_READ (0) +#endif +#endif +#endif + +#if defined(O_WRONLY) && defined(O_APPEND) +#define OPEN_APPEND (O_APPEND|O_WRONLY) +#else +#ifdef _OSK +#define OPEN_APPEND (S_IWRITE) +#else +#define OPEN_APPEND (1) +#endif +#endif + +/* + * Set a file descriptor to binary mode. + */ +#if MSDOS_COMPILER==MSOFTC +#define SET_BINARY(f) _setmode(f, _O_BINARY); +#else +#if MSDOS_COMPILER || OS2 +#define SET_BINARY(f) setmode(f, O_BINARY) +#else +#define SET_BINARY(f) +#endif +#endif + +/* + * Does the shell treat "?" as a metacharacter? + */ +#if MSDOS_COMPILER || OS2 || _OSK +#define SHELL_META_QUEST 0 +#else +#define SHELL_META_QUEST 1 +#endif + +#define SPACES_IN_FILENAMES 1 + +/* + * An IFILE represents an input file. + */ +#define IFILE VOID_POINTER +#define NULL_IFILE ((IFILE)NULL) + +/* + * The structure used to represent a "screen position". + * This consists of a file position, and a screen line number. + * The meaning is that the line starting at the given file + * position is displayed on the ln-th line of the screen. + * (Screen lines before ln are empty.) + */ +struct scrpos +{ + POSITION pos; + int ln; +}; + +/* + * A mark is an ifile (input file) plus a position within the file. + */ +struct mark +{ + IFILE m_ifile; + struct scrpos m_scrpos; +}; + +typedef union parg +{ + char *p_string; + int p_int; + LINENUM p_linenum; +} PARG; + +#define NULL_PARG ((PARG *)NULL) + +struct textlist +{ + char *string; + char *endstring; +}; + +struct wchar_range +{ + LWCHAR first, last; +}; + +struct wchar_range_table +{ + struct wchar_range *table; + int count; +}; + +#define EOI (-1) + +#define READ_INTR (-2) + +/* A fraction is represented by an int n; the fraction is n/NUM_FRAC_DENOM */ +#define NUM_FRAC_DENOM 1000000 +#define NUM_LOG_FRAC_DENOM 6 + +/* How quiet should we be? */ +#define NOT_QUIET 0 /* Ring bell at eof and for errors */ +#define LITTLE_QUIET 1 /* Ring bell only for errors */ +#define VERY_QUIET 2 /* Never ring bell */ + +/* How should we prompt? */ +#define PR_SHORT 0 /* Prompt with colon */ +#define PR_MEDIUM 1 /* Prompt with message */ +#define PR_LONG 2 /* Prompt with longer message */ + +/* How should we handle backspaces? */ +#define BS_SPECIAL 0 /* Do special things for underlining and bold */ +#define BS_NORMAL 1 /* \b treated as normal char; actually output */ +#define BS_CONTROL 2 /* \b treated as control char; prints as ^H */ + +/* How should we search? */ +#define SRCH_FORW (1 << 0) /* Search forward from current position */ +#define SRCH_BACK (1 << 1) /* Search backward from current position */ +#define SRCH_NO_MOVE (1 << 2) /* Highlight, but don't move */ +#define SRCH_FIND_ALL (1 << 4) /* Find and highlight all matches */ +#define SRCH_NO_MATCH (1 << 8) /* Search for non-matching lines */ +#define SRCH_PAST_EOF (1 << 9) /* Search past end-of-file, into next file */ +#define SRCH_FIRST_FILE (1 << 10) /* Search starting at the first file */ +#define SRCH_NO_REGEX (1 << 12) /* Don't use regular expressions */ +#define SRCH_FILTER (1 << 13) /* Search is for '&' (filter) command */ +#define SRCH_AFTER_TARGET (1 << 14) /* Start search after the target line */ + +#define SRCH_REVERSE(t) (((t) & SRCH_FORW) ? \ + (((t) & ~SRCH_FORW) | SRCH_BACK) : \ + (((t) & ~SRCH_BACK) | SRCH_FORW)) + +/* */ +#define NO_MCA 0 +#define MCA_DONE 1 +#define MCA_MORE 2 + +#define CC_OK 0 /* Char was accepted & processed */ +#define CC_QUIT 1 /* Char was a request to abort current cmd */ +#define CC_ERROR 2 /* Char could not be accepted due to error */ +#define CC_PASS 3 /* Char was rejected (internal) */ + +#define CF_QUIT_ON_ERASE 0001 /* Abort cmd if its entirely erased */ + +/* Special char bit-flags used to tell put_line() to do something special */ +#define AT_NORMAL (0) +#define AT_UNDERLINE (1 << 0) +#define AT_BOLD (1 << 1) +#define AT_BLINK (1 << 2) +#define AT_STANDOUT (1 << 3) +#define AT_ANSI (1 << 4) /* Content-supplied "ANSI" escape sequence */ +#define AT_BINARY (1 << 5) /* LESS*BINFMT representation */ +#define AT_HILITE (1 << 6) /* Internal highlights (e.g., for search) */ + +#if '0' == 240 +#define IS_EBCDIC_HOST 1 +#endif + +#if IS_EBCDIC_HOST +/* + * Long definition for EBCDIC. + * Since the argument is usually a constant, this macro normally compiles + * into a constant. + */ +#define CONTROL(c) ( \ + (c)=='[' ? '\047' : \ + (c)=='a' ? '\001' : \ + (c)=='b' ? '\002' : \ + (c)=='c' ? '\003' : \ + (c)=='d' ? '\067' : \ + (c)=='e' ? '\055' : \ + (c)=='f' ? '\056' : \ + (c)=='g' ? '\057' : \ + (c)=='h' ? '\026' : \ + (c)=='i' ? '\005' : \ + (c)=='j' ? '\025' : \ + (c)=='k' ? '\013' : \ + (c)=='l' ? '\014' : \ + (c)=='m' ? '\015' : \ + (c)=='n' ? '\016' : \ + (c)=='o' ? '\017' : \ + (c)=='p' ? '\020' : \ + (c)=='q' ? '\021' : \ + (c)=='r' ? '\022' : \ + (c)=='s' ? '\023' : \ + (c)=='t' ? '\074' : \ + (c)=='u' ? '\075' : \ + (c)=='v' ? '\062' : \ + (c)=='w' ? '\046' : \ + (c)=='x' ? '\030' : \ + (c)=='y' ? '\031' : \ + (c)=='z' ? '\077' : \ + (c)=='A' ? '\001' : \ + (c)=='B' ? '\002' : \ + (c)=='C' ? '\003' : \ + (c)=='D' ? '\067' : \ + (c)=='E' ? '\055' : \ + (c)=='F' ? '\056' : \ + (c)=='G' ? '\057' : \ + (c)=='H' ? '\026' : \ + (c)=='I' ? '\005' : \ + (c)=='J' ? '\025' : \ + (c)=='K' ? '\013' : \ + (c)=='L' ? '\014' : \ + (c)=='M' ? '\015' : \ + (c)=='N' ? '\016' : \ + (c)=='O' ? '\017' : \ + (c)=='P' ? '\020' : \ + (c)=='Q' ? '\021' : \ + (c)=='R' ? '\022' : \ + (c)=='S' ? '\023' : \ + (c)=='T' ? '\074' : \ + (c)=='U' ? '\075' : \ + (c)=='V' ? '\062' : \ + (c)=='W' ? '\046' : \ + (c)=='X' ? '\030' : \ + (c)=='Y' ? '\031' : \ + (c)=='Z' ? '\077' : \ + (c)=='|' ? '\031' : \ + (c)=='\\' ? '\034' : \ + (c)=='^' ? '\036' : \ + (c)&077) +#else +#define CONTROL(c) ((c)&037) +#endif /* IS_EBCDIC_HOST */ + +#define ESC CONTROL('[') +#define CSI ((unsigned char)'\233') +#define CHAR_END_COMMAND 0x40000000 + +#if _OSK_MWC32 +#define LSIGNAL(sig,func) os9_signal(sig,func) +#else +#define LSIGNAL(sig,func) signal(sig,func) +#endif + +#if HAVE_SIGPROCMASK +#if HAVE_SIGSET_T +#else +#undef HAVE_SIGPROCMASK +#endif +#endif +#if HAVE_SIGPROCMASK +#if HAVE_SIGEMPTYSET +#else +#undef sigemptyset +#define sigemptyset(mp) *(mp) = 0 +#endif +#endif + +#define S_INTERRUPT 01 +#define S_STOP 02 +#define S_WINCH 04 +#define ABORT_SIGS() (sigs & (S_INTERRUPT|S_STOP)) + +#define QUIT_OK 0 +#define QUIT_ERROR 1 +#define QUIT_INTERRUPT 2 +#define QUIT_SAVED_STATUS (-1) + +#define FOLLOW_DESC 0 +#define FOLLOW_NAME 1 + +/* filestate flags */ +#define CH_CANSEEK 001 +#define CH_KEEPOPEN 002 +#define CH_POPENED 004 +#define CH_HELPFILE 010 +#define CH_NODATA 020 /* Special case for zero length files */ + + +#define ch_zero() ((POSITION)0) + +#define FAKE_HELPFILE "@/\\less/\\help/\\file/\\@" +#define FAKE_EMPTYFILE "@/\\less/\\empty/\\file/\\@" + +/* Flags for cvt_text */ +#define CVT_TO_LC 01 /* Convert upper-case to lower-case */ +#define CVT_BS 02 /* Do backspace processing */ +#define CVT_CRLF 04 /* Remove CR after LF */ +#define CVT_ANSI 010 /* Remove ANSI escape sequences */ + +#if HAVE_TIME_T +#define time_type time_t +#else +#define time_type long +#endif + +#include "funcs.h" + +/* Functions not included in funcs.h */ +void postoa(); +void linenumtoa(); +void inttoa(); diff --git a/files/Sources/files/less/less.hlp b/files/Sources/files/less/less.hlp new file mode 100644 index 00000000..255dc7e0 --- /dev/null +++ b/files/Sources/files/less/less.hlp @@ -0,0 +1,236 @@ + + SSUUMMMMAARRYY OOFF LLEESSSS CCOOMMMMAANNDDSS + + Commands marked with * may be preceded by a number, _N. + Notes in parentheses indicate the behavior if _N is given. + A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K. + + h H Display this help. + q :q Q :Q ZZ Exit. + --------------------------------------------------------------------------- + + MMOOVVIINNGG + + e ^E j ^N CR * Forward one line (or _N lines). + y ^Y k ^K ^P * Backward one line (or _N lines). + f ^F ^V SPACE * Forward one window (or _N lines). + b ^B ESC-v * Backward one window (or _N lines). + z * Forward one window (and set window to _N). + w * Backward one window (and set window to _N). + ESC-SPACE * Forward one window, but don't stop at end-of-file. + d ^D * Forward one half-window (and set half-window to _N). + u ^U * Backward one half-window (and set half-window to _N). + ESC-) RightArrow * Right one half screen width (or _N positions). + ESC-( LeftArrow * Left one half screen width (or _N positions). + ESC-} ^RightArrow Right to last column displayed. + ESC-{ ^LeftArrow Left to first column. + F Forward forever; like "tail -f". + ESC-F Like F but stop when search pattern is found. + r ^R ^L Repaint screen. + R Repaint screen, discarding buffered input. + --------------------------------------------------- + Default "window" is the screen height. + Default "half-window" is half of the screen height. + --------------------------------------------------------------------------- + + SSEEAARRCCHHIINNGG + + /_p_a_t_t_e_r_n * Search forward for (_N-th) matching line. + ?_p_a_t_t_e_r_n * Search backward for (_N-th) matching line. + n * Repeat previous search (for _N-th occurrence). + N * Repeat previous search in reverse direction. + ESC-n * Repeat previous search, spanning files. + ESC-N * Repeat previous search, reverse dir. & spanning files. + ESC-u Undo (toggle) search highlighting. + &_p_a_t_t_e_r_n * Display only matching lines + --------------------------------------------------- + A search pattern may be preceded by one or more of: + ^N or ! Search for NON-matching lines. + ^E or * Search multiple files (pass thru END OF FILE). + ^F or @ Start search at FIRST file (for /) or last file (for ?). + ^K Highlight matches, but don't move (KEEP position). + ^R Don't use REGULAR EXPRESSIONS. + --------------------------------------------------------------------------- + + JJUUMMPPIINNGG + + g < ESC-< * Go to first line in file (or line _N). + G > ESC-> * Go to last line in file (or line _N). + p % * Go to beginning of file (or _N percent into file). + t * Go to the (_N-th) next tag. + T * Go to the (_N-th) previous tag. + { ( [ * Find close bracket } ) ]. + } ) ] * Find open bracket { ( [. + ESC-^F _<_c_1_> _<_c_2_> * Find close bracket _<_c_2_>. + ESC-^B _<_c_1_> _<_c_2_> * Find open bracket _<_c_1_> + --------------------------------------------------- + Each "find close bracket" command goes forward to the close bracket + matching the (_N-th) open bracket in the top line. + Each "find open bracket" command goes backward to the open bracket + matching the (_N-th) close bracket in the bottom line. + + m_<_l_e_t_t_e_r_> Mark the current position with . + '_<_l_e_t_t_e_r_> Go to a previously marked position. + '' Go to the previous position. + ^X^X Same as '. + --------------------------------------------------- + A mark is any upper-case or lower-case letter. + Certain marks are predefined: + ^ means beginning of the file + $ means end of the file + --------------------------------------------------------------------------- + + CCHHAANNGGIINNGG FFIILLEESS + + :e [_f_i_l_e] Examine a new file. + ^X^V Same as :e. + :n * Examine the (_N-th) next file from the command line. + :p * Examine the (_N-th) previous file from the command line. + :x * Examine the first (or _N-th) file from the command line. + :d Delete the current file from the command line list. + = ^G :f Print current file name. + --------------------------------------------------------------------------- + + MMIISSCCEELLLLAANNEEOOUUSS CCOOMMMMAANNDDSS + + -_<_f_l_a_g_> Toggle a command line option [see OPTIONS below]. + --_<_n_a_m_e_> Toggle a command line option, by name. + __<_f_l_a_g_> Display the setting of a command line option. + ___<_n_a_m_e_> Display the setting of an option, by name. + +_c_m_d Execute the less cmd each time a new file is examined. + + !_c_o_m_m_a_n_d Execute the shell command with $SHELL. + |XX_c_o_m_m_a_n_d Pipe file between current pos & mark XX to shell command. + s _f_i_l_e Save input to a file. + v Edit the current file with $VISUAL or $EDITOR. + V Print version number of "less". + --------------------------------------------------------------------------- + + OOPPTTIIOONNSS + + Most options may be changed either on the command line, + or from within less by using the - or -- command. + Options may be given in one of two forms: either a single + character preceded by a -, or a name preceded by --. + + -? ........ --help + Display help (from command line). + -a ........ --search-skip-screen + Search skips current screen. + -A ........ --SEARCH-SKIP-SCREEN + Search starts just after target line. + -b [_N] .... --buffers=[_N] + Number of buffers. + -B ........ --auto-buffers + Don't automatically allocate buffers for pipes. + -c ........ --clear-screen + Repaint by clearing rather than scrolling. + -d ........ --dumb + Dumb terminal. + -D [_x_n_._n] . --color=_x_n_._n + Set screen colors. (MS-DOS only) + -e -E .... --quit-at-eof --QUIT-AT-EOF + Quit at end of file. + -f ........ --force + Force open non-regular files. + -F ........ --quit-if-one-screen + Quit if entire file fits on first screen. + -g ........ --hilite-search + Highlight only last match for searches. + -G ........ --HILITE-SEARCH + Don't highlight any matches for searches. + -h [_N] .... --max-back-scroll=[_N] + Backward scroll limit. + -i ........ --ignore-case + Ignore case in searches that do not contain uppercase. + -I ........ --IGNORE-CASE + Ignore case in all searches. + -j [_N] .... --jump-target=[_N] + Screen position of target lines. + -J ........ --status-column + Display a status column at left edge of screen. + -k [_f_i_l_e] . --lesskey-file=[_f_i_l_e] + Use a lesskey file. + -K --quit-on-intr + Exit less in response to ctrl-C. + -L ........ --no-lessopen + Ignore the LESSOPEN environment variable. + -m -M .... --long-prompt --LONG-PROMPT + Set prompt style. + -n -N .... --line-numbers --LINE-NUMBERS + Don't use line numbers. + -o [_f_i_l_e] . --log-file=[_f_i_l_e] + Copy to log file (standard input only). + -O [_f_i_l_e] . --LOG-FILE=[_f_i_l_e] + Copy to log file (unconditionally overwrite). + -p [_p_a_t_t_e_r_n] --pattern=[_p_a_t_t_e_r_n] + Start at pattern (from command line). + -P [_p_r_o_m_p_t] --prompt=[_p_r_o_m_p_t] + Define new prompt. + -q -Q .... --quiet --QUIET --silent --SILENT + Quiet the terminal bell. + -r -R .... --raw-control-chars --RAW-CONTROL-CHARS + Output "raw" control characters. + -s ........ --squeeze-blank-lines + Squeeze multiple blank lines. + -S ........ --chop-long-lines + Chop (truncate) long lines rather than wrapping. + -t [_t_a_g] .. --tag=[_t_a_g] + Find a tag. + -T [_t_a_g_s_f_i_l_e] --tag-file=[_t_a_g_s_f_i_l_e] + Use an alternate tags file. + -u -U .... --underline-special --UNDERLINE-SPECIAL + Change handling of backspaces. + -V ........ --version + Display the version number of "less". + -w ........ --hilite-unread + Highlight first new line after forward-screen. + -W ........ --HILITE-UNREAD + Highlight first new line after any forward movement. + -x [_N[,...]] --tabs=[_N[,...]] + Set tab stops. + -X ........ --no-init + Don't use termcap init/deinit strings. + -y [_N] .... --max-forw-scroll=[_N] + Forward scroll limit. + -z [_N] .... --window=[_N] + Set size of window. + -" [_c[_c]] . --quotes=[_c[_c]] + Set shell quote characters. + -~ ........ --tilde + Don't display tildes after end of file. + -# [_N] .... --shift=[_N] + Horizontal scroll amount (0 = one half screen width) + ........ --no-keypad + Don't send termcap keypad init/deinit strings. + ........ --follow-name + The F command changes files if the input file is renamed. + ........ --use-backslash + Subsequent options use backslash as escape char. + + + --------------------------------------------------------------------------- + + LLIINNEE EEDDIITTIINNGG + + These keys can be used to edit text being entered + on the "command line" at the bottom of the screen. + + RightArrow ..................... ESC-l ... Move cursor right one character. + LeftArrow ...................... ESC-h ... Move cursor left one character. + ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word. + ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word. + HOME ........................... ESC-0 ... Move cursor to start of line. + END ............................ ESC-$ ... Move cursor to end of line. + BACKSPACE ................................ Delete char to left of cursor. + DELETE ......................... ESC-x ... Delete char under cursor. + ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor. + ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor. + ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line. + UpArrow ........................ ESC-k ... Retrieve previous command line. + DownArrow ...................... ESC-j ... Retrieve next command line. + TAB ...................................... Complete filename & cycle. + SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle. + ctrl-L ................................... Complete filename, list all. + + diff --git a/files/Sources/files/less/less.man b/files/Sources/files/less/less.man new file mode 100644 index 00000000..07054070 --- /dev/null +++ b/files/Sources/files/less/less.man @@ -0,0 +1,1648 @@ +LESS(1) General Commands Manual LESS(1) + + + +NAME + less - opposite of more + +SYNOPSIS + less -? + less --help + less -V + less --version + less [-[+]aABcCdeEfFgGiIJKLmMnNqQrRsSuUVwWX~] + [-b space] [-h lines] [-j line] [-k keyfile] + [-{oO} logfile] [-p pattern] [-P prompt] [-t tag] + [-T tagsfile] [-x tab,...] [-y lines] [-[z] lines] + [-# shift] [+[+]cmd] [--] [filename]... + (See the OPTIONS section for alternate option syntax with long option + names.) + + +DESCRIPTION + Less is a program similar to more (1), but which allows backward move- + ment in the file as well as forward movement. Also, less does not have + to read the entire input file before starting, so with large input + files it starts up faster than text editors like vi (1). Less uses + termcap (or terminfo on some systems), so it can run on a variety of + terminals. There is even limited support for hardcopy terminals. (On + a hardcopy terminal, lines which should be printed at the top of the + screen are prefixed with a caret.) + + Commands are based on both more and vi. Commands may be preceded by a + decimal number, called N in the descriptions below. The number is used + by some commands, as indicated. + + +COMMANDS + In the following descriptions, ^X means control-X. ESC stands for the + ESCAPE key; for example ESC-v means the two character sequence + "ESCAPE", then "v". + + h or H Help: display a summary of these commands. If you forget all + the other commands, remember this one. + + SPACE or ^V or f or ^F + Scroll forward N lines, default one window (see option -z + below). If N is more than the screen size, only the final + screenful is displayed. Warning: some systems use ^V as a spe- + cial literalization character. + + z Like SPACE, but if N is specified, it becomes the new window + size. + + ESC-SPACE + Like SPACE, but scrolls a full screenful, even if it reaches + end-of-file in the process. + + ENTER or RETURN or ^N or e or ^E or j or ^J + Scroll forward N lines, default 1. The entire N lines are dis- + played, even if N is more than the screen size. + + d or ^D + Scroll forward N lines, default one half of the screen size. If + N is specified, it becomes the new default for subsequent d and + u commands. + + b or ^B or ESC-v + Scroll backward N lines, default one window (see option -z + below). If N is more than the screen size, only the final + screenful is displayed. + + w Like ESC-v, but if N is specified, it becomes the new window + size. + + y or ^Y or ^P or k or ^K + Scroll backward N lines, default 1. The entire N lines are dis- + played, even if N is more than the screen size. Warning: some + systems use ^Y as a special job control character. + + u or ^U + Scroll backward N lines, default one half of the screen size. + If N is specified, it becomes the new default for subsequent d + and u commands. + + J Like j, but continues to scroll beyond the end of the file. + + K or Y Like k, but continues to scroll beyond the beginning of the + file. + + ESC-) or RIGHTARROW + Scroll horizontally right N characters, default half the screen + width (see the -# option). If a number N is specified, it + becomes the default for future RIGHTARROW and LEFTARROW com- + mands. While the text is scrolled, it acts as though the -S + option (chop lines) were in effect. + + ESC-( or LEFTARROW + Scroll horizontally left N characters, default half the screen + width (see the -# option). If a number N is specified, it + becomes the default for future RIGHTARROW and LEFTARROW com- + mands. + + ESC-} or ^RIGHTARROW + Scroll horizontally right to show the end of the longest dis- + played line. + + ESC-{ or ^LEFTARROW + Scroll horizontally left back to the first column. + + r or ^R or ^L + Repaint the screen. + + R Repaint the screen, discarding any buffered input. Useful if + the file is changing while it is being viewed. + + F Scroll forward, and keep trying to read when the end of file is + reached. Normally this command would be used when already at + the end of the file. It is a way to monitor the tail of a file + which is growing while it is being viewed. (The behavior is + similar to the "tail -f" command.) + + ESC-F Like F, but as soon as a line is found which matches the last + search pattern, the terminal bell is rung and forward scrolling + stops. + + g or < or ESC-< + Go to line N in the file, default 1 (beginning of file). (Warn- + ing: this may be slow if N is large.) + + G or > or ESC-> + Go to line N in the file, default the end of the file. (Warn- + ing: this may be slow if N is large, or if N is not specified + and standard input, rather than a file, is being read.) + + ESC-G Same as G, except if no number N is specified and the input is + standard input, goes to the last line which is currently + buffered. + + p or % Go to a position N percent into the file. N should be between 0 + and 100, and may contain a decimal point. + + P Go to the line containing byte offset N in the file. + + { If a left curly bracket appears in the top line displayed on the + screen, the { command will go to the matching right curly + bracket. The matching right curly bracket is positioned on the + bottom line of the screen. If there is more than one left curly + bracket on the top line, a number N may be used to specify the + N-th bracket on the line. + + } If a right curly bracket appears in the bottom line displayed on + the screen, the } command will go to the matching left curly + bracket. The matching left curly bracket is positioned on the + top line of the screen. If there is more than one right curly + bracket on the top line, a number N may be used to specify the + N-th bracket on the line. + + ( Like {, but applies to parentheses rather than curly brackets. + + ) Like }, but applies to parentheses rather than curly brackets. + + [ Like {, but applies to square brackets rather than curly brack- + ets. + + ] Like }, but applies to square brackets rather than curly brack- + ets. + + ESC-^F Followed by two characters, acts like {, but uses the two char- + acters as open and close brackets, respectively. For example, + "ESC ^F < >" could be used to go forward to the > which matches + the < in the top displayed line. + + ESC-^B Followed by two characters, acts like }, but uses the two char- + acters as open and close brackets, respectively. For example, + "ESC ^B < >" could be used to go backward to the < which matches + the > in the bottom displayed line. + + m Followed by any lowercase letter, marks the current position + with that letter. + + ' (Single quote.) Followed by any lowercase letter, returns to + the position which was previously marked with that letter. Fol- + lowed by another single quote, returns to the position at which + the last "large" movement command was executed. Followed by a ^ + or $, jumps to the beginning or end of the file respectively. + Marks are preserved when a new file is examined, so the ' com- + mand can be used to switch between input files. + + ^X^X Same as single quote. + + /pattern + Search forward in the file for the N-th line containing the pat- + tern. N defaults to 1. The pattern is a regular expression, as + recognized by the regular expression library supplied by your + system. The search starts at the first line displayed (but see + the -a and -j options, which change this). + + Certain characters are special if entered at the beginning of + the pattern; they modify the type of search rather than become + part of the pattern: + + ^N or ! + Search for lines which do NOT match the pattern. + + ^E or * + Search multiple files. That is, if the search reaches + the END of the current file without finding a match, the + search continues in the next file in the command line + list. + + ^F or @ + Begin the search at the first line of the FIRST file in + the command line list, regardless of what is currently + displayed on the screen or the settings of the -a or -j + options. + + ^K Highlight any text which matches the pattern on the cur- + rent screen, but don't move to the first match (KEEP cur- + rent position). + + ^R Don't interpret regular expression metacharacters; that + is, do a simple textual comparison. + + ?pattern + Search backward in the file for the N-th line containing the + pattern. The search starts at the last line displayed (but see + the -a and -j options, which change this). + + Certain characters are special as in the / command: + + ^N or ! + Search for lines which do NOT match the pattern. + + ^E or * + Search multiple files. That is, if the search reaches + the beginning of the current file without finding a + match, the search continues in the previous file in the + command line list. + + ^F or @ + Begin the search at the last line of the last file in the + command line list, regardless of what is currently dis- + played on the screen or the settings of the -a or -j + options. + + ^K As in forward searches. + + ^R As in forward searches. + + ESC-/pattern + Same as "/*". + + ESC-?pattern + Same as "?*". + + n Repeat previous search, for N-th line containing the last pat- + tern. If the previous search was modified by ^N, the search is + made for the N-th line NOT containing the pattern. If the pre- + vious search was modified by ^E, the search continues in the + next (or previous) file if not satisfied in the current file. + If the previous search was modified by ^R, the search is done + without using regular expressions. There is no effect if the + previous search was modified by ^F or ^K. + + N Repeat previous search, but in the reverse direction. + + ESC-n Repeat previous search, but crossing file boundaries. The + effect is as if the previous search were modified by *. + + ESC-N Repeat previous search, but in the reverse direction and cross- + ing file boundaries. + + ESC-u Undo search highlighting. Turn off highlighting of strings + matching the current search pattern. If highlighting is already + off because of a previous ESC-u command, turn highlighting back + on. Any search command will also turn highlighting back on. + (Highlighting can also be disabled by toggling the -G option; in + that case search commands do not turn highlighting back on.) + + &pattern + Display only lines which match the pattern; lines which do not + match the pattern are not displayed. If pattern is empty (if + you type & immediately followed by ENTER), any filtering is + turned off, and all lines are displayed. While filtering is in + effect, an ampersand is displayed at the beginning of the + prompt, as a reminder that some lines in the file may be hidden. + + Certain characters are special as in the / command: + + ^N or ! + Display only lines which do NOT match the pattern. + + ^R Don't interpret regular expression metacharacters; that + is, do a simple textual comparison. + + :e [filename] + Examine a new file. If the filename is missing, the "current" + file (see the :n and :p commands below) from the list of files + in the command line is re-examined. A percent sign (%) in the + filename is replaced by the name of the current file. A pound + sign (#) is replaced by the name of the previously examined + file. However, two consecutive percent signs are simply + replaced with a single percent sign. This allows you to enter a + filename that contains a percent sign in the name. Similarly, + two consecutive pound signs are replaced with a single pound + sign. The filename is inserted into the command line list of + files so that it can be seen by subsequent :n and :p commands. + If the filename consists of several files, they are all inserted + into the list of files and the first one is examined. If the + filename contains one or more spaces, the entire filename should + be enclosed in double quotes (also see the -" option). + + ^X^V or E + Same as :e. Warning: some systems use ^V as a special literal- + ization character. On such systems, you may not be able to use + ^V. + + :n Examine the next file (from the list of files given in the com- + mand line). If a number N is specified, the N-th next file is + examined. + + :p Examine the previous file in the command line list. If a number + N is specified, the N-th previous file is examined. + + :x Examine the first file in the command line list. If a number N + is specified, the N-th file in the list is examined. + + :d Remove the current file from the list of files. + + t Go to the next tag, if there were more than one matches for the + current tag. See the -t option for more details about tags. + + T Go to the previous tag, if there were more than one matches for + the current tag. + + = or ^G or :f + Prints some information about the file being viewed, including + its name and the line number and byte offset of the bottom line + being displayed. If possible, it also prints the length of the + file, the number of lines in the file and the percent of the + file above the last displayed line. + + - Followed by one of the command line option letters (see OPTIONS + below), this will change the setting of that option and print a + message describing the new setting. If a ^P (CONTROL-P) is + entered immediately after the dash, the setting of the option is + changed but no message is printed. If the option letter has a + numeric value (such as -b or -h), or a string value (such as -P + or -t), a new value may be entered after the option letter. If + no new value is entered, a message describing the current set- + ting is printed and nothing is changed. + + -- Like the - command, but takes a long option name (see OPTIONS + below) rather than a single option letter. You must press ENTER + or RETURN after typing the option name. A ^P immediately after + the second dash suppresses printing of a message describing the + new setting, as in the - command. + + -+ Followed by one of the command line option letters this will + reset the option to its default setting and print a message + describing the new setting. (The "-+X" command does the same + thing as "-+X" on the command line.) This does not work for + string-valued options. + + --+ Like the -+ command, but takes a long option name rather than a + single option letter. + + -! Followed by one of the command line option letters, this will + reset the option to the "opposite" of its default setting and + print a message describing the new setting. This does not work + for numeric or string-valued options. + + --! Like the -! command, but takes a long option name rather than a + single option letter. + + _ (Underscore.) Followed by one of the command line option let- + ters, this will print a message describing the current setting + of that option. The setting of the option is not changed. + + __ (Double underscore.) Like the _ (underscore) command, but takes + a long option name rather than a single option letter. You must + press ENTER or RETURN after typing the option name. + + +cmd Causes the specified cmd to be executed each time a new file is + examined. For example, +G causes less to initially display each + file starting at the end rather than the beginning. + + V Prints the version number of less being run. + + q or Q or :q or :Q or ZZ + Exits less. + + The following four commands may or may not be valid, depending on your + particular installation. + + v Invokes an editor to edit the current file being viewed. The + editor is taken from the environment variable VISUAL if defined, + or EDITOR if VISUAL is not defined, or defaults to "vi" if nei- + ther VISUAL nor EDITOR is defined. See also the discussion of + LESSEDIT under the section on PROMPTS below. + + ! shell-command + Invokes a shell to run the shell-command given. A percent sign + (%) in the command is replaced by the name of the current file. + A pound sign (#) is replaced by the name of the previously exam- + ined file. "!!" repeats the last shell command. "!" with no + shell command simply invokes a shell. On Unix systems, the + shell is taken from the environment variable SHELL, or defaults + to "sh". On MS-DOS and OS/2 systems, the shell is the normal + command processor. + + | shell-command + represents any mark letter. Pipes a section of the input + file to the given shell command. The section of the file to be + piped is between the first line on the current screen and the + position marked by the letter. may also be ^ or $ to indi- + cate beginning or end of file respectively. If is . or new- + line, the current screen is piped. + + s filename + Save the input to a file. This only works if the input is a + pipe, not an ordinary file. + +OPTIONS + Command line options are described below. Most options may be changed + while less is running, via the "-" command. + + Most options may be given in one of two forms: either a dash followed + by a single letter, or two dashes followed by a long option name. A + long option name may be abbreviated as long as the abbreviation is + unambiguous. For example, --quit-at-eof may be abbreviated --quit, but + not --qui, since both --quit-at-eof and --quiet begin with --qui. Some + long option names are in uppercase, such as --QUIT-AT-EOF, as distinct + from --quit-at-eof. Such option names need only have their first let- + ter capitalized; the remainder of the name may be in either case. For + example, --Quit-at-eof is equivalent to --QUIT-AT-EOF. + + Options are also taken from the environment variable "LESS". For exam- + ple, to avoid typing "less -options ..." each time less is invoked, you + might tell csh: + + setenv LESS "-options" + + or if you use sh: + + LESS="-options"; export LESS + + On MS-DOS, you don't need the quotes, but you should replace any per- + cent signs in the options string by double percent signs. + + The environment variable is parsed before the command line, so command + line options override the LESS environment variable. If an option + appears in the LESS variable, it can be reset to its default value on + the command line by beginning the command line option with "-+". + + Some options like -k or -D require a string to follow the option let- + ter. The string for that option is considered to end when a dollar + sign ($) is found. For example, you can set two -D options on MS-DOS + like this: + + LESS="Dn9.1$Ds4.1" + + If the --use-backslash option appears earlier in the options, then a + dollar sign or backslash may be included literally in an option string + by preceding it with a backslash. If the --use-backslash option is not + in effect, then backslashes are not treated specially, and there is no + way to include a dollar sign in the option string. + + -? or --help + This option displays a summary of the commands accepted by less + (the same as the h command). (Depending on how your shell + interprets the question mark, it may be necessary to quote the + question mark, thus: "-\?".) + + -a or --search-skip-screen + By default, forward searches start at the top of the displayed + screen and backwards searches start at the bottom of the dis- + played screen (except for repeated searches invoked by the n or + N commands, which start after or before the "target" line + respectively; see the -j option for more about the target line). + The -a option causes forward searches to instead start at the + bottom of the screen and backward searches to start at the top + of the screen, thus skipping all lines displayed on the screen. + + -A or --SEARCH-SKIP-SCREEN + Causes all forward searches (not just non-repeated searches) to + start just after the target line, and all backward searches to + start just before the target line. Thus, forward searches will + skip part of the displayed screen (from the first line up to and + including the target line). Similarly backwards searches will + skip the displayed screen from the last line up to and including + the target line. This was the default behavior in less versions + prior to 441. + + -bn or --buffers=n + Specifies the amount of buffer space less will use for each + file, in units of kilobytes (1024 bytes). By default 64 K of + buffer space is used for each file (unless the file is a pipe; + see the -B option). The -b option specifies instead that n + kilobytes of buffer space should be used for each file. If n is + -1, buffer space is unlimited; that is, the entire file can be + read into memory. + + -B or --auto-buffers + By default, when data is read from a pipe, buffers are allocated + automatically as needed. If a large amount of data is read from + the pipe, this can cause a large amount of memory to be allo- + cated. The -B option disables this automatic allocation of buf- + fers for pipes, so that only 64 K (or the amount of space speci- + fied by the -b option) is used for the pipe. Warning: use of -B + can result in erroneous display, since only the most recently + viewed part of the piped data is kept in memory; any earlier + data is lost. + + -c or --clear-screen + Causes full screen repaints to be painted from the top line + down. By default, full screen repaints are done by scrolling + from the bottom of the screen. + + -C or --CLEAR-SCREEN + Same as -c, for compatibility with older versions of less. + + -d or --dumb + The -d option suppresses the error message normally displayed if + the terminal is dumb; that is, lacks some important capability, + such as the ability to clear the screen or scroll backward. The + -d option does not otherwise change the behavior of less on a + dumb terminal. + + -Dxcolor or --color=xcolor + [MS-DOS only] Sets the color of the text displayed. x is a sin- + gle character which selects the type of text whose color is + being set: n=normal, s=standout, d=bold, u=underlined, k=blink. + color is a pair of numbers separated by a period. The first + number selects the foreground color and the second selects the + background color of the text. A single number N is the same as + N.M, where M is the normal background color. x may also be a to + toggle strict ANSI sequence rendering (SGR mode). + + + -e or --quit-at-eof + Causes less to automatically exit the second time it reaches + end-of-file. By default, the only way to exit less is via the + "q" command. + + -E or --QUIT-AT-EOF + Causes less to automatically exit the first time it reaches end- + of-file. + + -f or --force + Forces non-regular files to be opened. (A non-regular file is a + directory or a device special file.) Also suppresses the warn- + ing message when a binary file is opened. By default, less will + refuse to open non-regular files. Note that some operating sys- + tems will not allow directories to be read, even if -f is set. + + -F or --quit-if-one-screen + Causes less to automatically exit if the entire file can be dis- + played on the first screen. + + -g or --hilite-search + Normally, less will highlight ALL strings which match the last + search command. The -g option changes this behavior to high- + light only the particular string which was found by the last + search command. This can cause less to run somewhat faster than + the default. + + -G or --HILITE-SEARCH + The -G option suppresses all highlighting of strings found by + search commands. + + -hn or --max-back-scroll=n + Specifies a maximum number of lines to scroll backward. If it + is necessary to scroll backward more than n lines, the screen is + repainted in a forward direction instead. (If the terminal does + not have the ability to scroll backward, -h0 is implied.) + + -i or --ignore-case + Causes searches to ignore case; that is, uppercase and lowercase + are considered identical. This option is ignored if any upper- + case letters appear in the search pattern; in other words, if a + pattern contains uppercase letters, then that search does not + ignore case. + + -I or --IGNORE-CASE + Like -i, but searches ignore case even if the pattern contains + uppercase letters. + + -jn or --jump-target=n + Specifies a line on the screen where the "target" line is to be + positioned. The target line is the line specified by any com- + mand to search for a pattern, jump to a line number, jump to a + file percentage or jump to a tag. The screen line may be speci- + fied by a number: the top line on the screen is 1, the next is + 2, and so on. The number may be negative to specify a line rel- + ative to the bottom of the screen: the bottom line on the screen + is -1, the second to the bottom is -2, and so on. Alternately, + the screen line may be specified as a fraction of the height of + the screen, starting with a decimal point: .5 is in the middle + of the screen, .3 is three tenths down from the first line, and + so on. If the line is specified as a fraction, the actual line + number is recalculated if the terminal window is resized, so + that the target line remains at the specified fraction of the + screen height. If any form of the -j option is used, repeated + forward searches (invoked with "n" or "N") begin at the line + immediately after the target line, and repeated backward + searches begin at the target line, unless changed by -a or -A. + For example, if "-j4" is used, the target line is the fourth + line on the screen, so forward searches begin at the fifth line + on the screen. However nonrepeated searches (invoked with "/" + or "?") always begin at the start or end of the current screen + respectively. + + -J or --status-column + Displays a status column at the left edge of the screen. The + status column shows the lines that matched the current search. + The status column is also used if the -w or -W option is in + effect. + + -kfilename or --lesskey-file=filename + Causes less to open and interpret the named file as a lesskey + (1) file. Multiple -k options may be specified. If the LESSKEY + or LESSKEY_SYSTEM environment variable is set, or if a lesskey + file is found in a standard place (see KEY BINDINGS), it is also + used as a lesskey file. + + -K or --quit-on-intr + Causes less to exit immediately (with status 2) when an inter- + rupt character (usually ^C) is typed. Normally, an interrupt + character causes less to stop whatever it is doing and return to + its command prompt. Note that use of this option makes it + impossible to return to the command prompt from the "F" command. + + -L or --no-lessopen + Ignore the LESSOPEN environment variable (see the INPUT PRE- + PROCESSOR section below). This option can be set from within + less, but it will apply only to files opened subsequently, not + to the file which is currently open. + + -m or --long-prompt + Causes less to prompt verbosely (like more), with the percent + into the file. By default, less prompts with a colon. + + -M or --LONG-PROMPT + Causes less to prompt even more verbosely than more. + + -n or --line-numbers + Suppresses line numbers. The default (to use line numbers) may + cause less to run more slowly in some cases, especially with a + very large input file. Suppressing line numbers with the -n + option will avoid this problem. Using line numbers means: the + line number will be displayed in the verbose prompt and in the = + command, and the v command will pass the current line number to + the editor (see also the discussion of LESSEDIT in PROMPTS + below). + + -N or --LINE-NUMBERS + Causes a line number to be displayed at the beginning of each + line in the display. + + -ofilename or --log-file=filename + Causes less to copy its input to the named file as it is being + viewed. This applies only when the input file is a pipe, not an + ordinary file. If the file already exists, less will ask for + confirmation before overwriting it. + + -Ofilename or --LOG-FILE=filename + The -O option is like -o, but it will overwrite an existing file + without asking for confirmation. + + If no log file has been specified, the -o and -O options can be + used from within less to specify a log file. Without a file + name, they will simply report the name of the log file. The "s" + command is equivalent to specifying -o from within less. + + -ppattern or --pattern=pattern + The -p option on the command line is equivalent to specifying + +/pattern; that is, it tells less to start at the first occur- + rence of pattern in the file. + + -Pprompt or --prompt=prompt + Provides a way to tailor the three prompt styles to your own + preference. This option would normally be put in the LESS envi- + ronment variable, rather than being typed in with each less com- + mand. Such an option must either be the last option in the LESS + variable, or be terminated by a dollar sign. + -Ps followed by a string changes the default (short) prompt to + that string. + -Pm changes the medium (-m) prompt. + -PM changes the long (-M) prompt. + -Ph changes the prompt for the help screen. + -P= changes the message printed by the = command. + -Pw changes the message printed while waiting for data (in the + F command). All prompt strings consist of a sequence of letters + and special escape sequences. See the section on PROMPTS for + more details. + + -q or --quiet or --silent + Causes moderately "quiet" operation: the terminal bell is not + rung if an attempt is made to scroll past the end of the file or + before the beginning of the file. If the terminal has a "visual + bell", it is used instead. The bell will be rung on certain + other errors, such as typing an invalid character. The default + is to ring the terminal bell in all such cases. + + -Q or --QUIET or --SILENT + Causes totally "quiet" operation: the terminal bell is never + rung. + + -r or --raw-control-chars + Causes "raw" control characters to be displayed. The default is + to display control characters using the caret notation; for + example, a control-A (octal 001) is displayed as "^A". Warning: + when the -r option is used, less cannot keep track of the actual + appearance of the screen (since this depends on how the screen + responds to each type of control character). Thus, various dis- + play problems may result, such as long lines being split in the + wrong place. + + -R or --RAW-CONTROL-CHARS + Like -r, but only ANSI "color" escape sequences are output in + "raw" form. Unlike -r, the screen appearance is maintained cor- + rectly in most cases. ANSI "color" escape sequences are + sequences of the form: + + ESC [ ... m + + where the "..." is zero or more color specification characters + For the purpose of keeping track of screen appearance, ANSI + color escape sequences are assumed to not move the cursor. You + can make less think that characters other than "m" can end ANSI + color escape sequences by setting the environment variable + LESSANSIENDCHARS to the list of characters which can end a color + escape sequence. And you can make less think that characters + other than the standard ones may appear between the ESC and the + m by setting the environment variable LESSANSIMIDCHARS to the + list of characters which can appear. + + -s or --squeeze-blank-lines + Causes consecutive blank lines to be squeezed into a single + blank line. This is useful when viewing nroff output. + + -S or --chop-long-lines + Causes lines longer than the screen width to be chopped (trun- + cated) rather than wrapped. That is, the portion of a long line + that does not fit in the screen width is not shown. The default + is to wrap long lines; that is, display the remainder on the + next line. + + -ttag or --tag=tag + The -t option, followed immediately by a TAG, will edit the file + containing that tag. For this to work, tag information must be + available; for example, there may be a file in the current + directory called "tags", which was previously built by ctags (1) + or an equivalent command. If the environment variable LESSGLOB- + ALTAGS is set, it is taken to be the name of a command compati- + ble with global (1), and that command is executed to find the + tag. (See http://www.gnu.org/software/global/global.html). The + -t option may also be specified from within less (using the - + command) as a way of examining a new file. The command ":t" is + equivalent to specifying -t from within less. + + -Ttagsfile or --tag-file=tagsfile + Specifies a tags file to be used instead of "tags". + + -u or --underline-special + Causes backspaces and carriage returns to be treated as print- + able characters; that is, they are sent to the terminal when + they appear in the input. + + -U or --UNDERLINE-SPECIAL + Causes backspaces, tabs and carriage returns to be treated as + control characters; that is, they are handled as specified by + the -r option. + + By default, if neither -u nor -U is given, backspaces which + appear adjacent to an underscore character are treated spe- + cially: the underlined text is displayed using the terminal's + hardware underlining capability. Also, backspaces which appear + between two identical characters are treated specially: the + overstruck text is printed using the terminal's hardware bold- + face capability. Other backspaces are deleted, along with the + preceding character. Carriage returns immediately followed by a + newline are deleted. Other carriage returns are handled as + specified by the -r option. Text which is overstruck or under- + lined can be searched for if neither -u nor -U is in effect. + + -V or --version + Displays the version number of less. + + -w or --hilite-unread + Temporarily highlights the first "new" line after a forward + movement of a full page. The first "new" line is the line imme- + diately following the line previously at the bottom of the + screen. Also highlights the target line after a g or p command. + The highlight is removed at the next command which causes move- + ment. The entire line is highlighted, unless the -J option is + in effect, in which case only the status column is highlighted. + + -W or --HILITE-UNREAD + Like -w, but temporarily highlights the first new line after any + forward movement command larger than one line. + + -xn,... or --tabs=n,... + Sets tab stops. If only one n is specified, tab stops are set + at multiples of n. If multiple values separated by commas are + specified, tab stops are set at those positions, and then con- + tinue with the same spacing as the last two. For example, + -x9,17 will set tabs at positions 9, 17, 25, 33, etc. The + default for n is 8. + + -X or --no-init + Disables sending the termcap initialization and deinitialization + strings to the terminal. This is sometimes desirable if the + deinitialization string does something unnecessary, like clear- + ing the screen. + + -yn or --max-forw-scroll=n + Specifies a maximum number of lines to scroll forward. If it is + necessary to scroll forward more than n lines, the screen is + repainted instead. The -c or -C option may be used to repaint + from the top of the screen if desired. By default, any forward + movement causes scrolling. + + -[z]n or --window=n + Changes the default scrolling window size to n lines. The + default is one screenful. The z and w commands can also be used + to change the window size. The "z" may be omitted for compati- + bility with some versions of more. If the number n is negative, + it indicates n lines less than the current screen size. For + example, if the screen is 24 lines, -z-4 sets the scrolling win- + dow to 20 lines. If the screen is resized to 40 lines, the + scrolling window automatically changes to 36 lines. + + -"cc or --quotes=cc + Changes the filename quoting character. This may be necessary + if you are trying to name a file which contains both spaces and + quote characters. Followed by a single character, this changes + the quote character to that character. Filenames containing a + space should then be surrounded by that character rather than by + double quotes. Followed by two characters, changes the open + quote to the first character, and the close quote to the second + character. Filenames containing a space should then be preceded + by the open quote character and followed by the close quote + character. Note that even after the quote characters are + changed, this option remains -" (a dash followed by a double + quote). + + -~ or --tilde + Normally lines after end of file are displayed as a single tilde + (~). This option causes lines after end of file to be displayed + as blank lines. + + -# or --shift + Specifies the default number of positions to scroll horizontally + in the RIGHTARROW and LEFTARROW commands. If the number speci- + fied is zero, it sets the default number of positions to one + half of the screen width. Alternately, the number may be speci- + fied as a fraction of the width of the screen, starting with a + decimal point: .5 is half of the screen width, .3 is three + tenths of the screen width, and so on. If the number is speci- + fied as a fraction, the actual number of scroll positions is + recalculated if the terminal window is resized, so that the + actual scroll remains at the specified fraction of the screen + width. + + --follow-name + Normally, if the input file is renamed while an F command is + executing, less will continue to display the contents of the + original file despite its name change. If --follow-name is + specified, during an F command less will periodically attempt to + reopen the file by name. If the reopen succeeds and the file is + a different file from the original (which means that a new file + has been created with the same name as the original (now + renamed) file), less will display the contents of that new file. + + --no-keypad + Disables sending the keypad initialization and deinitialization + strings to the terminal. This is sometimes useful if the keypad + strings make the numeric keypad behave in an undesirable manner. + + --use-backslash + This option changes the interpretations of options which follow + this one. After the --use-backslash option, any backslash in an + option string is removed and the following character is taken + literally. This allows a dollar sign to be included in option + strings. + + -- A command line argument of "--" marks the end of option argu- + ments. Any arguments following this are interpreted as file- + names. This can be useful when viewing a file whose name begins + with a "-" or "+". + + + If a command line option begins with +, the remainder of that + option is taken to be an initial command to less. For example, + +G tells less to start at the end of the file rather than the + beginning, and +/xyz tells it to start at the first occurrence + of "xyz" in the file. As a special case, + acts like + +g; that is, it starts the display at the specified line + number (however, see the caveat under the "g" command above). + If the option starts with ++, the initial command applies to + every file being viewed, not just the first one. The + command + described previously may also be used to set (or change) an ini- + tial command for every file. + + +LINE EDITING + When entering command line at the bottom of the screen (for example, a + filename for the :e command, or the pattern for a search command), cer- + tain keys can be used to manipulate the command line. Most commands + have an alternate form in [ brackets ] which can be used if a key does + not exist on a particular keyboard. (Note that the forms beginning + with ESC do not work in some MS-DOS and Windows systems because ESC is + the line erase character.) Any of these special keys may be entered + literally by preceding it with the "literal" character, either ^V or + ^A. A backslash itself may also be entered literally by entering two + backslashes. + + LEFTARROW [ ESC-h ] + Move the cursor one space to the left. + + RIGHTARROW [ ESC-l ] + Move the cursor one space to the right. + + ^LEFTARROW [ ESC-b or ESC-LEFTARROW ] + (That is, CONTROL and LEFTARROW simultaneously.) Move the cur- + sor one word to the left. + + ^RIGHTARROW [ ESC-w or ESC-RIGHTARROW ] + (That is, CONTROL and RIGHTARROW simultaneously.) Move the cur- + sor one word to the right. + + HOME [ ESC-0 ] + Move the cursor to the beginning of the line. + + END [ ESC-$ ] + Move the cursor to the end of the line. + + BACKSPACE + Delete the character to the left of the cursor, or cancel the + command if the command line is empty. + + DELETE or [ ESC-x ] + Delete the character under the cursor. + + ^BACKSPACE [ ESC-BACKSPACE ] + (That is, CONTROL and BACKSPACE simultaneously.) Delete the + word to the left of the cursor. + + ^DELETE [ ESC-X or ESC-DELETE ] + (That is, CONTROL and DELETE simultaneously.) Delete the word + under the cursor. + + UPARROW [ ESC-k ] + Retrieve the previous command line. If you first enter some + text and then press UPARROW, it will retrieve the previous com- + mand which begins with that text. + + DOWNARROW [ ESC-j ] + Retrieve the next command line. If you first enter some text + and then press DOWNARROW, it will retrieve the next command + which begins with that text. + + TAB Complete the partial filename to the left of the cursor. If it + matches more than one filename, the first match is entered into + the command line. Repeated TABs will cycle thru the other + matching filenames. If the completed filename is a directory, a + "/" is appended to the filename. (On MS-DOS systems, a "\" is + appended.) The environment variable LESSSEPARATOR can be used + to specify a different character to append to a directory name. + + BACKTAB [ ESC-TAB ] + Like, TAB, but cycles in the reverse direction thru the matching + filenames. + + ^L Complete the partial filename to the left of the cursor. If it + matches more than one filename, all matches are entered into the + command line (if they fit). + + ^U (Unix and OS/2) or ESC (MS-DOS) + Delete the entire command line, or cancel the command if the + command line is empty. If you have changed your line-kill char- + acter in Unix to something other than ^U, that character is used + instead of ^U. + + ^G Delete the entire command line and return to the main prompt. + + +KEY BINDINGS + You may define your own less commands by using the program lesskey (1) + to create a lesskey file. This file specifies a set of command keys + and an action associated with each key. You may also use lesskey to + change the line-editing keys (see LINE EDITING), and to set environment + variables. If the environment variable LESSKEY is set, less uses that + as the name of the lesskey file. Otherwise, less looks in a standard + place for the lesskey file: On Unix systems, less looks for a lesskey + file called "$HOME/.less". On MS-DOS and Windows systems, less looks + for a lesskey file called "$HOME/_less", and if it is not found there, + then looks for a lesskey file called "_less" in any directory specified + in the PATH environment variable. On OS/2 systems, less looks for a + lesskey file called "$HOME/less.ini", and if it is not found, then + looks for a lesskey file called "less.ini" in any directory specified + in the INIT environment variable, and if it not found there, then looks + for a lesskey file called "less.ini" in any directory specified in the + PATH environment variable. See the lesskey manual page for more + details. + + A system-wide lesskey file may also be set up to provide key bindings. + If a key is defined in both a local lesskey file and in the system-wide + file, key bindings in the local file take precedence over those in the + system-wide file. If the environment variable LESSKEY_SYSTEM is set, + less uses that as the name of the system-wide lesskey file. Otherwise, + less looks in a standard place for the system-wide lesskey file: On + Unix systems, the system-wide lesskey file is /usr/local/etc/sysless. + (However, if less was built with a different sysconf directory than + /usr/local/etc, that directory is where the sysless file is found.) On + MS-DOS and Windows systems, the system-wide lesskey file is c:\_sys- + less. On OS/2 systems, the system-wide lesskey file is c:\sysless.ini. + + +INPUT PREPROCESSOR + You may define an "input preprocessor" for less. Before less opens a + file, it first gives your input preprocessor a chance to modify the way + the contents of the file are displayed. An input preprocessor is sim- + ply an executable program (or shell script), which writes the contents + of the file to a different file, called the replacement file. The con- + tents of the replacement file are then displayed in place of the con- + tents of the original file. However, it will appear to the user as if + the original file is opened; that is, less will display the original + filename as the name of the current file. + + An input preprocessor receives one command line argument, the original + filename, as entered by the user. It should create the replacement + file, and when finished, print the name of the replacement file to its + standard output. If the input preprocessor does not output a replace- + ment filename, less uses the original file, as normal. The input pre- + processor is not called when viewing standard input. To set up an + input preprocessor, set the LESSOPEN environment variable to a command + line which will invoke your input preprocessor. This command line + should include one occurrence of the string "%s", which will be + replaced by the filename when the input preprocessor command is + invoked. + + When less closes a file opened in such a way, it will call another pro- + gram, called the input postprocessor, which may perform any desired + clean-up action (such as deleting the replacement file created by + LESSOPEN). This program receives two command line arguments, the orig- + inal filename as entered by the user, and the name of the replacement + file. To set up an input postprocessor, set the LESSCLOSE environment + variable to a command line which will invoke your input postprocessor. + It may include two occurrences of the string "%s"; the first is + replaced with the original name of the file and the second with the + name of the replacement file, which was output by LESSOPEN. + + For example, on many Unix systems, these two scripts will allow you to + keep files in compressed format, but still let less view them directly: + + lessopen.sh: + #! /bin/sh + case "$1" in + *.Z) uncompress -c $1 >/tmp/less.$$ 2>/dev/null + if [ -s /tmp/less.$$ ]; then + echo /tmp/less.$$ + else + rm -f /tmp/less.$$ + fi + ;; + esac + + lessclose.sh: + #! /bin/sh + rm $2 + + To use these scripts, put them both where they can be executed and set + LESSOPEN="lessopen.sh %s", and LESSCLOSE="lessclose.sh %s %s". More + complex LESSOPEN and LESSCLOSE scripts may be written to accept other + types of compressed files, and so on. + + It is also possible to set up an input preprocessor to pipe the file + data directly to less, rather than putting the data into a replacement + file. This avoids the need to decompress the entire file before start- + ing to view it. An input preprocessor that works this way is called an + input pipe. An input pipe, instead of writing the name of a replace- + ment file on its standard output, writes the entire contents of the + replacement file on its standard output. If the input pipe does not + write any characters on its standard output, then there is no replace- + ment file and less uses the original file, as normal. To use an input + pipe, make the first character in the LESSOPEN environment variable a + vertical bar (|) to signify that the input preprocessor is an input + pipe. + + For example, on many Unix systems, this script will work like the pre- + vious example scripts: + + lesspipe.sh: + #! /bin/sh + case "$1" in + *.Z) uncompress -c $1 2>/dev/null + *) exit 1 + ;; + esac + exit $? + + To use this script, put it where it can be executed and set + LESSOPEN="|lesspipe.sh %s". + + Note that a preprocessor cannot output an empty file, since that is + interpreted as meaning there is no replacement, and the original file + is used. To avoid this, if LESSOPEN starts with two vertical bars, the + exit status of the script becomes meaningful. If the exit status is + zero, the output is considered to be replacement text, even if it + empty. If the exit status is nonzero, any output is ignored and the + original file is used. For compatibility with previous versions of + less, if LESSOPEN starts with only one vertical bar, the exit status of + the preprocessor is ignored. + + When an input pipe is used, a LESSCLOSE postprocessor can be used, but + it is usually not necessary since there is no replacement file to clean + up. In this case, the replacement file name passed to the LESSCLOSE + postprocessor is "-". + + For compatibility with previous versions of less, the input preproces- + sor or pipe is not used if less is viewing standard input. However, if + the first character of LESSOPEN is a dash (-), the input preprocessor + is used on standard input as well as other files. In this case, the + dash is not considered to be part of the preprocessor command. If + standard input is being viewed, the input preprocessor is passed a file + name consisting of a single dash. Similarly, if the first two charac- + ters of LESSOPEN are vertical bar and dash (|-) or two vertical bars + and a dash (||-), the input pipe is used on standard input as well as + other files. Again, in this case the dash is not considered to be part + of the input pipe command. + + +NATIONAL CHARACTER SETS + There are three types of characters in the input file: + + normal characters + can be displayed directly to the screen. + + control characters + should not be displayed directly, but are expected to be found + in ordinary text files (such as backspace and tab). + + binary characters + should not be displayed directly and are not expected to be + found in text files. + + A "character set" is simply a description of which characters are to be + considered normal, control, and binary. The LESSCHARSET environment + variable may be used to select a character set. Possible values for + LESSCHARSET are: + + ascii BS, TAB, NL, CR, and formfeed are control characters, all chars + with values between 32 and 126 are normal, and all others are + binary. + + iso8859 + Selects an ISO 8859 character set. This is the same as ASCII, + except characters between 160 and 255 are treated as normal + characters. + + latin1 Same as iso8859. + + latin9 Same as iso8859. + + dos Selects a character set appropriate for MS-DOS. + + ebcdic Selects an EBCDIC character set. + + IBM-1047 + Selects an EBCDIC character set used by OS/390 Unix Services. + This is the EBCDIC analogue of latin1. You get similar results + by setting either LESSCHARSET=IBM-1047 or LC_CTYPE=en_US in your + environment. + + koi8-r Selects a Russian character set. + + next Selects a character set appropriate for NeXT computers. + + utf-8 Selects the UTF-8 encoding of the ISO 10646 character set. + UTF-8 is special in that it supports multi-byte characters in + the input file. It is the only character set that supports + multi-byte characters. + + windows + Selects a character set appropriate for Microsoft Windows (cp + 1251). + + In rare cases, it may be desired to tailor less to use a character set + other than the ones definable by LESSCHARSET. In this case, the envi- + ronment variable LESSCHARDEF can be used to define a character set. It + should be set to a string where each character in the string represents + one character in the character set. The character "." is used for a + normal character, "c" for control, and "b" for binary. A decimal num- + ber may be used for repetition. For example, "bccc4b." would mean + character 0 is binary, 1, 2 and 3 are control, 4, 5, 6 and 7 are + binary, and 8 is normal. All characters after the last are taken to be + the same as the last, so characters 9 through 255 would be normal. + (This is an example, and does not necessarily represent any real char- + acter set.) + + This table shows the value of LESSCHARDEF which is equivalent to each + of the possible values for LESSCHARSET: + + ascii 8bcccbcc18b95.b + dos 8bcccbcc12bc5b95.b. + ebcdic 5bc6bcc7bcc41b.9b7.9b5.b..8b6.10b6.b9.7b + 9.8b8.17b3.3b9.7b9.8b8.6b10.b.b.b. + IBM-1047 4cbcbc3b9cbccbccbb4c6bcc5b3cbbc4bc4bccbc + 191.b + iso8859 8bcccbcc18b95.33b. + koi8-r 8bcccbcc18b95.b128. + latin1 8bcccbcc18b95.33b. + next 8bcccbcc18b95.bb125.bb + + If neither LESSCHARSET nor LESSCHARDEF is set, but any of the strings + "UTF-8", "UTF8", "utf-8" or "utf8" is found in the LC_ALL, LC_CTYPE or + LANG environment variables, then the default character set is utf-8. + + If that string is not found, but your system supports the setlocale + interface, less will use setlocale to determine the character set. + setlocale is controlled by setting the LANG or LC_CTYPE environment + variables. + + Finally, if the setlocale interface is also not available, the default + character set is latin1. + + Control and binary characters are displayed in standout (reverse + video). Each such character is displayed in caret notation if possible + (e.g. ^A for control-A). Caret notation is used only if inverting the + 0100 bit results in a normal printable character. Otherwise, the char- + acter is displayed as a hex number in angle brackets. This format can + be changed by setting the LESSBINFMT environment variable. LESSBINFMT + may begin with a "*" and one character to select the display attribute: + "*k" is blinking, "*d" is bold, "*u" is underlined, "*s" is standout, + and "*n" is normal. If LESSBINFMT does not begin with a "*", normal + attribute is assumed. The remainder of LESSBINFMT is a string which + may include one printf-style escape sequence (a % followed by x, X, o, + d, etc.). For example, if LESSBINFMT is "*u[%x]", binary characters + are displayed in underlined hexadecimal surrounded by brackets. The + default if no LESSBINFMT is specified is "*s<%02X>". Warning: the + result of expanding the character via LESSBINFMT must be less than 31 + characters. + + When the character set is utf-8, the LESSUTFBINFMT environment variable + acts similarly to LESSBINFMT but it applies to Unicode code points that + were successfully decoded but are unsuitable for display (e.g., unas- + signed code points). Its default value is "". Note that + LESSUTFBINFMT and LESSBINFMT share their display attribute setting + ("*x") so specifying one will affect both; LESSUTFBINFMT is read after + LESSBINFMT so its setting, if any, will have priority. Problematic + octets in a UTF-8 file (octets of a truncated sequence, octets of a + complete but non-shortest form sequence, illegal octets, and stray + trailing octets) are displayed individually using LESSBINFMT so as to + facilitate diagnostic of how the UTF-8 file is ill-formed. + + +PROMPTS + The -P option allows you to tailor the prompt to your preference. The + string given to the -P option replaces the specified prompt string. + Certain characters in the string are interpreted specially. The prompt + mechanism is rather complicated to provide flexibility, but the ordi- + nary user need not understand the details of constructing personalized + prompt strings. + + A percent sign followed by a single character is expanded according to + what the following character is: + + %bX Replaced by the byte offset into the current input file. The b + is followed by a single character (shown as X above) which spec- + ifies the line whose byte offset is to be used. If the charac- + ter is a "t", the byte offset of the top line in the display is + used, an "m" means use the middle line, a "b" means use the bot- + tom line, a "B" means use the line just after the bottom line, + and a "j" means use the "target" line, as specified by the -j + option. + + %B Replaced by the size of the current input file. + + %c Replaced by the column number of the text appearing in the first + column of the screen. + + %dX Replaced by the page number of a line in the input file. The + line to be used is determined by the X, as with the %b option. + + %D Replaced by the number of pages in the input file, or equiva- + lently, the page number of the last line in the input file. + + %E Replaced by the name of the editor (from the VISUAL environment + variable, or the EDITOR environment variable if VISUAL is not + defined). See the discussion of the LESSEDIT feature below. + + %f Replaced by the name of the current input file. + + %F Replaced by the last component of the name of the current input + file. + + %i Replaced by the index of the current file in the list of input + files. + + %lX Replaced by the line number of a line in the input file. The + line to be used is determined by the X, as with the %b option. + + %L Replaced by the line number of the last line in the input file. + + %m Replaced by the total number of input files. + + %pX Replaced by the percent into the current input file, based on + byte offsets. The line used is determined by the X as with the + %b option. + + %PX Replaced by the percent into the current input file, based on + line numbers. The line used is determined by the X as with the + %b option. + + %s Same as %B. + + %t Causes any trailing spaces to be removed. Usually used at the + end of the string, but may appear anywhere. + + %T Normally expands to the word "file". However if viewing files + via a tags list using the -t option, it expands to the word + "tag". + + %x Replaced by the name of the next input file in the list. + + If any item is unknown (for example, the file size if input is a pipe), + a question mark is printed instead. + + The format of the prompt string can be changed depending on certain + conditions. A question mark followed by a single character acts like + an "IF": depending on the following character, a condition is evalu- + ated. If the condition is true, any characters following the question + mark and condition character, up to a period, are included in the + prompt. If the condition is false, such characters are not included. + A colon appearing between the question mark and the period can be used + to establish an "ELSE": any characters between the colon and the period + are included in the string if and only if the IF condition is false. + Condition characters (which follow a question mark) may be: + + ?a True if any characters have been included in the prompt so far. + + ?bX True if the byte offset of the specified line is known. + + ?B True if the size of current input file is known. + + ?c True if the text is horizontally shifted (%c is not zero). + + ?dX True if the page number of the specified line is known. + + ?e True if at end-of-file. + + ?f True if there is an input filename (that is, if input is not a + pipe). + + ?lX True if the line number of the specified line is known. + + ?L True if the line number of the last line in the file is known. + + ?m True if there is more than one input file. + + ?n True if this is the first prompt in a new input file. + + ?pX True if the percent into the current input file, based on byte + offsets, of the specified line is known. + + ?PX True if the percent into the current input file, based on line + numbers, of the specified line is known. + + ?s Same as "?B". + + ?x True if there is a next input file (that is, if the current + input file is not the last one). + + Any characters other than the special ones (question mark, colon, + period, percent, and backslash) become literally part of the prompt. + Any of the special characters may be included in the prompt literally + by preceding it with a backslash. + + Some examples: + + ?f%f:Standard input. + + This prompt prints the filename, if known; otherwise the string "Stan- + dard input". + + ?f%f .?ltLine %lt:?pt%pt\%:?btByte %bt:-... + + This prompt would print the filename, if known. The filename is fol- + lowed by the line number, if known, otherwise the percent if known, + otherwise the byte offset if known. Otherwise, a dash is printed. + Notice how each question mark has a matching period, and how the % + after the %pt is included literally by escaping it with a backslash. + + ?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\: %x..%t"; + + This prints the filename if this is the first prompt in a file, fol- + lowed by the "file N of N" message if there is more than one input + file. Then, if we are at end-of-file, the string "(END)" is printed + followed by the name of the next file, if there is one. Finally, any + trailing spaces are truncated. This is the default prompt. For refer- + ence, here are the defaults for the other two prompts (-m and -M + respectively). Each is broken into two lines here for readability + only. + + ?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\: %x.: + ?pB%pB\%:byte %bB?s/%s...%t + + ?f%f .?n?m(%T %i of %m) ..?ltlines %lt-%lb?L/%L. : + byte %bB?s/%s. .?e(END) ?x- Next\: %x.:?pB%pB\%..%t + + And here is the default message produced by the = command: + + ?f%f .?m(%T %i of %m) .?ltlines %lt-%lb?L/%L. . + byte %bB?s/%s. ?e(END) :?pB%pB\%..%t + + The prompt expansion features are also used for another purpose: if an + environment variable LESSEDIT is defined, it is used as the command to + be executed when the v command is invoked. The LESSEDIT string is + expanded in the same way as the prompt strings. The default value for + LESSEDIT is: + + %E ?lm+%lm. %f + + Note that this expands to the editor name, followed by a + and the line + number, followed by the file name. If your editor does not accept the + "+linenumber" syntax, or has other differences in invocation syntax, + the LESSEDIT variable can be changed to modify this default. + + +SECURITY + When the environment variable LESSSECURE is set to 1, less runs in a + "secure" mode. This means these features are disabled: + + ! the shell command + + | the pipe command + + :e the examine command. + + v the editing command + + s -o log files + + -k use of lesskey files + + -t use of tags files + + metacharacters in filenames, such as * + + filename completion (TAB, ^L) + + Less can also be compiled to be permanently in "secure" mode. + + +COMPATIBILITY WITH MORE + If the environment variable LESS_IS_MORE is set to 1, or if the program + is invoked via a file link named "more", less behaves (mostly) in con- + formance with the POSIX "more" command specification. In this mode, + less behaves differently in these ways: + + The -e option works differently. If the -e option is not set, less + behaves as if the -e option were set. If the -e option is set, less + behaves as if the -E option were set. + + The -m option works differently. If the -m option is not set, the + medium prompt is used, and it is prefixed with the string "--More--". + If the -m option is set, the short prompt is used. + + The -n option acts like the -z option. The normal behavior of the -n + option is unavailable in this mode. + + The parameter to the -p option is taken to be a less command rather + than a search pattern. + + The LESS environment variable is ignored, and the MORE environment + variable is used in its place. + + +ENVIRONMENT VARIABLES + Environment variables may be specified either in the system environment + as usual, or in a lesskey (1) file. If environment variables are + defined in more than one place, variables defined in a local lesskey + file take precedence over variables defined in the system environment, + which take precedence over variables defined in the system-wide lesskey + file. + + COLUMNS + Sets the number of columns on the screen. Takes precedence over + the number of columns specified by the TERM variable. (But if + you have a windowing system which supports TIOCGWINSZ or + WIOCGETD, the window system's idea of the screen size takes + precedence over the LINES and COLUMNS environment variables.) + + EDITOR The name of the editor (used for the v command). + + HOME Name of the user's home directory (used to find a lesskey file + on Unix and OS/2 systems). + + HOMEDRIVE, HOMEPATH + Concatenation of the HOMEDRIVE and HOMEPATH environment vari- + ables is the name of the user's home directory if the HOME vari- + able is not set (only in the Windows version). + + INIT Name of the user's init directory (used to find a lesskey file + on OS/2 systems). + + LANG Language for determining the character set. + + LC_CTYPE + Language for determining the character set. + + LESS Options which are passed to less automatically. + + LESSANSIENDCHARS + Characters which may end an ANSI color escape sequence (default + "m"). + + LESSANSIMIDCHARS + Characters which may appear between the ESC character and the + end character in an ANSI color escape sequence (default + "0123456789:;[?!"'#%()*+ ". + + LESSBINFMT + Format for displaying non-printable, non-control characters. + + LESSCHARDEF + Defines a character set. + + LESSCHARSET + Selects a predefined character set. + + LESSCLOSE + Command line to invoke the (optional) input-postprocessor. + + LESSECHO + Name of the lessecho program (default "lessecho"). The lessecho + program is needed to expand metacharacters, such as * and ?, in + filenames on Unix systems. + + LESSEDIT + Editor prototype string (used for the v command). See discus- + sion under PROMPTS. + + LESSGLOBALTAGS + Name of the command used by the -t option to find global tags. + Normally should be set to "global" if your system has the global + (1) command. If not set, global tags are not used. + + LESSHISTFILE + Name of the history file used to remember search commands and + shell commands between invocations of less. If set to "-" or + "/dev/null", a history file is not used. The default is + "$HOME/.lesshst" on Unix systems, "$HOME/_lesshst" on DOS and + Windows systems, or "$HOME/lesshst.ini" or "$INIT/lesshst.ini" + on OS/2 systems. + + LESSHISTSIZE + The maximum number of commands to save in the history file. The + default is 100. + + LESSKEY + Name of the default lesskey(1) file. + + LESSKEY_SYSTEM + Name of the default system-wide lesskey(1) file. + + LESSMETACHARS + List of characters which are considered "metacharacters" by the + shell. + + LESSMETAESCAPE + Prefix which less will add before each metacharacter in a com- + mand sent to the shell. If LESSMETAESCAPE is an empty string, + commands containing metacharacters will not be passed to the + shell. + + LESSOPEN + Command line to invoke the (optional) input-preprocessor. + + LESSSECURE + Runs less in "secure" mode. See discussion under SECURITY. + + LESSSEPARATOR + String to be appended to a directory name in filename comple- + tion. + + LESSUTFBINFMT + Format for displaying non-printable Unicode code points. + + LESS_IS_MORE + Emulate the more (1) command. + + LINES Sets the number of lines on the screen. Takes precedence over + the number of lines specified by the TERM variable. (But if you + have a windowing system which supports TIOCGWINSZ or WIOCGETD, + the window system's idea of the screen size takes precedence + over the LINES and COLUMNS environment variables.) + + MORE Options which are passed to less automatically when running in + more compatible mode. + + PATH User's search path (used to find a lesskey file on MS-DOS and + OS/2 systems). + + SHELL The shell used to execute the ! command, as well as to expand + filenames. + + TERM The type of terminal on which less is being run. + + VISUAL The name of the editor (used for the v command). + + +SEE ALSO + lesskey(1) + + +COPYRIGHT + Copyright (C) 1984-2016 Mark Nudelman + + less is part of the GNU project and is free software. You can redis- + tribute it and/or modify it under the terms of either (1) the GNU Gen- + eral Public License as published by the Free Software Foundation; or + (2) the Less License. See the file README in the less distribution for + more details regarding redistribution. You should have received a copy + of the GNU General Public License along with the source for less; see + the file COPYING. If not, write to the Free Software Foundation, 59 + Temple Place, Suite 330, Boston, MA 02111-1307, USA. You should also + have received a copy of the Less License; see the file LICENSE. + + less is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FIT- + NESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + +AUTHOR + Mark Nudelman + Send bug reports or comments to + See http://www.greenwoodsoftware.com/less/bugs.html for the latest list + of known bugs in less. + For more information, see the less homepage at + http://www.greenwoodsoftware.com/less. + + + + Version 487: 25 Oct 2016 LESS(1) diff --git a/files/Sources/files/less/less.nro b/files/Sources/files/less/less.nro new file mode 100644 index 00000000..c08ffd64 --- /dev/null +++ b/files/Sources/files/less/less.nro @@ -0,0 +1,1779 @@ +.TH LESS 1 "Version 487: 25 Oct 2016" +.SH NAME +less \- opposite of more +.SH SYNOPSIS +.B "less \-?" +.br +.B "less \-\-help" +.br +.B "less \-V" +.br +.B "less \-\-version" +.br +.B "less [\-[+]aABcCdeEfFgGiIJKLmMnNqQrRsSuUVwWX~]" +.br +.B " [\-b \fIspace\/\fP] [\-h \fIlines\/\fP] [\-j \fIline\/\fP] [\-k \fIkeyfile\/\fP]" +.br +.B " [\-{oO} \fIlogfile\/\fP] [\-p \fIpattern\/\fP] [\-P \fIprompt\/\fP] [\-t \fItag\/\fP]" +.br +.B " [\-T \fItagsfile\/\fP] [\-x \fItab\/\fP,...] [\-y \fIlines\/\fP] [\-[z] \fIlines\/\fP]" +.br +.B " [\-# \fIshift\/\fP] [+[+]\fIcmd\/\fP] [\-\-] [\fIfilename\/\fP]..." +.br +(See the OPTIONS section for alternate option syntax with long option names.) + +.SH DESCRIPTION +.I Less +is a program similar to +.I more +(1), but which allows backward movement +in the file as well as forward movement. +Also, +.I less +does not have to read the entire input file before starting, +so with large input files it starts up faster than text editors like +.I vi +(1). +.I Less +uses termcap (or terminfo on some systems), +so it can run on a variety of terminals. +There is even limited support for hardcopy terminals. +(On a hardcopy terminal, lines which should be printed at the top +of the screen are prefixed with a caret.) +.PP +Commands are based on both +.I more +and +.I vi. +Commands may be preceded by a decimal number, +called N in the descriptions below. +The number is used by some commands, as indicated. + +.SH COMMANDS +In the following descriptions, ^X means control-X. +ESC stands for the ESCAPE key; for example ESC-v means the +two character sequence "ESCAPE", then "v". +.IP "h or H" +Help: display a summary of these commands. +If you forget all the other commands, remember this one. +.IP "SPACE or ^V or f or ^F" +Scroll forward N lines, default one window (see option \-z below). +If N is more than the screen size, only the final screenful is displayed. +Warning: some systems use ^V as a special literalization character. +.IP "z" +Like SPACE, but if N is specified, it becomes the new window size. +.IP "ESC-SPACE" +Like SPACE, but scrolls a full screenful, even if it reaches +end-of-file in the process. +.IP "ENTER or RETURN or ^N or e or ^E or j or ^J" +Scroll forward N lines, default 1. +The entire N lines are displayed, even if N is more than the screen size. +.IP "d or ^D" +Scroll forward N lines, default one half of the screen size. +If N is specified, it becomes the new default for +subsequent d and u commands. +.IP "b or ^B or ESC-v" +Scroll backward N lines, default one window (see option \-z below). +If N is more than the screen size, only the final screenful is displayed. +.IP "w" +Like ESC-v, but if N is specified, it becomes the new window size. +.IP "y or ^Y or ^P or k or ^K" +Scroll backward N lines, default 1. +The entire N lines are displayed, even if N is more than the screen size. +Warning: some systems use ^Y as a special job control character. +.IP "u or ^U" +Scroll backward N lines, default one half of the screen size. +If N is specified, it becomes the new default for +subsequent d and u commands. +.IP "J" +Like j, but continues to scroll beyond the end of the file. +.IP "K or Y" +Like k, but continues to scroll beyond the beginning of the file. +.IP "ESC-) or RIGHTARROW" +Scroll horizontally right N characters, default half the screen width +(see the \-# option). +If a number N is specified, it becomes the default for future RIGHTARROW +and LEFTARROW commands. +While the text is scrolled, it acts as though the \-S option +(chop lines) were in effect. +.IP "ESC-( or LEFTARROW" +Scroll horizontally left N characters, default half the screen width +(see the \-# option). +If a number N is specified, it becomes the default for future RIGHTARROW +and LEFTARROW commands. +.IP "ESC-} or ^RIGHTARROW" +Scroll horizontally right to show the end of the longest displayed line. +.IP "ESC-{ or ^LEFTARROW" +Scroll horizontally left back to the first column. +.IP "r or ^R or ^L" +Repaint the screen. +.IP R +Repaint the screen, discarding any buffered input. +Useful if the file is changing while it is being viewed. +.IP "F" +Scroll forward, and keep trying to read when the +end of file is reached. +Normally this command would be used when already at the end of the file. +It is a way to monitor the tail of a file which is growing +while it is being viewed. +(The behavior is similar to the "tail \-f" command.) +.IP "ESC-F" +Like F, but as soon as a line is found which matches +the last search pattern, the terminal bell is rung +and forward scrolling stops. +.IP "g or < or ESC-<" +Go to line N in the file, default 1 (beginning of file). +(Warning: this may be slow if N is large.) +.IP "G or > or ESC->" +Go to line N in the file, default the end of the file. +(Warning: this may be slow if N is large, +or if N is not specified and +standard input, rather than a file, is being read.) +.IP "ESC-G" +Same as G, except if no number N is specified and the input is standard input, +goes to the last line which is currently buffered. +.IP "p or %" +Go to a position N percent into the file. +N should be between 0 and 100, and may contain a decimal point. +.IP "P" +Go to the line containing byte offset N in the file. +.IP "{" +If a left curly bracket appears in the top line displayed +on the screen, +the { command will go to the matching right curly bracket. +The matching right curly bracket is positioned on the bottom +line of the screen. +If there is more than one left curly bracket on the top line, +a number N may be used to specify the N-th bracket on the line. +.IP "}" +If a right curly bracket appears in the bottom line displayed +on the screen, +the } command will go to the matching left curly bracket. +The matching left curly bracket is positioned on the top +line of the screen. +If there is more than one right curly bracket on the top line, +a number N may be used to specify the N-th bracket on the line. +.IP "(" +Like {, but applies to parentheses rather than curly brackets. +.IP ")" +Like }, but applies to parentheses rather than curly brackets. +.IP "[" +Like {, but applies to square brackets rather than curly brackets. +.IP "]" +Like }, but applies to square brackets rather than curly brackets. +.IP "ESC-^F" +Followed by two characters, +acts like {, but uses the two characters as open and close brackets, +respectively. +For example, "ESC ^F < >" could be used to +go forward to the > which matches the < in the top displayed line. +.IP "ESC-^B" +Followed by two characters, +acts like }, but uses the two characters as open and close brackets, +respectively. +For example, "ESC ^B < >" could be used to +go backward to the < which matches the > in the bottom displayed line. +.IP m +Followed by any lowercase letter, +marks the current position with that letter. +.IP "'" +(Single quote.) +Followed by any lowercase letter, returns to the position which +was previously marked with that letter. +Followed by another single quote, returns to the position at +which the last "large" movement command was executed. +Followed by a ^ or $, jumps to the beginning or end of the +file respectively. +Marks are preserved when a new file is examined, +so the ' command can be used to switch between input files. +.IP "^X^X" +Same as single quote. +.IP /pattern +Search forward in the file for the N-th line containing the pattern. +N defaults to 1. +The pattern is a regular expression, as recognized by +the regular expression library supplied by your system. +The search starts at the first line displayed +(but see the \-a and \-j options, which change this). +.sp +Certain characters are special +if entered at the beginning of the pattern; +they modify the type of search rather than become part of the pattern: +.RS +.IP "^N or !" +Search for lines which do NOT match the pattern. +.IP "^E or *" +Search multiple files. +That is, if the search reaches the END of the current file +without finding a match, +the search continues in the next file in the command line list. +.IP "^F or @" +Begin the search at the first line of the FIRST file +in the command line list, +regardless of what is currently displayed on the screen +or the settings of the \-a or \-j options. +.IP "^K" +Highlight any text which matches the pattern on the current screen, +but don't move to the first match (KEEP current position). +.IP "^R" +Don't interpret regular expression metacharacters; +that is, do a simple textual comparison. +.RE +.IP ?pattern +Search backward in the file for the N-th line containing the pattern. +The search starts at the last line displayed +(but see the \-a and \-j options, which change this). +.sp +Certain characters are special as in the / command: +.RS +.IP "^N or !" +Search for lines which do NOT match the pattern. +.IP "^E or *" +Search multiple files. +That is, if the search reaches the beginning of the current file +without finding a match, +the search continues in the previous file in the command line list. +.IP "^F or @" +Begin the search at the last line of the last file +in the command line list, +regardless of what is currently displayed on the screen +or the settings of the \-a or \-j options. +.IP "^K" +As in forward searches. +.IP "^R" +As in forward searches. +.RE +.IP "ESC-/pattern" +Same as "/*". +.IP "ESC-?pattern" +Same as "?*". +.IP n +Repeat previous search, for N-th line containing the last pattern. +If the previous search was modified by ^N, the search is made for the +N-th line NOT containing the pattern. +If the previous search was modified by ^E, the search continues +in the next (or previous) file if not satisfied in the current file. +If the previous search was modified by ^R, the search is done +without using regular expressions. +There is no effect if the previous search was modified by ^F or ^K. +.IP N +Repeat previous search, but in the reverse direction. +.IP "ESC-n" +Repeat previous search, but crossing file boundaries. +The effect is as if the previous search were modified by *. +.IP "ESC-N" +Repeat previous search, but in the reverse direction +and crossing file boundaries. +.IP "ESC-u" +Undo search highlighting. +Turn off highlighting of strings matching the current search pattern. +If highlighting is already off because of a previous ESC-u command, +turn highlighting back on. +Any search command will also turn highlighting back on. +(Highlighting can also be disabled by toggling the \-G option; +in that case search commands do not turn highlighting back on.) +.IP "&pattern" +Display only lines which match the pattern; +lines which do not match the pattern are not displayed. +If pattern is empty (if you type & immediately followed by ENTER), +any filtering is turned off, and all lines are displayed. +While filtering is in effect, an ampersand is displayed at the +beginning of the prompt, +as a reminder that some lines in the file may be hidden. +.sp +Certain characters are special as in the / command: +.RS +.IP "^N or !" +Display only lines which do NOT match the pattern. +.IP "^R" +Don't interpret regular expression metacharacters; +that is, do a simple textual comparison. +.RE +.IP ":e [filename]" +Examine a new file. +If the filename is missing, the "current" file (see the :n and :p commands +below) from the list of files in the command line is re-examined. +A percent sign (%) in the filename is replaced by the name of the +current file. +A pound sign (#) is replaced by the name of the previously examined file. +However, two consecutive percent signs are simply +replaced with a single percent sign. +This allows you to enter a filename that contains a percent sign +in the name. +Similarly, two consecutive pound signs are replaced with a single pound sign. +The filename is inserted into the command line list of files +so that it can be seen by subsequent :n and :p commands. +If the filename consists of several files, they are all inserted into +the list of files and the first one is examined. +If the filename contains one or more spaces, +the entire filename should be enclosed in double quotes +(also see the \-" option). +.IP "^X^V or E" +Same as :e. +Warning: some systems use ^V as a special literalization character. +On such systems, you may not be able to use ^V. +.IP ":n" +Examine the next file (from the list of files given in the command line). +If a number N is specified, the N-th next file is examined. +.IP ":p" +Examine the previous file in the command line list. +If a number N is specified, the N-th previous file is examined. +.IP ":x" +Examine the first file in the command line list. +If a number N is specified, the N-th file in the list is examined. +.IP ":d" +Remove the current file from the list of files. +.IP "t" +Go to the next tag, if there were more than one matches for the current tag. +See the \-t option for more details about tags. +.IP "T" +Go to the previous tag, if there were more than one matches for the current tag. +.IP "= or ^G or :f" +Prints some information about the file being viewed, +including its name +and the line number and byte offset of the bottom line being displayed. +If possible, it also prints the length of the file, +the number of lines in the file +and the percent of the file above the last displayed line. +.IP \- +Followed by one of the command line option letters (see OPTIONS below), +this will change the setting of that option +and print a message describing the new setting. +If a ^P (CONTROL-P) is entered immediately after the dash, +the setting of the option is changed but no message is printed. +If the option letter has a numeric value (such as \-b or \-h), +or a string value (such as \-P or \-t), +a new value may be entered after the option letter. +If no new value is entered, a message describing +the current setting is printed and nothing is changed. +.IP \-\- +Like the \- command, but takes a long option name (see OPTIONS below) +rather than a single option letter. +You must press ENTER or RETURN after typing the option name. +A ^P immediately after the second dash suppresses printing of a +message describing the new setting, as in the \- command. +.IP \-+ +Followed by one of the command line option letters +this will reset the option to its default setting +and print a message describing the new setting. +(The "\-+\fIX\fP" command does the same thing +as "\-+\fIX\fP" on the command line.) +This does not work for string-valued options. +.IP \-\-+ +Like the \-+ command, but takes a long option name +rather than a single option letter. +.IP \-! +Followed by one of the command line option letters, +this will reset the option to the "opposite" of its default setting +and print a message describing the new setting. +This does not work for numeric or string-valued options. +.IP \-\-! +Like the \-! command, but takes a long option name +rather than a single option letter. +.IP _ +(Underscore.) +Followed by one of the command line option letters, +this will print a message describing the current setting of that option. +The setting of the option is not changed. +.IP __ +(Double underscore.) +Like the _ (underscore) command, but takes a long option name +rather than a single option letter. +You must press ENTER or RETURN after typing the option name. +.IP +cmd +Causes the specified cmd to be executed each time a new file is examined. +For example, +G causes +.I less +to initially display each file starting at the end +rather than the beginning. +.IP V +Prints the version number of +.I less +being run. +.IP "q or Q or :q or :Q or ZZ" +Exits +.I less. +.PP +The following +four +commands may or may not be valid, depending on your particular installation. +.PP +.IP v +Invokes an editor to edit the current file being viewed. +The editor is taken from the environment variable VISUAL if defined, +or EDITOR if VISUAL is not defined, +or defaults to "vi" if neither VISUAL nor EDITOR is defined. +See also the discussion of LESSEDIT under the section on PROMPTS below. +.IP "! shell-command" +Invokes a shell to run the shell-command given. +A percent sign (%) in the command is replaced by the name of the +current file. +A pound sign (#) is replaced by the name of the previously examined file. +"!!" repeats the last shell command. +"!" with no shell command simply invokes a shell. +On Unix systems, the shell is taken from the environment variable SHELL, +or defaults to "sh". +On MS-DOS and OS/2 systems, the shell is the normal command processor. +.IP "| shell-command" + represents any mark letter. +Pipes a section of the input file to the given shell command. +The section of the file to be piped is between the first line on +the current screen and the position marked by the letter. + may also be ^ or $ to indicate beginning or end of file respectively. +If is \&.\& or newline, the current screen is piped. +.IP "s filename" +Save the input to a file. +This only works if the input is a pipe, not an ordinary file. +.PP +.SH OPTIONS +Command line options are described below. +Most options may be changed while +.I less +is running, via the "\-" command. +.PP +Most options may be given in one of two forms: +either a dash followed by a single letter, +or two dashes followed by a long option name. +A long option name may be abbreviated as long as +the abbreviation is unambiguous. +For example, \-\-quit-at-eof may be abbreviated \-\-quit, but not +\-\-qui, since both \-\-quit-at-eof and \-\-quiet begin with \-\-qui. +Some long option names are in uppercase, such as \-\-QUIT-AT-EOF, as +distinct from \-\-quit-at-eof. +Such option names need only have their first letter capitalized; +the remainder of the name may be in either case. +For example, \-\-Quit-at-eof is equivalent to \-\-QUIT-AT-EOF. +.PP +Options are also taken from the environment variable "LESS". +For example, +to avoid typing "less \-options \&...\&" each time +.I less +is invoked, you might tell +.I csh: +.sp +setenv LESS "\-options" +.sp +or if you use +.I sh: +.sp +LESS="\-options"; export LESS +.sp +On MS-DOS, you don't need the quotes, but you should replace any +percent signs in the options string by double percent signs. +.sp +The environment variable is parsed before the command line, +so command line options override the LESS environment variable. +If an option appears in the LESS variable, it can be reset +to its default value on the command line by beginning the command +line option with "\-+". +.sp +Some options like \-k or \-D require a string to follow the option letter. +The string for that option is considered to end when a dollar sign ($) is found. +For example, you can set two \-D options on MS-DOS like this: +.sp +LESS="Dn9.1$Ds4.1" +.sp +If the \-\-use-backslash option appears earlier in the options, then +a dollar sign or backslash may be included literally in an option string +by preceding it with a backslash. +If the \-\-use-backslash option is not in effect, then backslashes are +not treated specially, and there is no way to include a dollar sign +in the option string. +.IP "\-? or \-\-help" +This option displays a summary of the commands accepted by +.I less +(the same as the h command). +(Depending on how your shell interprets the question mark, +it may be necessary to quote the question mark, thus: "\-\e?".) +.IP "\-a or \-\-search-skip-screen" +By default, forward searches start at the top of the displayed screen +and backwards searches start at the bottom of the displayed screen +(except for repeated searches invoked by the n or N commands, +which start after or before the "target" line respectively; +see the \-j option for more about the target line). +The \-a option causes forward searches to instead start at +the bottom of the screen +and backward searches to start at the top of the screen, +thus skipping all lines displayed on the screen. +.IP "\-A or \-\-SEARCH-SKIP-SCREEN" +Causes all forward searches (not just non-repeated searches) +to start just after the target line, and all backward searches +to start just before the target line. +Thus, forward searches will skip part of the displayed screen +(from the first line up to and including the target line). +Similarly backwards searches will skip the displayed screen +from the last line up to and including the target line. +This was the default behavior in less versions prior to 441. +.IP "\-b\fIn\fP or \-\-buffers=\fIn\fP" +Specifies the amount of buffer space +.I less +will use for each file, in units of kilobytes (1024 bytes). +By default 64\ K of buffer space is used for each file +(unless the file is a pipe; see the \-B option). +The \-b option specifies instead that \fIn\fP kilobytes of +buffer space should be used for each file. +If \fIn\fP is \-1, buffer space is unlimited; that is, +the entire file can be read into memory. +.IP "\-B or \-\-auto-buffers" +By default, when data is read from a pipe, +buffers are allocated automatically as needed. +If a large amount of data is read from the pipe, this can cause +a large amount of memory to be allocated. +The \-B option disables this automatic allocation of buffers for pipes, +so that only 64\ K +(or the amount of space specified by the \-b option) +is used for the pipe. +Warning: use of \-B can result in erroneous display, since only the +most recently viewed part of the piped data is kept in memory; +any earlier data is lost. +.IP "\-c or \-\-clear-screen" +Causes full screen repaints to be painted from the top line down. +By default, +full screen repaints are done by scrolling from the bottom of the screen. +.IP "\-C or \-\-CLEAR-SCREEN" +Same as \-c, for compatibility with older versions of +.I less. +.IP "\-d or \-\-dumb" +The \-d option suppresses the error message +normally displayed if the terminal is dumb; +that is, lacks some important capability, +such as the ability to clear the screen or scroll backward. +The \-d option does not otherwise change the behavior of +.I less +on a dumb terminal. +.IP "\-D\fBx\fP\fIcolor\fP or \-\-color=\fBx\fP\fIcolor\fP" +[MS-DOS only] +Sets the color of the text displayed. +\fBx\fP is a single character which selects the type of text whose color is +being set: n=normal, s=standout, d=bold, u=underlined, k=blink. +\fIcolor\fP is a pair of numbers separated by a period. +The first number selects the foreground color and the second selects +the background color of the text. +A single number \fIN\fP is the same as \fIN.M\fP, +where \fIM\fP is the normal background color. +\fBx\fP may also be \fBa\fP to toggle strict ANSI sequence rendering (SGR mode). + +.IP "\-e or \-\-quit-at-eof" +Causes +.I less +to automatically exit +the second time it reaches end-of-file. +By default, the only way to exit +.I less +is via the "q" command. +.IP "\-E or \-\-QUIT-AT-EOF" +Causes +.I less +to automatically exit the first time it reaches end-of-file. +.IP "\-f or \-\-force" +Forces non-regular files to be opened. +(A non-regular file is a directory or a device special file.) +Also suppresses the warning message when a binary file is opened. +By default, +.I less +will refuse to open non-regular files. +Note that some operating systems will not allow directories +to be read, even if \-f is set. +.IP "\-F or \-\-quit-if-one-screen" +Causes +.I less +to automatically exit +if the entire file can be displayed on the first screen. +.IP "\-g or \-\-hilite-search" +Normally, +.I less +will highlight ALL strings which match the last search command. +The \-g option changes this behavior to highlight only the particular string +which was found by the last search command. +This can cause +.I less +to run somewhat faster than the default. +.IP "\-G or \-\-HILITE-SEARCH" +The \-G option suppresses all highlighting of strings found by search commands. +.IP "\-h\fIn\fP or \-\-max-back-scroll=\fIn\fP" +Specifies a maximum number of lines to scroll backward. +If it is necessary to scroll backward more than \fIn\fP lines, +the screen is repainted in a forward direction instead. +(If the terminal does not have the ability to scroll +backward, \-h0 is implied.) +.IP "\-i or \-\-ignore-case" +Causes searches to ignore case; that is, +uppercase and lowercase are considered identical. +This option is ignored if any uppercase letters +appear in the search pattern; +in other words, +if a pattern contains uppercase letters, then that search does not ignore case. +.IP "\-I or \-\-IGNORE-CASE" +Like \-i, but searches ignore case even if +the pattern contains uppercase letters. +.IP "\-j\fIn\fP or \-\-jump-target=\fIn\fP" +Specifies a line on the screen where the "target" line +is to be positioned. +The target line is the line specified by any command to +search for a pattern, jump to a line number, +jump to a file percentage or jump to a tag. +The screen line may be specified by a number: the top line on the screen +is 1, the next is 2, and so on. +The number may be negative to specify a line relative to the bottom +of the screen: the bottom line on the screen is \-1, the second +to the bottom is \-2, and so on. +Alternately, the screen line may be specified as a fraction of the height +of the screen, starting with a decimal point: \&.5 is in the middle of the +screen, \&.3 is three tenths down from the first line, and so on. +If the line is specified as a fraction, the actual line number +is recalculated if the terminal window is resized, so that the +target line remains at the specified fraction of the screen height. +If any form of the \-j option is used, +repeated forward searches (invoked with "n" or "N") +begin at the line immediately after the target line, +and repeated backward searches begin at the target line, +unless changed by \-a or \-A. +For example, if "\-j4" is used, the target line is the +fourth line on the screen, so forward searches begin at the fifth line +on the screen. +However nonrepeated searches (invoked with "/" or "?") +always begin at the start or end of the current screen respectively. +.IP "\-J or \-\-status-column" +Displays a status column at the left edge of the screen. +The status column shows the lines that matched the current search. +The status column is also used if the \-w or \-W option is in effect. +.IP "\-k\fIfilename\fP or \-\-lesskey-file=\fIfilename\fP" +Causes +.I less +to open and interpret the named file as a +.I lesskey +(1) file. +Multiple \-k options may be specified. +If the LESSKEY or LESSKEY_SYSTEM environment variable is set, or +if a lesskey file is found in a standard place (see KEY BINDINGS), +it is also used as a +.I lesskey +file. +.IP "\-K or \-\-quit-on-intr" +Causes +.I less +to exit immediately (with status 2) +when an interrupt character (usually ^C) is typed. +Normally, an interrupt character causes +.I less +to stop whatever it is doing and return to its command prompt. +Note that use of this option makes it impossible to return to the +command prompt from the "F" command. +.IP "\-L or \-\-no-lessopen" +Ignore the LESSOPEN environment variable +(see the INPUT PREPROCESSOR section below). +This option can be set from within \fIless\fP, +but it will apply only to files opened subsequently, not to the +file which is currently open. +.IP "\-m or \-\-long-prompt" +Causes +.I less +to prompt verbosely (like \fImore\fP), +with the percent into the file. +By default, +.I less +prompts with a colon. +.IP "\-M or \-\-LONG-PROMPT" +Causes +.I less +to prompt even more verbosely than +.I more. +.IP "\-n or \-\-line-numbers" +Suppresses line numbers. +The default (to use line numbers) may cause +.I less +to run more slowly in some cases, especially with a very large input file. +Suppressing line numbers with the \-n option will avoid this problem. +Using line numbers means: the line number will be displayed in the verbose +prompt and in the = command, +and the v command will pass the current line number to the editor +(see also the discussion of LESSEDIT in PROMPTS below). +.IP "\-N or \-\-LINE-NUMBERS" +Causes a line number to be displayed at the beginning of +each line in the display. +.IP "\-o\fIfilename\fP or \-\-log-file=\fIfilename\fP" +Causes +.I less +to copy its input to the named file as it is being viewed. +This applies only when the input file is a pipe, +not an ordinary file. +If the file already exists, +.I less +will ask for confirmation before overwriting it. +.IP "\-O\fIfilename\fP or \-\-LOG-FILE=\fIfilename\fP" +The \-O option is like \-o, but it will overwrite an existing +file without asking for confirmation. +.sp +If no log file has been specified, +the \-o and \-O options can be used from within +.I less +to specify a log file. +Without a file name, they will simply report the name of the log file. +The "s" command is equivalent to specifying \-o from within +.I less. +.IP "\-p\fIpattern\fP or \-\-pattern=\fIpattern\fP" +The \-p option on the command line is equivalent to +specifying +/\fIpattern\fP; +that is, it tells +.I less +to start at the first occurrence of \fIpattern\fP in the file. +.IP "\-P\fIprompt\fP or \-\-prompt=\fIprompt\fP" +Provides a way to tailor the three prompt +styles to your own preference. +This option would normally be put in the LESS environment +variable, rather than being typed in with each +.I less +command. +Such an option must either be the last option in the LESS variable, +or be terminated by a dollar sign. + \-Ps followed by a string changes the default (short) prompt +to that string. + \-Pm changes the medium (\-m) prompt. + \-PM changes the long (\-M) prompt. + \-Ph changes the prompt for the help screen. + \-P= changes the message printed by the = command. + \-Pw changes the message printed while waiting for data (in the F command). +All prompt strings consist of a sequence of +letters and special escape sequences. +See the section on PROMPTS for more details. +.IP "\-q or \-\-quiet or \-\-silent" +Causes moderately "quiet" operation: +the terminal bell is not rung +if an attempt is made to scroll past the end of the file +or before the beginning of the file. +If the terminal has a "visual bell", it is used instead. +The bell will be rung on certain other errors, +such as typing an invalid character. +The default is to ring the terminal bell in all such cases. +.IP "\-Q or \-\-QUIET or \-\-SILENT" +Causes totally "quiet" operation: +the terminal bell is never rung. +.IP "\-r or \-\-raw-control-chars" +Causes "raw" control characters to be displayed. +The default is to display control characters using the caret notation; +for example, a control-A (octal 001) is displayed as "^A". +Warning: when the \-r option is used, +.I less +cannot keep track of the actual appearance of the screen +(since this depends on how the screen responds to +each type of control character). +Thus, various display problems may result, +such as long lines being split in the wrong place. +.IP "\-R or \-\-RAW-CONTROL-CHARS" +Like \-r, but only ANSI "color" escape sequences are output in "raw" form. +Unlike \-r, the screen appearance is maintained correctly in most cases. +ANSI "color" escape sequences are sequences of the form: +.sp + ESC [ \&...\& m +.sp +where the "...\&" is zero or more color specification characters +For the purpose of keeping track of screen appearance, +ANSI color escape sequences are assumed to not move the cursor. +You can make +.I less +think that characters other than "m" can end ANSI color escape sequences +by setting the environment variable LESSANSIENDCHARS to the list of +characters which can end a color escape sequence. +And you can make +.I less +think that characters other than the standard ones may appear between +the ESC and the m by setting the environment variable LESSANSIMIDCHARS +to the list of characters which can appear. +.IP "\-s or \-\-squeeze-blank-lines" +Causes consecutive blank lines to be squeezed into a single blank line. +This is useful when viewing +.I nroff +output. +.IP "\-S or \-\-chop-long-lines" +Causes lines longer than the screen width to be +chopped (truncated) rather than wrapped. +That is, the portion of a long line that does not fit in +the screen width is not shown. +The default is to wrap long lines; that is, display the remainder +on the next line. +.IP "\-t\fItag\fP or \-\-tag=\fItag\fP" +The \-t option, followed immediately by a TAG, +will edit the file containing that tag. +For this to work, tag information must be available; +for example, there may be a file in the current directory called "tags", +which was previously built by +.I ctags +(1) or an equivalent command. +If the environment variable LESSGLOBALTAGS is set, it is taken to be +the name of a command compatible with +.I global +(1), and that command is executed to find the tag. +(See http://www.gnu.org/software/global/global.html). +The \-t option may also be specified from within +.I less +(using the \- command) as a way of examining a new file. +The command ":t" is equivalent to specifying \-t from within +.I less. +.IP "\-T\fItagsfile\fP or \-\-tag-file=\fItagsfile\fP" +Specifies a tags file to be used instead of "tags". +.IP "\-u or \-\-underline-special" +Causes backspaces and carriage returns to be treated as printable characters; +that is, they are sent to the terminal when they appear in the input. +.IP "\-U or \-\-UNDERLINE-SPECIAL" +Causes backspaces, tabs and carriage returns to be +treated as control characters; +that is, they are handled as specified by the \-r option. +.sp +By default, if neither \-u nor \-U is given, +backspaces which appear adjacent to an underscore character +are treated specially: +the underlined text is displayed +using the terminal's hardware underlining capability. +Also, backspaces which appear between two identical characters +are treated specially: +the overstruck text is printed +using the terminal's hardware boldface capability. +Other backspaces are deleted, along with the preceding character. +Carriage returns immediately followed by a newline are deleted. +Other carriage returns are handled as specified by the \-r option. +Text which is overstruck or underlined can be searched for +if neither \-u nor \-U is in effect. +.IP "\-V or \-\-version" +Displays the version number of +.I less. +.IP "\-w or \-\-hilite-unread" +Temporarily highlights the first "new" line after a forward movement +of a full page. +The first "new" line is the line immediately following the line previously +at the bottom of the screen. +Also highlights the target line after a g or p command. +The highlight is removed at the next command which causes movement. +The entire line is highlighted, unless the \-J option is in effect, +in which case only the status column is highlighted. +.IP "\-W or \-\-HILITE-UNREAD" +Like \-w, but temporarily highlights the first new line after any +forward movement command larger than one line. +.IP "\-x\fIn\fP,...\& or \-\-tabs=\fIn\fP,..." +Sets tab stops. +If only one \fIn\fP is specified, tab stops are set at multiples of \fIn\fP. +If multiple values separated by commas are specified, tab stops +are set at those positions, and then continue with the same spacing as the +last two. +For example, \fI-x9,17\fP will set tabs at positions 9, 17, 25, 33, etc. +The default for \fIn\fP is 8. +.IP "\-X or \-\-no-init" +Disables sending the termcap initialization and deinitialization strings +to the terminal. +This is sometimes desirable if the deinitialization string does +something unnecessary, like clearing the screen. +.IP "\-y\fIn\fP or \-\-max-forw-scroll=\fIn\fP" +Specifies a maximum number of lines to scroll forward. +If it is necessary to scroll forward more than \fIn\fP lines, +the screen is repainted instead. +The \-c or \-C option may be used to repaint from the top of +the screen if desired. +By default, any forward movement causes scrolling. +.IP "\-[z]\fIn\fP or \-\-window=\fIn\fP" +Changes the default scrolling window size to \fIn\fP lines. +The default is one screenful. +The z and w commands can also be used to change the window size. +The "z" may be omitted for compatibility with some versions of +.I more. +If the number +.I n +is negative, it indicates +.I n +lines less than the current screen size. +For example, if the screen is 24 lines, \fI\-z\-4\fP sets the +scrolling window to 20 lines. If the screen is resized to 40 lines, +the scrolling window automatically changes to 36 lines. +.IP "\-\fI\(dqcc\fP\ or\ \-\-quotes=\fIcc\fP" +Changes the filename quoting character. +This may be necessary if you are trying to name a file +which contains both spaces and quote characters. +Followed by a single character, this changes the quote character to that +character. +Filenames containing a space should then be surrounded by that character +rather than by double quotes. +Followed by two characters, changes the open quote to the first character, +and the close quote to the second character. +Filenames containing a space should then be preceded by the open quote +character and followed by the close quote character. +Note that even after the quote characters are changed, this option +remains \-" (a dash followed by a double quote). +.IP "\-~ or \-\-tilde" +Normally lines after end of file are displayed as a single tilde (~). +This option causes lines after end of file to be displayed as blank lines. +.IP "\-# or \-\-shift" +Specifies the default number of positions to scroll horizontally +in the RIGHTARROW and LEFTARROW commands. +If the number specified is zero, it sets the default number of +positions to one half of the screen width. +Alternately, the number may be specified as a fraction of the width +of the screen, starting with a decimal point: \&.5 is half of the +screen width, \&.3 is three tenths of the screen width, and so on. +If the number is specified as a fraction, the actual number of +scroll positions is recalculated if the terminal window is resized, +so that the actual scroll remains at the specified fraction +of the screen width. +.IP "\-\-follow-name" +Normally, if the input file is renamed while an F command is executing, +.I less +will continue to display the contents of the original file despite +its name change. +If \-\-follow-name is specified, during an F command +.I less +will periodically attempt to reopen the file by name. +If the reopen succeeds and the file is a different file from the original +(which means that a new file has been created +with the same name as the original (now renamed) file), +.I less +will display the contents of that new file. +.IP "\-\-no-keypad" +Disables sending the keypad initialization and deinitialization strings +to the terminal. +This is sometimes useful if the keypad strings make the numeric +keypad behave in an undesirable manner. +.IP "\-\-use-backslash" +This option changes the interpretations of options which follow this one. +After the \-\-use-backslash option, any backslash in an option string is +removed and the following character is taken literally. +This allows a dollar sign to be included in option strings. +.IP \-\- +A command line argument of "\-\-" marks the end of option arguments. +Any arguments following this are interpreted as filenames. +This can be useful when viewing a file whose name begins with a "\-" or "+". +.IP + +If a command line option begins with \fB+\fP, +the remainder of that option is taken to be an initial command to +.I less. +For example, +G tells +.I less +to start at the end of the file rather than the beginning, +and +/xyz tells it to start at the first occurrence of "xyz" in the file. +As a special case, + acts like +g; +that is, it starts the display at the specified line number +(however, see the caveat under the "g" command above). +If the option starts with ++, the initial command applies to +every file being viewed, not just the first one. +The + command described previously +may also be used to set (or change) an initial command for every file. + +.SH "LINE EDITING" +When entering command line at the bottom of the screen +(for example, a filename for the :e command, +or the pattern for a search command), +certain keys can be used to manipulate the command line. +Most commands have an alternate form in [ brackets ] which can be used if +a key does not exist on a particular keyboard. +(Note that the forms beginning with ESC do not work +in some MS-DOS and Windows systems because ESC is the line erase character.) +Any of these special keys may be entered literally by preceding +it with the "literal" character, either ^V or ^A. +A backslash itself may also be entered literally by entering two backslashes. +.IP "LEFTARROW [ ESC-h ]" +Move the cursor one space to the left. +.IP "RIGHTARROW [ ESC-l ]" +Move the cursor one space to the right. +.IP "^LEFTARROW [ ESC-b or ESC-LEFTARROW ]" +(That is, CONTROL and LEFTARROW simultaneously.) +Move the cursor one word to the left. +.IP "^RIGHTARROW [ ESC-w or ESC-RIGHTARROW ]" +(That is, CONTROL and RIGHTARROW simultaneously.) +Move the cursor one word to the right. +.IP "HOME [ ESC-0 ]" +Move the cursor to the beginning of the line. +.IP "END [ ESC-$ ]" +Move the cursor to the end of the line. +.IP "BACKSPACE" +Delete the character to the left of the cursor, +or cancel the command if the command line is empty. +.IP "DELETE or [ ESC-x ]" +Delete the character under the cursor. +.IP "^BACKSPACE [ ESC-BACKSPACE ]" +(That is, CONTROL and BACKSPACE simultaneously.) +Delete the word to the left of the cursor. +.IP "^DELETE [ ESC-X or ESC-DELETE ]" +(That is, CONTROL and DELETE simultaneously.) +Delete the word under the cursor. +.IP "UPARROW [ ESC-k ]" +Retrieve the previous command line. +If you first enter some text and then press UPARROW, +it will retrieve the previous command which begins with that text. +.IP "DOWNARROW [ ESC-j ]" +Retrieve the next command line. +If you first enter some text and then press DOWNARROW, +it will retrieve the next command which begins with that text. +.IP "TAB" +Complete the partial filename to the left of the cursor. +If it matches more than one filename, the first match +is entered into the command line. +Repeated TABs will cycle thru the other matching filenames. +If the completed filename is a directory, a "/" is appended to the filename. +(On MS-DOS systems, a "\e" is appended.) +The environment variable LESSSEPARATOR can be used to specify a +different character to append to a directory name. +.IP "BACKTAB [ ESC-TAB ]" +Like, TAB, but cycles in the reverse direction thru the matching filenames. +.IP "^L" +Complete the partial filename to the left of the cursor. +If it matches more than one filename, all matches are entered into +the command line (if they fit). +.IP "^U (Unix and OS/2) or ESC (MS-DOS)" +Delete the entire command line, +or cancel the command if the command line is empty. +If you have changed your line-kill character in Unix to something +other than ^U, that character is used instead of ^U. +.IP "^G" +Delete the entire command line and return to the main prompt. + +.SH "KEY BINDINGS" +You may define your own +.I less +commands by using the program +.I lesskey +(1) +to create a lesskey file. +This file specifies a set of command keys and an action +associated with each key. +You may also use +.I lesskey +to change the line-editing keys (see LINE EDITING), +and to set environment variables. +If the environment variable LESSKEY is set, +.I less +uses that as the name of the lesskey file. +Otherwise, +.I less +looks in a standard place for the lesskey file: +On Unix systems, +.I less +looks for a lesskey file called "$HOME/.less". +On MS-DOS and Windows systems, +.I less +looks for a lesskey file called "$HOME/_less", and if it is not found there, +then looks for a lesskey file called "_less" in any directory specified +in the PATH environment variable. +On OS/2 systems, +.I less +looks for a lesskey file called "$HOME/less.ini", and if it is not found, +then looks for a lesskey file called "less.ini" in any directory specified +in the INIT environment variable, and if it not found there, +then looks for a lesskey file called "less.ini" in any directory specified +in the PATH environment variable. +See the +.I lesskey +manual page for more details. +.P +A system-wide lesskey file may also be set up to provide key bindings. +If a key is defined in both a local lesskey file and in the +system-wide file, key bindings in the local file take precedence over +those in the system-wide file. +If the environment variable LESSKEY_SYSTEM is set, +.I less +uses that as the name of the system-wide lesskey file. +Otherwise, +.I less +looks in a standard place for the system-wide lesskey file: +On Unix systems, the system-wide lesskey file is /usr/local/etc/sysless. +(However, if +.I less +was built with a different sysconf directory than /usr/local/etc, +that directory is where the sysless file is found.) +On MS-DOS and Windows systems, the system-wide lesskey file is c:\e_sysless. +On OS/2 systems, the system-wide lesskey file is c:\esysless.ini. + +.SH "INPUT PREPROCESSOR" +You may define an "input preprocessor" for +.I less. +Before +.I less +opens a file, it first gives your input preprocessor a chance to modify the +way the contents of the file are displayed. +An input preprocessor is simply an executable program (or shell script), +which writes the contents of the file to a different file, +called the replacement file. +The contents of the replacement file are then displayed +in place of the contents of the original file. +However, it will appear to the user as if the original file is opened; +that is, +.I less +will display the original filename as the name of the current file. +.PP +An input preprocessor receives one command line argument, the original filename, +as entered by the user. +It should create the replacement file, and when finished, +print the name of the replacement file to its standard output. +If the input preprocessor does not output a replacement filename, +.I less +uses the original file, as normal. +The input preprocessor is not called when viewing standard input. +To set up an input preprocessor, set the LESSOPEN environment variable +to a command line which will invoke your input preprocessor. +This command line should include one occurrence of the string "%s", +which will be replaced by the filename +when the input preprocessor command is invoked. +.PP +When +.I less +closes a file opened in such a way, it will call another program, +called the input postprocessor, +which may perform any desired clean-up action (such as deleting the +replacement file created by LESSOPEN). +This program receives two command line arguments, the original filename +as entered by the user, and the name of the replacement file. +To set up an input postprocessor, set the LESSCLOSE environment variable +to a command line which will invoke your input postprocessor. +It may include two occurrences of the string "%s"; +the first is replaced with the original name of the file and +the second with the name of the replacement file, +which was output by LESSOPEN. +.PP +For example, on many Unix systems, these two scripts will allow you +to keep files in compressed format, but still let +.I less +view them directly: +.PP +lessopen.sh: +.br + #! /bin/sh +.br + case "$1" in +.br + *.Z) uncompress \-c $1 >/tmp/less.$$ 2>/dev/null +.br + if [ \-s /tmp/less.$$ ]; then +.br + echo /tmp/less.$$ +.br + else +.br + rm \-f /tmp/less.$$ +.br + fi +.br + ;; +.br + esac +.PP +lessclose.sh: +.br + #! /bin/sh +.br + rm $2 +.PP +To use these scripts, put them both where they can be executed and +set LESSOPEN="lessopen.sh\ %s", and +LESSCLOSE="lessclose.sh\ %s\ %s". +More complex LESSOPEN and LESSCLOSE scripts may be written +to accept other types of compressed files, and so on. +.PP +It is also possible to set up an input preprocessor to +pipe the file data directly to +.I less, +rather than putting the data into a replacement file. +This avoids the need to decompress the entire file before +starting to view it. +An input preprocessor that works this way is called an input pipe. +An input pipe, instead of writing the name of a replacement file on +its standard output, +writes the entire contents of the replacement file on its standard output. +If the input pipe does not write any characters on its standard output, +then there is no replacement file and +.I less +uses the original file, as normal. +To use an input pipe, +make the first character in the LESSOPEN environment variable a +vertical bar (|) to signify that the input preprocessor is an input pipe. +.PP +For example, on many Unix systems, this script will work like the +previous example scripts: +.PP +lesspipe.sh: +.br + #! /bin/sh +.br + case "$1" in +.br + *.Z) uncompress \-c $1 2>/dev/null +.br + *) exit 1 +.br + ;; +.br + esac +.br + exit $? +.br +.PP +To use this script, put it where it can be executed and set +LESSOPEN="|lesspipe.sh %s". +.PP +Note that a preprocessor cannot output an empty file, since that +is interpreted as meaning there is no replacement, and +the original file is used. +To avoid this, if LESSOPEN starts with two vertical bars, +the exit status of the script becomes meaningful. +If the exit status is zero, the output is considered to be +replacement text, even if it empty. +If the exit status is nonzero, any output is ignored and the +original file is used. +For compatibility with previous versions of +.I less, +if LESSOPEN starts with only one vertical bar, the exit status +of the preprocessor is ignored. +.PP +When an input pipe is used, a LESSCLOSE postprocessor can be used, +but it is usually not necessary since there is no replacement file +to clean up. +In this case, the replacement file name passed to the LESSCLOSE +postprocessor is "\-". +.PP +For compatibility with previous versions of +.I less, +the input preprocessor or pipe is not used if +.I less +is viewing standard input. +However, if the first character of LESSOPEN is a dash (\-), +the input preprocessor is used on standard input as well as other files. +In this case, the dash is not considered to be part of +the preprocessor command. +If standard input is being viewed, the input preprocessor is passed +a file name consisting of a single dash. +Similarly, if the first two characters of LESSOPEN are vertical bar and dash +(|\-) or two vertical bars and a dash (||\-), +the input pipe is used on standard input as well as other files. +Again, in this case the dash is not considered to be part of +the input pipe command. + +.SH "NATIONAL CHARACTER SETS" +There are three types of characters in the input file: +.IP "normal characters" +can be displayed directly to the screen. +.IP "control characters" +should not be displayed directly, but are expected to be found +in ordinary text files (such as backspace and tab). +.IP "binary characters" +should not be displayed directly and are not expected to be found +in text files. +.PP +A "character set" is simply a description of which characters are to +be considered normal, control, and binary. +The LESSCHARSET environment variable may be used to select a character set. +Possible values for LESSCHARSET are: +.IP ascii +BS, TAB, NL, CR, and formfeed are control characters, +all chars with values between 32 and 126 are normal, +and all others are binary. +.IP iso8859 +Selects an ISO 8859 character set. +This is the same as ASCII, except characters between 160 and 255 are +treated as normal characters. +.IP latin1 +Same as iso8859. +.IP latin9 +Same as iso8859. +.IP dos +Selects a character set appropriate for MS-DOS. +.IP ebcdic +Selects an EBCDIC character set. +.IP IBM-1047 +Selects an EBCDIC character set used by OS/390 Unix Services. +This is the EBCDIC analogue of latin1. You get similar results +by setting either LESSCHARSET=IBM-1047 or LC_CTYPE=en_US +in your environment. +.IP koi8-r +Selects a Russian character set. +.IP next +Selects a character set appropriate for NeXT computers. +.IP utf-8 +Selects the UTF-8 encoding of the ISO 10646 character set. +UTF-8 is special in that it supports multi-byte characters in the input file. +It is the only character set that supports multi-byte characters. +.IP windows +Selects a character set appropriate for Microsoft Windows (cp 1251). +.PP +In rare cases, it may be desired to tailor +.I less +to use a character set other than the ones definable by LESSCHARSET. +In this case, the environment variable LESSCHARDEF can be used +to define a character set. +It should be set to a string where each character in the string represents +one character in the character set. +The character "." is used for a normal character, "c" for control, +and "b" for binary. +A decimal number may be used for repetition. +For example, "bccc4b.\&" would mean character 0 is binary, +1, 2 and 3 are control, 4, 5, 6 and 7 are binary, and 8 is normal. +All characters after the last are taken to be the same as the last, +so characters 9 through 255 would be normal. +(This is an example, and does not necessarily +represent any real character set.) +.PP +This table shows the value of LESSCHARDEF which is equivalent +to each of the possible values for LESSCHARSET: +.sp + ascii\ 8bcccbcc18b95.b +.br + dos\ \ \ 8bcccbcc12bc5b95.b. +.br + ebcdic 5bc6bcc7bcc41b.9b7.9b5.b..8b6.10b6.b9.7b +.br + \ \ \ \ \ \ 9.8b8.17b3.3b9.7b9.8b8.6b10.b.b.b. +.br + IBM-1047 4cbcbc3b9cbccbccbb4c6bcc5b3cbbc4bc4bccbc +.br + \ \ \ \ \ \ 191.b +.br + iso8859 8bcccbcc18b95.33b. +.br + koi8-r 8bcccbcc18b95.b128. +.br + latin1 8bcccbcc18b95.33b. +.br + next\ \ 8bcccbcc18b95.bb125.bb +.PP +If neither LESSCHARSET nor LESSCHARDEF is set, +but any of the strings "UTF-8", "UTF8", "utf-8" or "utf8" +is found in the LC_ALL, LC_CTYPE or LANG +environment variables, then the default character set is utf-8. +.PP +If that string is not found, but your system supports the +.I setlocale +interface, +.I less +will use setlocale to determine the character set. +setlocale is controlled by setting the LANG or LC_CTYPE environment +variables. +.PP +Finally, if the +.I setlocale +interface is also not available, the default character set is latin1. +.PP +Control and binary characters are displayed in standout (reverse video). +Each such character is displayed in caret notation if possible +(e.g.\& ^A for control-A). Caret notation is used only if +inverting the 0100 bit results in a normal printable character. +Otherwise, the character is displayed as a hex number in angle brackets. +This format can be changed by +setting the LESSBINFMT environment variable. +LESSBINFMT may begin with a "*" and one character to select +the display attribute: +"*k" is blinking, "*d" is bold, "*u" is underlined, "*s" is standout, +and "*n" is normal. +If LESSBINFMT does not begin with a "*", normal attribute is assumed. +The remainder of LESSBINFMT is a string which may include one +printf-style escape sequence (a % followed by x, X, o, d, etc.). +For example, if LESSBINFMT is "*u[%x]", binary characters +are displayed in underlined hexadecimal surrounded by brackets. +The default if no LESSBINFMT is specified is "*s<%02X>". +Warning: the result of expanding the character via LESSBINFMT must +be less than 31 characters. +.PP +When the character set is utf-8, the LESSUTFBINFMT environment variable +acts similarly to LESSBINFMT but it applies to Unicode code points +that were successfully decoded but are unsuitable for display (e.g., +unassigned code points). +Its default value is "". +Note that LESSUTFBINFMT and LESSBINFMT share their display attribute +setting ("*x") so specifying one will affect both; +LESSUTFBINFMT is read after LESSBINFMT so its setting, if any, +will have priority. +Problematic octets in a UTF-8 file (octets of a truncated sequence, +octets of a complete but non-shortest form sequence, illegal octets, +and stray trailing octets) +are displayed individually using LESSBINFMT so as to facilitate diagnostic +of how the UTF-8 file is ill-formed. + +.SH "PROMPTS" +The \-P option allows you to tailor the prompt to your preference. +The string given to the \-P option replaces the specified prompt string. +Certain characters in the string are interpreted specially. +The prompt mechanism is rather complicated to provide flexibility, +but the ordinary user need not understand the details of constructing +personalized prompt strings. +.sp +A percent sign followed by a single character is expanded +according to what the following character is: +.IP "%b\fIX\fP" +Replaced by the byte offset into the current input file. +The b is followed by a single character (shown as \fIX\fP above) +which specifies the line whose byte offset is to be used. +If the character is a "t", the byte offset of the top line in the +display is used, +an "m" means use the middle line, +a "b" means use the bottom line, +a "B" means use the line just after the bottom line, +and a "j" means use the "target" line, as specified by the \-j option. +.IP "%B" +Replaced by the size of the current input file. +.IP "%c" +Replaced by the column number of the text appearing in the first +column of the screen. +.IP "%d\fIX\fP" +Replaced by the page number of a line in the input file. +The line to be used is determined by the \fIX\fP, as with the %b option. +.IP "%D" +Replaced by the number of pages in the input file, +or equivalently, the page number of the last line in the input file. +.IP "%E" +Replaced by the name of the editor (from the VISUAL environment variable, +or the EDITOR environment variable if VISUAL is not defined). +See the discussion of the LESSEDIT feature below. +.IP "%f" +Replaced by the name of the current input file. +.IP "%F" +Replaced by the last component of the name of the current input file. +.IP "%i" +Replaced by the index of the current file in the list of +input files. +.IP "%l\fIX\fP" +Replaced by the line number of a line in the input file. +The line to be used is determined by the \fIX\fP, as with the %b option. +.IP "%L" +Replaced by the line number of the last line in the input file. +.IP "%m" +Replaced by the total number of input files. +.IP "%p\fIX\fP" +Replaced by the percent into the current input file, based on byte offsets. +The line used is determined by the \fIX\fP as with the %b option. +.IP "%P\fIX\fP" +Replaced by the percent into the current input file, based on line numbers. +The line used is determined by the \fIX\fP as with the %b option. +.IP "%s" +Same as %B. +.IP "%t" +Causes any trailing spaces to be removed. +Usually used at the end of the string, but may appear anywhere. +.IP "%T" +Normally expands to the word "file". +However if viewing files via a tags list using the \-t option, it expands to the word "tag". +.IP "%x" +Replaced by the name of the next input file in the list. +.PP +If any item is unknown (for example, the file size if input +is a pipe), a question mark is printed instead. +.PP +The format of the prompt string can be changed +depending on certain conditions. +A question mark followed by a single character acts like an "IF": +depending on the following character, a condition is evaluated. +If the condition is true, any characters following the question mark +and condition character, up to a period, are included in the prompt. +If the condition is false, such characters are not included. +A colon appearing between the question mark and the +period can be used to establish an "ELSE": any characters between +the colon and the period are included in the string if and only if +the IF condition is false. +Condition characters (which follow a question mark) may be: +.IP "?a" +True if any characters have been included in the prompt so far. +.IP "?b\fIX\fP" +True if the byte offset of the specified line is known. +.IP "?B" +True if the size of current input file is known. +.IP "?c" +True if the text is horizontally shifted (%c is not zero). +.IP "?d\fIX\fP" +True if the page number of the specified line is known. +.IP "?e" +True if at end-of-file. +.IP "?f" +True if there is an input filename +(that is, if input is not a pipe). +.IP "?l\fIX\fP" +True if the line number of the specified line is known. +.IP "?L" +True if the line number of the last line in the file is known. +.IP "?m" +True if there is more than one input file. +.IP "?n" +True if this is the first prompt in a new input file. +.IP "?p\fIX\fP" +True if the percent into the current input file, based on byte offsets, +of the specified line is known. +.IP "?P\fIX\fP" +True if the percent into the current input file, based on line numbers, +of the specified line is known. +.IP "?s" +Same as "?B". +.IP "?x" +True if there is a next input file +(that is, if the current input file is not the last one). +.PP +Any characters other than the special ones +(question mark, colon, period, percent, and backslash) +become literally part of the prompt. +Any of the special characters may be included in the prompt literally +by preceding it with a backslash. +.PP +Some examples: +.sp +?f%f:Standard input. +.sp +This prompt prints the filename, if known; +otherwise the string "Standard input". +.sp +?f%f \&.?ltLine %lt:?pt%pt\e%:?btByte %bt:-... +.sp +This prompt would print the filename, if known. +The filename is followed by the line number, if known, +otherwise the percent if known, otherwise the byte offset if known. +Otherwise, a dash is printed. +Notice how each question mark has a matching period, +and how the % after the %pt +is included literally by escaping it with a backslash. +.sp +?n?f%f\ .?m(%T %i of %m)\ ..?e(END)\ ?x-\ Next\e:\ %x..%t"; +.sp +This prints the filename if this is the first prompt in a file, +followed by the "file N of N" message if there is more +than one input file. +Then, if we are at end-of-file, the string "(END)" is printed +followed by the name of the next file, if there is one. +Finally, any trailing spaces are truncated. +This is the default prompt. +For reference, here are the defaults for +the other two prompts (\-m and \-M respectively). +Each is broken into two lines here for readability only. +.nf +.sp +?n?f%f\ .?m(%T\ %i\ of\ %m)\ ..?e(END)\ ?x-\ Next\e:\ %x.: + ?pB%pB\e%:byte\ %bB?s/%s...%t +.sp +?f%f\ .?n?m(%T\ %i\ of\ %m)\ ..?ltlines\ %lt-%lb?L/%L.\ : + byte\ %bB?s/%s.\ .?e(END)\ ?x-\ Next\e:\ %x.:?pB%pB\e%..%t +.sp +.fi +And here is the default message produced by the = command: +.nf +.sp +?f%f\ .?m(%T\ %i\ of\ %m)\ .?ltlines\ %lt-%lb?L/%L.\ . + byte\ %bB?s/%s.\ ?e(END)\ :?pB%pB\e%..%t +.fi +.PP +The prompt expansion features are also used for another purpose: +if an environment variable LESSEDIT is defined, it is used +as the command to be executed when the v command is invoked. +The LESSEDIT string is expanded in the same way as the prompt strings. +The default value for LESSEDIT is: +.nf +.sp + %E\ ?lm+%lm.\ %f +.sp +.fi +Note that this expands to the editor name, followed by a + and the +line number, followed by the file name. +If your editor does not accept the "+linenumber" syntax, or has other +differences in invocation syntax, the LESSEDIT variable can be +changed to modify this default. + +.SH SECURITY +When the environment variable LESSSECURE is set to 1, +.I less +runs in a "secure" mode. +This means these features are disabled: +.RS +.IP "!" +the shell command +.IP "|" +the pipe command +.IP ":e" +the examine command. +.IP "v" +the editing command +.IP "s \-o" +log files +.IP "\-k" +use of lesskey files +.IP "\-t" +use of tags files +.IP " " +metacharacters in filenames, such as * +.IP " " +filename completion (TAB, ^L) +.RE +.PP +Less can also be compiled to be permanently in "secure" mode. + +.SH "COMPATIBILITY WITH MORE" +If the environment variable LESS_IS_MORE is set to 1, +or if the program is invoked via a file link named "more", +.I less +behaves (mostly) in conformance with the POSIX "more" command specification. +In this mode, less behaves differently in these ways: +.PP +The \-e option works differently. +If the \-e option is not set, +.I less +behaves as if the \-e option were set. +If the \-e option is set, +.I less +behaves as if the \-E option were set. +.PP +The \-m option works differently. +If the \-m option is not set, the medium prompt is used, +and it is prefixed with the string "--More--". +If the \-m option is set, the short prompt is used. +.PP +The \-n option acts like the \-z option. +The normal behavior of the \-n option is unavailable in this mode. +.PP +The parameter to the \-p option is taken to be a +.I less +command rather than a search pattern. +.PP +The LESS environment variable is ignored, +and the MORE environment variable is used in its place. + +.SH "ENVIRONMENT VARIABLES" +Environment variables may be specified either in the system environment +as usual, or in a +.I lesskey +(1) file. +If environment variables are defined in more than one place, +variables defined in a local lesskey file take precedence over +variables defined in the system environment, which take precedence +over variables defined in the system-wide lesskey file. +.IP COLUMNS +Sets the number of columns on the screen. +Takes precedence over the number of columns specified by the TERM variable. +(But if you have a windowing system which supports TIOCGWINSZ or WIOCGETD, +the window system's idea of the screen size takes precedence over the +LINES and COLUMNS environment variables.) +.IP EDITOR +The name of the editor (used for the v command). +.IP HOME +Name of the user's home directory +(used to find a lesskey file on Unix and OS/2 systems). +.IP "HOMEDRIVE, HOMEPATH" +Concatenation of the HOMEDRIVE and HOMEPATH environment variables is +the name of the user's home directory if the HOME variable is not set +(only in the Windows version). +.IP INIT +Name of the user's init directory (used to find a lesskey file on OS/2 systems). +.IP LANG +Language for determining the character set. +.IP LC_CTYPE +Language for determining the character set. +.IP LESS +Options which are passed to +.I less +automatically. +.IP LESSANSIENDCHARS +Characters which may end an ANSI color escape sequence +(default "m"). +.IP LESSANSIMIDCHARS +Characters which may appear between the ESC character and the +end character in an ANSI color escape sequence +(default "0123456789:;[?!"'#%()*+\ ". +.IP LESSBINFMT +Format for displaying non-printable, non-control characters. +.IP LESSCHARDEF +Defines a character set. +.IP LESSCHARSET +Selects a predefined character set. +.IP LESSCLOSE +Command line to invoke the (optional) input-postprocessor. +.IP LESSECHO +Name of the lessecho program (default "lessecho"). +The lessecho program is needed to expand metacharacters, such as * and ?, +in filenames on Unix systems. +.IP LESSEDIT +Editor prototype string (used for the v command). +See discussion under PROMPTS. +.IP LESSGLOBALTAGS +Name of the command used by the \-t option to find global tags. +Normally should be set to "global" if your system has the +.I global +(1) command. If not set, global tags are not used. +.IP LESSHISTFILE +Name of the history file used to remember search commands and +shell commands between invocations of +.I less. +If set to "\-" or "/dev/null", a history file is not used. +The default is "$HOME/.lesshst" on Unix systems, "$HOME/_lesshst" on +DOS and Windows systems, or "$HOME/lesshst.ini" or "$INIT/lesshst.ini" +on OS/2 systems. +.IP LESSHISTSIZE +The maximum number of commands to save in the history file. +The default is 100. +.IP LESSKEY +Name of the default lesskey(1) file. +.IP LESSKEY_SYSTEM +Name of the default system-wide lesskey(1) file. +.IP LESSMETACHARS +List of characters which are considered "metacharacters" by the shell. +.IP LESSMETAESCAPE +Prefix which less will add before each metacharacter in a +command sent to the shell. +If LESSMETAESCAPE is an empty string, commands containing +metacharacters will not be passed to the shell. +.IP LESSOPEN +Command line to invoke the (optional) input-preprocessor. +.IP LESSSECURE +Runs less in "secure" mode. +See discussion under SECURITY. +.IP LESSSEPARATOR +String to be appended to a directory name in filename completion. +.IP LESSUTFBINFMT +Format for displaying non-printable Unicode code points. +.IP LESS_IS_MORE +Emulate the +.I more +(1) command. +.IP LINES +Sets the number of lines on the screen. +Takes precedence over the number of lines specified by the TERM variable. +(But if you have a windowing system which supports TIOCGWINSZ or WIOCGETD, +the window system's idea of the screen size takes precedence over the +LINES and COLUMNS environment variables.) +.IP MORE +Options which are passed to +.I less +automatically when running in +.I more +compatible mode. +.IP PATH +User's search path (used to find a lesskey file +on MS-DOS and OS/2 systems). +.IP SHELL +The shell used to execute the ! command, as well as to expand filenames. +.IP TERM +The type of terminal on which +.I less +is being run. +.IP VISUAL +The name of the editor (used for the v command). + +.SH "SEE ALSO" +lesskey(1) + +.SH COPYRIGHT +Copyright (C) 1984-2016 Mark Nudelman +.PP +less is part of the GNU project and is free software. +You can redistribute it and/or modify it +under the terms of either +(1) the GNU General Public License as published by +the Free Software Foundation; or (2) the Less License. +See the file README in the less distribution for more details +regarding redistribution. +You should have received a copy of the GNU General Public License +along with the source for less; see the file COPYING. +If not, write to the Free Software Foundation, 59 Temple Place, +Suite 330, Boston, MA 02111-1307, USA. +You should also have received a copy of the Less License; +see the file LICENSE. +.PP +less is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU General Public License for more details. + +.SH AUTHOR +.PP +Mark Nudelman +.br +Send bug reports or comments to +.br +See http://www.greenwoodsoftware.com/less/bugs.html for the latest list of known bugs in less. +.br +For more information, see the less homepage at +.br +http://www.greenwoodsoftware.com/less. diff --git a/files/Sources/files/less/less_main.c b/files/Sources/files/less/less_main.c new file mode 100644 index 00000000..1348555d --- /dev/null +++ b/files/Sources/files/less/less_main.c @@ -0,0 +1,636 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Entry point, initialization, miscellaneous routines. + */ + +#include "less.h" +#if MSDOS_COMPILER==WIN32C +#include +#endif + +#ifdef __APPLE__ +// #include "get_compat.h" +// #else +#define COMPAT_MODE(func, mode) 1 +#endif + + +public char * every_first_cmd = NULL; +public int new_file; +public int is_tty; +public IFILE curr_ifile = NULL_IFILE; +public IFILE old_ifile = NULL_IFILE; +public struct scrpos initial_scrpos; +public int any_display = FALSE; +public POSITION start_attnpos = NULL_POSITION; +public POSITION end_attnpos = NULL_POSITION; +public int wscroll; +public char * progname; +public int quitting; +public int secure; +public int dohelp; + +public int file_errors = 0; +public int unix2003_compat = 0; +public int add_newline = 0; +public char * active_dashp_command = NULL; +public char * dashp_commands = NULL; +#if LOGFILE +public int logfile = -1; +public int force_logfile = FALSE; +public char * namelogfile = NULL; +#endif + +#if EDITOR +public char * editor; +public char * editproto; +#endif + +#if TAGS +extern char * tags; +extern char * tagoption; +extern int jump_sline; +#endif + +#ifdef WIN32 +static char consoleTitle[256]; +#endif + +extern int less_is_more; +extern int missing_cap; +extern int know_dumb; +extern int pr_type; + +// iOS: +#if SPACES_IN_FILENAMES +extern char openquote; +extern char closequote; +#endif +extern int utf_mode; +extern int binattr; +extern int fd0; +extern int no_back_scroll; +extern int same_pos_bell; +extern int display_next_file_or_exit; +extern int size_linebuf; +extern int tabstops[]; +extern int ntabstops; +extern int tabdefault; +extern int plusoption; +// from opttbl.c +extern int quiet; /* Should we suppress the audible bell? */ +extern int how_search; /* Where should forward searches start? */ +extern int top_scroll; /* Repaint screen from top? (alternative is scroll from bottom) */ +extern int pr_type; /* Type of prompt (short, medium, long) */ +extern int bs_mode; /* How to process backspaces */ +extern int know_dumb; /* Don't complain about dumb terminals */ +extern int quit_at_eof; /* Quit after hitting end of file twice */ +extern int quit_if_one_screen; /* Quit if EOF on first screen */ +extern int squeeze; /* Squeeze multiple blank lines into one */ +extern int tabstop; /* Tab settings */ +extern int back_scroll; /* Repaint screen on backwards movement */ +extern int forw_scroll; /* Repaint screen on forward movement */ +extern int caseless; /* Do "caseless" searches */ +extern int linenums; /* Use line numbers */ +extern int autobuf; /* Automatically allocate buffers as needed */ +extern int bufspace; /* Max buffer space per file (K) */ +extern int ctldisp; /* Send control chars to screen untranslated */ +extern int force_open; /* Open the file even if not regular file */ +extern int swindow; /* Size of scrolling window */ +extern int jump_sline; /* Screen line of "jump target" */ +extern long jump_sline_fraction; +extern long shift_count_fraction; +extern int chopline; /* Truncate displayed lines at screen width */ +extern int no_init; /* Disable sending ti/te termcap strings */ +extern int no_keypad; /* Disable sending ks/ke termcap strings */ +extern int twiddle; /* Show tildes after EOF */ +extern int show_attn; /* Hilite first unread line */ +extern int shift_count; /* Number of positions to shift horizontally */ +extern int dashn_numline_count; /* Number of lines override (Unix 2003) */ +extern int status_col; /* Display a status column */ +extern int use_lessopen; /* Use the LESSOPEN filter */ +extern int quit_on_intr; /* Quit on interrupt */ +extern int follow_mode; /* F cmd Follows file desc or file name? */ +extern int oldbot; /* Old bottom of screen behavior {{REMOVE}} */ +extern int opt_use_backslash; /* Use backslash escaping in option parsing */ +#if HILITE_SEARCH +extern int hilite_search; /* Highlight matched search patterns? */ +#endif +// from optfunc.c: +extern int sc_width; +extern int sc_height; +extern char *prproto[]; +extern char *eqproto; +extern char *hproto; +extern char *wproto; +#if TAGS +extern char* ztags; +#endif +// from output.c: +extern int errmsgs; /* Count of messages displayed by error() */ +extern int need_clr; +extern int final_attr; +extern int at_prompt; +// end iOS + +/* + * Entry point. + */ +int +less_main(argc, argv) + int argc; + char *argv[]; +{ + IFILE ifile; + char *s; + + // iOS: reinitialize flags: + file_errors = 0; + unix2003_compat = 0; + add_newline = 0; + less_is_more = 0; /* Make compatible with POSIX more */ + utf_mode = 1; // iOS + binattr = AT_STANDOUT; + fd0 = fileno(thread_stdin); + no_back_scroll = 0; + same_pos_bell = 1; + display_next_file_or_exit = 0; + size_linebuf = 0; /* Size of line buffer (and attr buffer) */ + tabstops[0] = 0; /* Custom tabstops */ + ntabstops = 1; /* Number of tabstops */ + tabdefault = 8; /* Default repeated tabstops */ + every_first_cmd = NULL; + curr_ifile = NULL_IFILE; + old_ifile = NULL_IFILE; + any_display = FALSE; + start_attnpos = NULL_POSITION; + end_attnpos = NULL_POSITION; + file_errors = 0; + unix2003_compat = 0; + add_newline = 0; + active_dashp_command = NULL; + dashp_commands = NULL; + plusoption = FALSE; + ztags = "tags"; + tags = ztags; + missing_cap = 0; /* Some capability is missing */ + // From opttbl.c: + quiet = 0; /* Should we suppress the audible bell? */ + how_search = 0; /* Where should forward searches start? */ + top_scroll = 0; /* Repaint screen from top? (alternative is scroll from bottom) */ + pr_type = 0; /* Type of prompt (short, medium, long) */ + bs_mode = 0; /* How to process backspaces */ + know_dumb = 0; /* Don't complain about dumb terminals */ + quit_at_eof = 0; /* Quit after hitting end of file twice */ + quit_if_one_screen = 0; /* Quit if EOF on first screen */ + squeeze = 0; /* Squeeze multiple blank lines into one */ + tabstop = 0; /* Tab settings */ + back_scroll = 0; /* Repaint screen on backwards movement */ + forw_scroll = 0; /* Repaint screen on forward movement */ + caseless = 0; /* Do "caseless" searches */ + linenums = 0; /* Use line numbers */ + autobuf = 0; /* Automatically allocate buffers as needed */ + bufspace = 0; /* Max buffer space per file (K) */ + ctldisp = 0; /* Send control chars to screen untranslated */ + force_open = 0; /* Open the file even if not regular file */ + swindow = 0; /* Size of scrolling window */ + jump_sline = 0; /* Screen line of "jump target" */ + jump_sline_fraction = -1; + shift_count_fraction = -1; + chopline = 0; /* Truncate displayed lines at screen width */ + no_init = 0; /* Disable sending ti/te termcap strings */ + no_keypad = 0; /* Disable sending ks/ke termcap strings */ + twiddle = 0; /* Show tildes after EOF */ + show_attn = 0; /* Hilite first unread line */ + shift_count = 0; /* Number of positions to shift horizontally */ + dashn_numline_count = 0; /* Number of lines override (Unix 2003) */ + status_col = 0; /* Display a status column */ + use_lessopen = 0; /* Use the LESSOPEN filter */ + quit_on_intr = 0; /* Quit on interrupt */ + follow_mode = 0; /* F cmd Follows file desc or file name? */ + oldbot = 0; /* Old bottom of screen behavior {{REMOVE}} */ + opt_use_backslash = 0; /* Use backslash escaping in option parsing */ +#if HILITE_SEARCH + hilite_search = 0; /* Highlight matched search patterns? */ +#endif +#if LOGFILE + logfile = -1; + force_logfile = FALSE; + namelogfile = NULL; +#endif +#if SPACES_IN_FILENAMES + openquote = '"'; + closequote = '"'; +#endif + // from optfunc.c +#if TAGS + public char *tagoption = NULL; +#endif + sc_width = 0; + sc_height = 0; + secure = 0; + dohelp = 0; + // from output.c: + errmsgs = 0; /* Count of messages displayed by error() */ + need_clr = 0; + final_attr = 0; + at_prompt = 0; + + + + if (COMPAT_MODE("bin/more", "unix2003")) { + unix2003_compat = 1; + } +#ifdef __EMX__ + _response(&argc, &argv); + _wildcard(&argc, &argv); +#endif + + progname = *argv++; + argc--; + + secure = 0; + s = lgetenv("LESSSECURE"); + if (s != NULL && *s != '\0') + secure = 1; + +#ifdef WIN32 + if (getenv("HOME") == NULL) + { + /* + * If there is no HOME environment variable, + * try the concatenation of HOMEDRIVE + HOMEPATH. + */ + char *drive = getenv("HOMEDRIVE"); + char *path = getenv("HOMEPATH"); + if (drive != NULL && path != NULL) + { + char *env = (char *) ecalloc(strlen(drive) + + strlen(path) + 6, sizeof(char)); + strcpy(env, "HOME="); + strcat(env, drive); + strcat(env, path); + putenv(env); + } + } + GetConsoleTitle(consoleTitle, sizeof(consoleTitle)/sizeof(char)); +#endif /* WIN32 */ + + is_tty = ios_isatty(1); + get_term(); + init_cmds(); + init_charset(); + init_line(); + init_cmdhist(); + init_option(); + init_search(); + + /* + * If the name of the executable program is "more", + * act like LESS_IS_MORE is set. + */ + for (s = progname + strlen(progname); s > progname; s--) + { + if (s[-1] == PATHNAME_SEP[0]) + break; + } + if (strcmp(s, "more") == 0) + less_is_more = 1; + else + unix2003_compat = 0; + init_prompt(); + if (less_is_more) { + if (!unix2003_compat) { + scan_option("-E"); + } + scan_option("-m"); + scan_option("-G"); + scan_option("-X"); /* avoid alternate screen */ + scan_option("-A"); /* search avoids current screen */ + } + s = lgetenv(less_is_more ? "MORE" : "LESS"); + if (s != NULL) + scan_option(save(s)); + +#define isoptstring(s) (((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0') + while (argc > 0 && (isoptstring(*argv) || isoptpending())) + { + s = *argv++; + argc--; + if (strcmp(s, "--") == 0) + break; + scan_option(s); + } +#undef isoptstring + + if (isoptpending()) + { + /* + * Last command line option was a flag requiring a + * following string, but there was no following string. + */ + nopendopt(); + quit(QUIT_OK); + } + +#if EDITOR + editor = lgetenv("VISUAL"); + if (editor == NULL || *editor == '\0') + { + editor = lgetenv("EDITOR"); + if (editor == NULL || *editor == '\0') + editor = EDIT_PGM; + } + editproto = lgetenv("LESSEDIT"); + if (editproto == NULL || *editproto == '\0') + { + if (unix2003_compat) { + editproto = "%E ?l+%l. %f"; + } else { + editproto = "%E ?lm+%lm. %f"; + } + } +#endif + if (less_is_more) { + if (unix2003_compat) { + /* If -n option appears, force screen size override */ + get_term(); + } + } + + /* + * Call get_ifile with all the command line filenames + * to "register" them with the ifile system. + */ + ifile = NULL_IFILE; + if (dohelp) + ifile = get_ifile(FAKE_HELPFILE, ifile); + while (argc-- > 0) + { + char *filename; +#if (MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC) + /* + * Because the "shell" doesn't expand filename patterns, + * treat each argument as a filename pattern rather than + * a single filename. + * Expand the pattern and iterate over the expanded list. + */ + struct textlist tlist; + char *gfilename; + + gfilename = lglob(*argv++); + init_textlist(&tlist, gfilename); + filename = NULL; + while ((filename = forw_textlist(&tlist, filename)) != NULL) + { + (void) get_ifile(filename, ifile); + ifile = prev_ifile(NULL_IFILE); + } + free(gfilename); +#else + filename = shell_quote(*argv); + if (filename == NULL) + filename = *argv; + argv++; + (void) get_ifile(filename, ifile); + ifile = prev_ifile(NULL_IFILE); + free(filename); +#endif + } + /* + * Set up terminal, etc. + */ + if (!is_tty) + { + /* + * Output is not a tty. + * Just copy the input file(s) to output. + */ + SET_BINARY(1); + if (nifile() == 0) + { + if (edit_stdin() == 0) + cat_file(); + else + file_errors++; + } else if (edit_first() == 0) + { + do { + cat_file(); + } while (edit_next(1) == 0); + } else + file_errors++; + if (file_errors) { + if (unix2003_compat) + quit(QUIT_ERROR); + } + quit(QUIT_OK); + } + + if (missing_cap && !know_dumb && !less_is_more) + error("WARNING: terminal is not fully functional", NULL_PARG); + init_mark(); + open_getchr(); + raw_mode(1); + init_signals(1); + + /* + * Select the first file to examine. + */ +#if TAGS + if (tagoption != NULL || strcmp(tags, "-") == 0) + { + int tags_skip_other_files = 1; + /* + * A -t option was given. + * Verify that no filenames were also given. + * Edit the file selected by the "tags" search, + * and search for the proper line in the file. + */ + if (unix2003_compat) { + tags_skip_other_files = 0; + } else { + if (nifile() > 0) + { + error("No filenames allowed with -t option", NULL_PARG); + quit(QUIT_ERROR); + } + } + findtag(tagoption); + if (edit_tagfile()) /* Edit file which contains the tag */ + quit(QUIT_ERROR); + /* + * Search for the line which contains the tag. + * Set up initial_scrpos so we display that line. + */ + initial_scrpos.pos = tagsearch(); + if (initial_scrpos.pos == NULL_POSITION) + quit(QUIT_ERROR); + initial_scrpos.ln = jump_sline; + if (!tags_skip_other_files) { + /* TBD: -t under unix2003 requires other files on + command line to be processed after tagfile, but + conformance tests do not test this feature + */ + } + } + else +#endif + if (nifile() == 0) + { + if (edit_stdin()) /* Edit standard input */ + quit(QUIT_ERROR); + } else + { + if (edit_first()) /* Edit first valid file in cmd line */ + quit(QUIT_ERROR); + } + + init(); + commands(); + if (file_errors) { + if (unix2003_compat) + quit(QUIT_ERROR); + } + quit(QUIT_OK); + /*NOTREACHED*/ + return (0); +} + +/* + * Copy a string to a "safe" place + * (that is, to a buffer allocated by calloc). + */ + public char * +save(s) + char *s; +{ + register char *p; + + p = (char *) ecalloc(strlen(s)+1, sizeof(char)); + strcpy(p, s); + return (p); +} + +/* + * Allocate memory. + * Like calloc(), but never returns an error (NULL). + */ + public VOID_POINTER +ecalloc(count, size) + int count; + unsigned int size; +{ + register VOID_POINTER p; + + p = (VOID_POINTER) calloc(count, size); + if (p != NULL) + return (p); + error("Cannot allocate memory", NULL_PARG); + quit(QUIT_ERROR); + /*NOTREACHED*/ + return (NULL); +} + +/* + * Skip leading spaces in a string. + */ + public char * +skipsp(s) + register char *s; +{ + while (*s == ' ' || *s == '\t') + s++; + return (s); +} + +/* + * See how many characters of two strings are identical. + * If uppercase is true, the first string must begin with an uppercase + * character; the remainder of the first string may be either case. + */ + public int +sprefix(ps, s, uppercase) + char *ps; + char *s; + int uppercase; +{ + register int c; + register int sc; + register int len = 0; + + for ( ; *s != '\0'; s++, ps++) + { + c = *ps; + if (uppercase) + { + if (len == 0 && ASCII_IS_LOWER(c)) + return (-1); + if (ASCII_IS_UPPER(c)) + c = ASCII_TO_LOWER(c); + } + sc = *s; + if (len > 0 && ASCII_IS_UPPER(sc)) + sc = ASCII_TO_LOWER(sc); + if (c != sc) + break; + len++; + } + return (len); +} + +/* + * Exit the program. + */ + public void +quit(status) + int status; +{ + static int save_status; + + /* + * Put cursor at bottom left corner, clear the line, + * reset the terminal modes, and exit. + */ + if (status < 0) + status = save_status; + else + save_status = status; + quitting = 1; + // iOS: + while (curr_ifile != NULL_IFILE) + del_ifile(curr_ifile); + clean_cmds(); + // + edit((char*)NULL); + save_cmdhist(); + if (any_display && is_tty) + clear_bot(); + deinit(); + flush(); + raw_mode(0); +#if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC + /* + * If we don't close 2, we get some garbage from + * 2's buffer when it flushes automatically. + * I cannot track this one down RB + * The same bug shows up if we use ^C^C to abort. + */ + close(2); +#endif +#ifdef WIN32 + SetConsoleTitle(consoleTitle); +#endif + close_getchr(); + flush(); + exit(status); +} diff --git a/files/Sources/files/less/lessecho.c b/files/Sources/files/less/lessecho.c new file mode 100644 index 00000000..86b9aa50 --- /dev/null +++ b/files/Sources/files/less/lessecho.c @@ -0,0 +1,271 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * lessecho [-ox] [-cx] [-pn] [-dn] [-a] file ... + * Simply echos its filename arguments on standard output. + * But any argument containing spaces is enclosed in quotes. + * + * -ox Specifies "x" to be the open quote character. + * -cx Specifies "x" to be the close quote character. + * -pn Specifies "n" to be the open quote character, as an integer. + * -dn Specifies "n" to be the close quote character, as an integer. + * -mx Specifies "x" to be a metachar. + * -nn Specifies "n" to be a metachar, as an integer. + * -ex Specifies "x" to be the escape char for metachars. + * -fn Specifies "x" to be the escape char for metachars, as an integer. + * -a Specifies that all arguments are to be quoted. + * The default is that only arguments containing spaces are quoted. + */ + +#include "less.h" + +static char *version = "$Revision: 1.15 $"; + +static int quote_all = 0; +static char openquote = '"'; +static char closequote = '"'; +static char *meta_escape = "\\"; +static char meta_escape_buf[2]; +static char metachars[64] = ""; +static int num_metachars = 0; + + static void +pr_usage() +{ + fprintf(thread_stderr, + "usage: lessecho [-ox] [-cx] [-pn] [-dn] [-mx] [-nn] [-ex] [-fn] [-a] file ...\n"); +} + + static void +pr_version() +{ + char *p; + char buf[10]; + char *pbuf = buf; + + for (p = version; *p != ' '; p++) + if (*p == '\0') + return; + for (p++; *p != '$' && *p != ' ' && *p != '\0'; p++) + *pbuf++ = *p; + *pbuf = '\0'; + fprintf(thread_stdout, "%s\n", buf); +} + + static void +pr_error(s) + char *s; +{ + fprintf(thread_stderr, "%s\n", s); + exit(1); +} + + static long +lstrtol(s, radix, pend) + char *s; + int radix; + char **pend; +{ + int v; + int neg = 0; + long n = 0; + + /* Skip leading white space. */ + while (*s == ' ' || *s == '\t') + s++; + + /* Check for a leading + or -. */ + if (*s == '-') + { + neg = 1; + s++; + } else if (*s == '+') + { + s++; + } + + /* Determine radix if caller does not specify. */ + if (radix == 0) + { + radix = 10; + if (*s == '0') + { + switch (*++s) + { + case 'x': + radix = 16; + s++; + break; + default: + radix = 8; + break; + } + } + } + + /* Parse the digits of the number. */ + for (;;) + { + if (*s >= '0' && *s <= '9') + v = *s - '0'; + else if (*s >= 'a' && *s <= 'f') + v = *s - 'a' + 10; + else if (*s >= 'A' && *s <= 'F') + v = *s - 'A' + 10; + else + break; + if (v >= radix) + break; + n = n * radix + v; + s++; + } + + if (pend != NULL) + { + /* Skip trailing white space. */ + while (*s == ' ' || *s == '\t') + s++; + *pend = s; + } + if (neg) + return (-n); + return (n); +} + + +#if !HAVE_STRCHR + char * +strchr(s, c) + char *s; + int c; +{ + for ( ; *s != '\0'; s++) + if (*s == c) + return (s); + if (c == '\0') + return (s); + return (NULL); +} +#endif + + int +lessecho_main(argc, argv) + int argc; + char *argv[]; +{ + char *arg; + char *s; + int no_more_options; + + no_more_options = 0; + while (--argc > 0) + { + arg = *++argv; + if (*arg != '-' || no_more_options) + break; + switch (*++arg) + { + case 'a': + quote_all = 1; + break; + case 'c': + closequote = *++arg; + break; + case 'd': + closequote = lstrtol(++arg, 0, &s); + if (s == arg) + pr_error("Missing number after -d"); + break; + case 'e': + if (strcmp(++arg, "-") == 0) + meta_escape = ""; + else + meta_escape = arg; + break; + case 'f': + meta_escape_buf[0] = lstrtol(++arg, 0, &s); + meta_escape = meta_escape_buf; + if (s == arg) + pr_error("Missing number after -f"); + break; + case 'o': + openquote = *++arg; + break; + case 'p': + openquote = lstrtol(++arg, 0, &s); + if (s == arg) + pr_error("Missing number after -p"); + break; + case 'm': + metachars[num_metachars++] = *++arg; + metachars[num_metachars] = '\0'; + break; + case 'n': + metachars[num_metachars++] = lstrtol(++arg, 0, &s); + if (s == arg) + pr_error("Missing number after -n"); + metachars[num_metachars] = '\0'; + break; + case '?': + pr_usage(); + return (0); + case '-': + if (*++arg == '\0') + { + no_more_options = 1; + break; + } + if (strcmp(arg, "version") == 0) + { + pr_version(); + return (0); + } + if (strcmp(arg, "help") == 0) + { + pr_usage(); + return (0); + } + pr_error("Invalid option after --"); + default: + pr_error("Invalid option letter"); + } + } + + while (argc-- > 0) + { + int has_meta = 0; + arg = *argv++; + for (s = arg; *s != '\0'; s++) + { + if (strchr(metachars, *s) != NULL) + { + has_meta = 1; + break; + } + } + if (quote_all || (has_meta && strlen(meta_escape) == 0)) + fprintf(thread_stdout, "%c%s%c", openquote, arg, closequote); + else + { + for (s = arg; *s != '\0'; s++) + { + if (strchr(metachars, *s) != NULL) + fprintf(thread_stdout, "%s", meta_escape); + fprintf(thread_stdout, "%c", *s); + } + } + if (argc > 0) + fprintf(thread_stdout, " "); + else + fprintf(thread_stdout, "\n"); + } + return (0); +} diff --git a/files/Sources/files/less/lessecho.man b/files/Sources/files/less/lessecho.man new file mode 100644 index 00000000..5f26d4af --- /dev/null +++ b/files/Sources/files/less/lessecho.man @@ -0,0 +1,54 @@ +LESSECHO(1) General Commands Manual LESSECHO(1) + + + +NAME + lessecho - expand metacharacters + +SYNOPSIS + lessecho [-ox] [-cx] [-pn] [-dn] [-mx] [-nn] [-ex] [-a] file ... + +DESCRIPTION + lessecho is a program that simply echos its arguments on standard out- + put. But any metacharacter in the output is preceded by an "escape" + character, which by default is a backslash. + +OPTIONS + A summary of options is included below. + + -ex Specifies "x", rather than backslash, to be the escape char for + metachars. If x is "-", no escape char is used and arguments + containing metachars are surrounded by quotes instead. + + -ox Specifies "x", rather than double-quote, to be the open quote + character, which is used if the -e- option is specified. + + -cx Specifies "x" to be the close quote character. + + -pn Specifies "n" to be the open quote character, as an integer. + + -dn Specifies "n" to be the close quote character, as an integer. + + -mx Specifies "x" to be a metachar. By default, no characters are + considered metachars. + + -nn Specifies "n" to be a metachar, as an integer. + + -fn Specifies "n" to be the escape char for metachars, as an inte- + ger. + + -a Specifies that all arguments are to be quoted. The default is + that only arguments containing metacharacters are quoted + +SEE ALSO + less(1) + +AUTHOR + This manual page was written by Thomas Schoepf , + for the Debian GNU/Linux system (but may be used by others). + + Send bug reports or comments to bug-less@gnu.org. + + + + Version 487: 25 Oct 2016 LESSECHO(1) diff --git a/files/Sources/files/less/lessecho.nro b/files/Sources/files/less/lessecho.nro new file mode 100644 index 00000000..d1302c83 --- /dev/null +++ b/files/Sources/files/less/lessecho.nro @@ -0,0 +1,52 @@ +.TH LESSECHO 1 "Version 487: 25 Oct 2016" +.SH NAME +lessecho \- expand metacharacters +.SH SYNOPSIS +.B lessecho +.I "[-ox] [-cx] [-pn] [-dn] [-mx] [-nn] [-ex] [-a] file ..." +.SH "DESCRIPTION" +.I lessecho +is a program that simply echos its arguments on standard output. +But any metacharacter in the output is preceded by an "escape" +character, which by default is a backslash. +.SH OPTIONS +A summary of options is included below. +.TP +.B \-ex +Specifies "x", rather than backslash, to be the escape char for metachars. +If x is "-", no escape char is used and arguments containing metachars +are surrounded by quotes instead. +.TP +.B \-ox +Specifies "x", rather than double-quote, to be the open quote character, +which is used if the -e- option is specified. +.TP +.B \-cx +Specifies "x" to be the close quote character. +.TP +.B \-pn +Specifies "n" to be the open quote character, as an integer. +.TP +.B \-dn +Specifies "n" to be the close quote character, as an integer. +.TP +.B \-mx +Specifies "x" to be a metachar. +By default, no characters are considered metachars. +.TP +.B \-nn +Specifies "n" to be a metachar, as an integer. +.TP +.B \-fn +Specifies "n" to be the escape char for metachars, as an integer. +.TP +.B \-a +Specifies that all arguments are to be quoted. +The default is that only arguments containing metacharacters are quoted +.SH "SEE ALSO" +less(1) +.SH AUTHOR +This manual page was written by Thomas Schoepf , +for the Debian GNU/Linux system (but may be used by others). +.PP +Send bug reports or comments to bug-less@gnu.org. diff --git a/files/Sources/files/less/lglob.h b/files/Sources/files/less/lglob.h new file mode 100644 index 00000000..0ebf1f62 --- /dev/null +++ b/files/Sources/files/less/lglob.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Macros to define the method of doing filename "globbing". + * There are three possible mechanisms: + * 1. GLOB_LIST + * This defines a function that returns a list of matching filenames. + * 2. GLOB_NAME + * This defines a function that steps thru the list of matching + * filenames, returning one name each time it is called. + * 3. GLOB_STRING + * This defines a function that returns the complete list of + * matching filenames as a single space-separated string. + */ + +#if OS2 + +#define DECL_GLOB_LIST(list) char **list; char **pp; +#define GLOB_LIST(filename,list) list = _fnexplode(filename) +#define GLOB_LIST_FAILED(list) list == NULL +#define SCAN_GLOB_LIST(list,p) pp = list; *pp != NULL; pp++ +#define INIT_GLOB_LIST(list,p) p = *pp +#define GLOB_LIST_DONE(list) _fnexplodefree(list) + +#else +#if MSDOS_COMPILER==DJGPPC + +#define DECL_GLOB_LIST(list) glob_t list; int i; +#define GLOB_LIST(filename,list) glob(filename,GLOB_NOCHECK,0,&list) +#define GLOB_LIST_FAILED(list) 0 +#define SCAN_GLOB_LIST(list,p) i = 0; i < list.gl_pathc; i++ +#define INIT_GLOB_LIST(list,p) p = list.gl_pathv[i] +#define GLOB_LIST_DONE(list) globfree(&list) + +#else +#if MSDOS_COMPILER==MSOFTC || MSDOS_COMPILER==BORLANDC + +#define GLOB_FIRST_NAME(filename,fndp,h) h = _dos_findfirst(filename, ~_A_VOLID, fndp) +#define GLOB_FIRST_FAILED(handle) ((handle) != 0) +#define GLOB_NEXT_NAME(handle,fndp) _dos_findnext(fndp) +#define GLOB_NAME_DONE(handle) +#define GLOB_NAME name +#define DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle) \ + struct find_t fnd; \ + char drive[_MAX_DRIVE]; \ + char dir[_MAX_DIR]; \ + char fname[_MAX_FNAME]; \ + char ext[_MAX_EXT]; \ + int handle; +#else +#if MSDOS_COMPILER==WIN32C && defined(_MSC_VER) + +#define GLOB_FIRST_NAME(filename,fndp,h) h = _findfirst(filename, fndp) +#define GLOB_FIRST_FAILED(handle) ((handle) == -1) +#define GLOB_NEXT_NAME(handle,fndp) _findnext(handle, fndp) +#define GLOB_NAME_DONE(handle) _findclose(handle) +#define GLOB_NAME name +#define DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle) \ + struct _finddata_t fnd; \ + char drive[_MAX_DRIVE]; \ + char dir[_MAX_DIR]; \ + char fname[_MAX_FNAME]; \ + char ext[_MAX_EXT]; \ + long handle; + +#else +#if MSDOS_COMPILER==WIN32C && !defined(_MSC_VER) /* Borland C for Windows */ + +#define GLOB_FIRST_NAME(filename,fndp,h) h = findfirst(filename, fndp, ~FA_LABEL) +#define GLOB_FIRST_FAILED(handle) ((handle) != 0) +#define GLOB_NEXT_NAME(handle,fndp) findnext(fndp) +#define GLOB_NAME_DONE(handle) +#define GLOB_NAME ff_name +#define DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle) \ + struct ffblk fnd; \ + char drive[MAXDRIVE]; \ + char dir[MAXDIR]; \ + char fname[MAXFILE]; \ + char ext[MAXEXT]; \ + int handle; + +#endif +#endif +#endif +#endif +#endif diff --git a/files/Sources/files/less/line.c b/files/Sources/files/less/line.c new file mode 100644 index 00000000..c20f94f5 --- /dev/null +++ b/files/Sources/files/less/line.c @@ -0,0 +1,1282 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Routines to manipulate the "line buffer". + * The line buffer holds a line of output as it is being built + * in preparation for output to the screen. + */ + +#include "less.h" +#include "charset.h" +#include "position.h" + +static char *linebuf = NULL; /* Buffer which holds the current output line */ +static char *attr = NULL; /* Extension of linebuf to hold attributes */ +public int size_linebuf = 0; /* Size of line buffer (and attr buffer) */ + +static int cshift; /* Current left-shift of output line buffer */ +public int hshift; /* Desired left-shift of output line buffer */ +public int tabstops[TABSTOP_MAX] = { 0 }; /* Custom tabstops */ +public int ntabstops = 1; /* Number of tabstops */ +public int tabdefault = 8; /* Default repeated tabstops */ +public POSITION highest_hilite; /* Pos of last hilite in file found so far */ + +static int curr; /* Index into linebuf */ +static int column; /* Printable length, accounting for + backspaces, etc. */ +static int overstrike; /* Next char should overstrike previous char */ +static int last_overstrike = AT_NORMAL; +static int is_null_line; /* There is no current line */ +static int lmargin; /* Left margin */ +static LWCHAR pendc; +static POSITION pendpos; +static char *end_ansi_chars; +static char *mid_ansi_chars; + +static int attr_swidth(); +static int attr_ewidth(); +static int do_append(); + +extern int sigs; +extern int bs_mode; +extern int linenums; +extern int ctldisp; +extern int twiddle; +extern int binattr; +extern int status_col; +extern int auto_wrap, ignaw; +extern int bo_s_width, bo_e_width; +extern int ul_s_width, ul_e_width; +extern int bl_s_width, bl_e_width; +extern int so_s_width, so_e_width; +extern int sc_width, sc_height; +extern int utf_mode; +extern POSITION start_attnpos; +extern POSITION end_attnpos; + +static char mbc_buf[MAX_UTF_CHAR_LEN]; +static int mbc_buf_len = 0; +static int mbc_buf_index = 0; +static POSITION mbc_pos; + +/* + * Initialize from environment variables. + */ + public void +init_line() +{ + end_ansi_chars = lgetenv("LESSANSIENDCHARS"); + if (end_ansi_chars == NULL || *end_ansi_chars == '\0') + end_ansi_chars = "m"; + + mid_ansi_chars = lgetenv("LESSANSIMIDCHARS"); + if (mid_ansi_chars == NULL || *mid_ansi_chars == '\0') + mid_ansi_chars = "0123456789:;[?!\"'#%()*+ "; + + linebuf = (char *) ecalloc(LINEBUF_SIZE, sizeof(char)); + attr = (char *) ecalloc(LINEBUF_SIZE, sizeof(char)); + size_linebuf = LINEBUF_SIZE; +} + +/* + * Expand the line buffer. + */ + static int +expand_linebuf() +{ + /* Double the size of the line buffer. */ + int new_size = size_linebuf * 2; + + /* Just realloc to expand the buffer, if we can. */ +#if HAVE_REALLOC + char *new_buf = (char *) realloc(linebuf, new_size); + char *new_attr = (char *) realloc(attr, new_size); +#else + char *new_buf = (char *) calloc(new_size, sizeof(char)); + char *new_attr = (char *) calloc(new_size, sizeof(char)); +#endif + if (new_buf == NULL || new_attr == NULL) + { + if (new_attr != NULL) + free(new_attr); + if (new_buf != NULL) + free(new_buf); + return 1; + } +#if HAVE_REALLOC + /* + * We realloc'd the buffers; they already have the old contents. + */ + #if 0 + memset(new_buf + size_linebuf, 0, new_size - size_linebuf); + memset(new_attr + size_linebuf, 0, new_size - size_linebuf); + #endif +#else + /* + * We just calloc'd the buffers; copy the old contents. + */ + memcpy(new_buf, linebuf, size_linebuf * sizeof(char)); + memcpy(new_attr, attr, size_linebuf * sizeof(char)); + free(attr); + free(linebuf); +#endif + linebuf = new_buf; + attr = new_attr; + size_linebuf = new_size; + return 0; +} + +/* + * Is a character ASCII? + */ + public int +is_ascii_char(ch) + LWCHAR ch; +{ + return (ch <= 0x7F); +} + +/* + * Rewind the line buffer. + */ + public void +prewind() +{ + curr = 0; + column = 0; + cshift = 0; + overstrike = 0; + last_overstrike = AT_NORMAL; + mbc_buf_len = 0; + is_null_line = 0; + pendc = '\0'; + lmargin = 0; + if (status_col) + lmargin += 1; +} + +/* + * Insert the line number (of the given position) into the line buffer. + */ + public void +plinenum(pos) + POSITION pos; +{ + register LINENUM linenum = 0; + register int i; + + if (linenums == OPT_ONPLUS) + { + /* + * Get the line number and put it in the current line. + * {{ Note: since find_linenum calls forw_raw_line, + * it may seek in the input file, requiring the caller + * of plinenum to re-seek if necessary. }} + * {{ Since forw_raw_line modifies linebuf, we must + * do this first, before storing anything in linebuf. }} + */ + linenum = find_linenum(pos); + } + + /* + * Display a status column if the -J option is set. + */ + if (status_col) + { + linebuf[curr] = ' '; + if (start_attnpos != NULL_POSITION && + pos >= start_attnpos && pos < end_attnpos) + attr[curr] = AT_NORMAL|AT_HILITE; + else + attr[curr] = AT_NORMAL; + curr++; + column++; + } + /* + * Display the line number at the start of each line + * if the -N option is set. + */ + if (linenums == OPT_ONPLUS) + { + char buf[INT_STRLEN_BOUND(pos) + 2]; + int n; + + linenumtoa(linenum, buf); + n = (int) strlen(buf); + if (n < MIN_LINENUM_WIDTH) + n = MIN_LINENUM_WIDTH; + sprintf(linebuf+curr, "%*s ", n, buf); + n++; /* One space after the line number. */ + for (i = 0; i < n; i++) + attr[curr+i] = AT_BOLD; + curr += n; + column += n; + lmargin += n; + } + + /* + * Append enough spaces to bring us to the lmargin. + */ + while (column < lmargin) + { + linebuf[curr] = ' '; + attr[curr++] = AT_NORMAL; + column++; + } +} + +/* + * Shift the input line left. + * This means discarding N printable chars at the start of the buffer. + */ + static void +pshift(shift) + int shift; +{ + LWCHAR prev_ch = 0; + unsigned char c; + int shifted = 0; + int to; + int from; + int len; + int width; + int prev_attr; + int next_attr; + + if (shift > column - lmargin) + shift = column - lmargin; + if (shift > curr - lmargin) + shift = curr - lmargin; + + to = from = lmargin; + /* + * We keep on going when shifted == shift + * to get all combining chars. + */ + while (shifted <= shift && from < curr) + { + c = linebuf[from]; + if (ctldisp == OPT_ONPLUS && IS_CSI_START(c)) + { + /* Keep cumulative effect. */ + linebuf[to] = c; + attr[to++] = attr[from++]; + while (from < curr && linebuf[from]) + { + linebuf[to] = linebuf[from]; + attr[to++] = attr[from]; + if (!is_ansi_middle(linebuf[from++])) + break; + } + continue; + } + + width = 0; + + if (!IS_ASCII_OCTET(c) && utf_mode) + { + /* Assumes well-formedness validation already done. */ + LWCHAR ch; + + len = utf_len(c); + if (from + len > curr) + break; + ch = get_wchar(linebuf + from); + if (!is_composing_char(ch) && !is_combining_char(prev_ch, ch)) + width = is_wide_char(ch) ? 2 : 1; + prev_ch = ch; + } else + { + len = 1; + if (c == '\b') + /* XXX - Incorrect if several '\b' in a row. */ + width = (utf_mode && is_wide_char(prev_ch)) ? -2 : -1; + else if (!control_char(c)) + width = 1; + prev_ch = 0; + } + + if (width == 2 && shift - shifted == 1) { + /* Should never happen when called by pshift_all(). */ + attr[to] = attr[from]; + /* + * Assume a wide_char will never be the first half of a + * combining_char pair, so reset prev_ch in case we're + * followed by a '\b'. + */ + prev_ch = linebuf[to++] = ' '; + from += len; + shifted++; + continue; + } + + /* Adjust width for magic cookies. */ + prev_attr = (to > 0) ? attr[to-1] : AT_NORMAL; + next_attr = (from + len < curr) ? attr[from + len] : prev_attr; + if (!is_at_equiv(attr[from], prev_attr) && + !is_at_equiv(attr[from], next_attr)) + { + width += attr_swidth(attr[from]); + if (from + len < curr) + width += attr_ewidth(attr[from]); + if (is_at_equiv(prev_attr, next_attr)) + { + width += attr_ewidth(prev_attr); + if (from + len < curr) + width += attr_swidth(next_attr); + } + } + + if (shift - shifted < width) + break; + from += len; + shifted += width; + if (shifted < 0) + shifted = 0; + } + while (from < curr) + { + linebuf[to] = linebuf[from]; + attr[to++] = attr[from++]; + } + curr = to; + column -= shifted; + cshift += shifted; +} + +/* + * + */ + public void +pshift_all() +{ + pshift(column); +} + +/* + * Return the printing width of the start (enter) sequence + * for a given character attribute. + */ + static int +attr_swidth(a) + int a; +{ + int w = 0; + + a = apply_at_specials(a); + + if (a & AT_UNDERLINE) + w += ul_s_width; + if (a & AT_BOLD) + w += bo_s_width; + if (a & AT_BLINK) + w += bl_s_width; + if (a & AT_STANDOUT) + w += so_s_width; + + return w; +} + +/* + * Return the printing width of the end (exit) sequence + * for a given character attribute. + */ + static int +attr_ewidth(a) + int a; +{ + int w = 0; + + a = apply_at_specials(a); + + if (a & AT_UNDERLINE) + w += ul_e_width; + if (a & AT_BOLD) + w += bo_e_width; + if (a & AT_BLINK) + w += bl_e_width; + if (a & AT_STANDOUT) + w += so_e_width; + + return w; +} + +/* + * Return the printing width of a given character and attribute, + * if the character were added to the current position in the line buffer. + * Adding a character with a given attribute may cause an enter or exit + * attribute sequence to be inserted, so this must be taken into account. + */ + static int +pwidth(ch, a, prev_ch) + LWCHAR ch; + int a; + LWCHAR prev_ch; +{ + int w; + + if (ch == '\b') + /* + * Backspace moves backwards one or two positions. + * XXX - Incorrect if several '\b' in a row. + */ + return (utf_mode && is_wide_char(prev_ch)) ? -2 : -1; + + if (!utf_mode || is_ascii_char(ch)) + { + if (control_char((char)ch)) + { + /* + * Control characters do unpredictable things, + * so we don't even try to guess; say it doesn't move. + * This can only happen if the -r flag is in effect. + */ + return (0); + } + } else + { + if (is_composing_char(ch) || is_combining_char(prev_ch, ch)) + { + /* + * Composing and combining chars take up no space. + * + * Some terminals, upon failure to compose a + * composing character with the character(s) that + * precede(s) it will actually take up one column + * for the composing character; there isn't much + * we could do short of testing the (complex) + * composition process ourselves and printing + * a binary representation when it fails. + */ + return (0); + } + } + + /* + * Other characters take one or two columns, + * plus the width of any attribute enter/exit sequence. + */ + w = 1; + if (is_wide_char(ch)) + w++; + if (curr > 0 && !is_at_equiv(attr[curr-1], a)) + w += attr_ewidth(attr[curr-1]); + if ((apply_at_specials(a) != AT_NORMAL) && + (curr == 0 || !is_at_equiv(attr[curr-1], a))) + w += attr_swidth(a); + return (w); +} + +/* + * Delete to the previous base character in the line buffer. + * Return 1 if one is found. + */ + static int +backc() +{ + LWCHAR prev_ch; + char *p = linebuf + curr; + LWCHAR ch = step_char(&p, -1, linebuf + lmargin); + int width; + + /* This assumes that there is no '\b' in linebuf. */ + while ( curr > lmargin + && column > lmargin + && (!(attr[curr - 1] & (AT_ANSI|AT_BINARY)))) + { + curr = (int) (p - linebuf); + prev_ch = step_char(&p, -1, linebuf + lmargin); + width = pwidth(ch, attr[curr], prev_ch); + column -= width; + if (width > 0) + return 1; + ch = prev_ch; + } + + return 0; +} + +/* + * Are we currently within a recognized ANSI escape sequence? + */ + static int +in_ansi_esc_seq() +{ + char *p; + + /* + * Search backwards for either an ESC (which means we ARE in a seq); + * or an end char (which means we're NOT in a seq). + */ + for (p = &linebuf[curr]; p > linebuf; ) + { + LWCHAR ch = step_char(&p, -1, linebuf); + if (IS_CSI_START(ch)) + return (1); + if (!is_ansi_middle(ch)) + return (0); + } + return (0); +} + +/* + * Is a character the end of an ANSI escape sequence? + */ + public int +is_ansi_end(ch) + LWCHAR ch; +{ + if (!is_ascii_char(ch)) + return (0); + return (strchr(end_ansi_chars, (char) ch) != NULL); +} + +/* + * + */ + public int +is_ansi_middle(ch) + LWCHAR ch; +{ + if (!is_ascii_char(ch)) + return (0); + if (is_ansi_end(ch)) + return (0); + return (strchr(mid_ansi_chars, (char) ch) != NULL); +} + +/* + * Append a character and attribute to the line buffer. + */ +#define STORE_CHAR(ch,a,rep,pos) \ + do { \ + if (store_char((ch),(a),(rep),(pos))) return (1); \ + } while (0) + + static int +store_char(ch, a, rep, pos) + LWCHAR ch; + int a; + char *rep; + POSITION pos; +{ + int w; + int replen; + char cs; + + w = (a & (AT_UNDERLINE|AT_BOLD)); /* Pre-use w. */ + if (w != AT_NORMAL) + last_overstrike = w; + +#if HILITE_SEARCH + { + int matches; + if (is_hilited(pos, pos+1, 0, &matches)) + { + /* + * This character should be highlighted. + * Override the attribute passed in. + */ + if (a != AT_ANSI) + { + if (highest_hilite != NULL_POSITION && + pos > highest_hilite) + highest_hilite = pos; + a |= AT_HILITE; + } + } + } +#endif + + if (ctldisp == OPT_ONPLUS && in_ansi_esc_seq()) + { + if (!is_ansi_end(ch) && !is_ansi_middle(ch)) { + /* Remove whole unrecognized sequence. */ + char *p = &linebuf[curr]; + LWCHAR bch; + do { + bch = step_char(&p, -1, linebuf); + } while (p > linebuf && !IS_CSI_START(bch)); + curr = (int) (p - linebuf); + return 0; + } + a = AT_ANSI; /* Will force re-AT_'ing around it. */ + w = 0; + } + else if (ctldisp == OPT_ONPLUS && IS_CSI_START(ch)) + { + a = AT_ANSI; /* Will force re-AT_'ing around it. */ + w = 0; + } + else + { + char *p = &linebuf[curr]; + LWCHAR prev_ch = step_char(&p, -1, linebuf); + w = pwidth(ch, a, prev_ch); + } + + if (ctldisp != OPT_ON && column + w + attr_ewidth(a) > sc_width) + /* + * Won't fit on screen. + */ + return (1); + + if (rep == NULL) + { + cs = (char) ch; + rep = &cs; + replen = 1; + } else + { + replen = utf_len(rep[0]); + } + if (curr + replen >= size_linebuf-6) + { + /* + * Won't fit in line buffer. + * Try to expand it. + */ + if (expand_linebuf()) + return (1); + } + + while (replen-- > 0) + { + linebuf[curr] = *rep++; + attr[curr] = a; + curr++; + } + column += w; + return (0); +} + +/* + * Append a tab to the line buffer. + * Store spaces to represent the tab. + */ +#define STORE_TAB(a,pos) \ + do { if (store_tab((a),(pos))) return (1); } while (0) + + static int +store_tab(attr, pos) + int attr; + POSITION pos; +{ + int to_tab = column + cshift - lmargin; + int i; + + if (ntabstops < 2 || to_tab >= tabstops[ntabstops-1]) + to_tab = tabdefault - + ((to_tab - tabstops[ntabstops-1]) % tabdefault); + else + { + for (i = ntabstops - 2; i >= 0; i--) + if (to_tab >= tabstops[i]) + break; + to_tab = tabstops[i+1] - to_tab; + } + + if (column + to_tab - 1 + pwidth(' ', attr, 0) + attr_ewidth(attr) > sc_width) + return 1; + + do { + STORE_CHAR(' ', attr, " ", pos); + } while (--to_tab > 0); + return 0; +} + +#define STORE_PRCHAR(c, pos) \ + do { if (store_prchar((c), (pos))) return 1; } while (0) + + static int +store_prchar(c, pos) + LWCHAR c; + POSITION pos; +{ + char *s; + + /* + * Convert to printable representation. + */ + s = prchar(c); + + /* + * Make sure we can get the entire representation + * of the character on this line. + */ + if (column + (int) strlen(s) - 1 + + pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width) + return 1; + + for ( ; *s != 0; s++) + STORE_CHAR(*s, AT_BINARY, NULL, pos); + + return 0; +} + + static int +flush_mbc_buf(pos) + POSITION pos; +{ + int i; + + for (i = 0; i < mbc_buf_index; i++) + if (store_prchar(mbc_buf[i], pos)) + return mbc_buf_index - i; + + return 0; +} + +/* + * Append a character to the line buffer. + * Expand tabs into spaces, handle underlining, boldfacing, etc. + * Returns 0 if ok, 1 if couldn't fit in buffer. + */ + public int +pappend(unsigned char c, POSITION pos) +{ + int r; + + if (pendc) + { + if (c == '\r' && pendc == '\r') + return (0); + if (do_append(pendc, NULL, pendpos)) + /* + * Oops. We've probably lost the char which + * was in pendc, since caller won't back up. + */ + return (1); + pendc = '\0'; + } + + if (c == '\r' && bs_mode == BS_SPECIAL) + { + if (mbc_buf_len > 0) /* utf_mode must be on. */ + { + /* Flush incomplete (truncated) sequence. */ + r = flush_mbc_buf(mbc_pos); + mbc_buf_index = r + 1; + mbc_buf_len = 0; + if (r) + return (mbc_buf_index); + } + + /* + * Don't put the CR into the buffer until we see + * the next char. If the next char is a newline, + * discard the CR. + */ + pendc = c; + pendpos = pos; + return (0); + } + + if (!utf_mode) + { + r = do_append(c, NULL, pos); + } else + { + /* Perform strict validation in all possible cases. */ + if (mbc_buf_len == 0) + { + retry: + mbc_buf_index = 1; + *mbc_buf = c; + if (IS_ASCII_OCTET(c)) + r = do_append(c, NULL, pos); + else if (IS_UTF8_LEAD(c)) + { + mbc_buf_len = utf_len(c); + mbc_pos = pos; + return (0); + } else + /* UTF8_INVALID or stray UTF8_TRAIL */ + r = flush_mbc_buf(pos); + } else if (IS_UTF8_TRAIL(c)) + { + mbc_buf[mbc_buf_index++] = c; + if (mbc_buf_index < mbc_buf_len) + return (0); + if (is_utf8_well_formed(mbc_buf, mbc_buf_index)) + r = do_append(get_wchar(mbc_buf), mbc_buf, mbc_pos); + else + /* Complete, but not shortest form, sequence. */ + mbc_buf_index = r = flush_mbc_buf(mbc_pos); + mbc_buf_len = 0; + } else + { + /* Flush incomplete (truncated) sequence. */ + r = flush_mbc_buf(mbc_pos); + mbc_buf_index = r + 1; + mbc_buf_len = 0; + /* Handle new char. */ + if (!r) + goto retry; + } + } + + /* + * If we need to shift the line, do it. + * But wait until we get to at least the middle of the screen, + * so shifting it doesn't affect the chars we're currently + * pappending. (Bold & underline can get messed up otherwise.) + */ + if (cshift < hshift && column > sc_width / 2) + { + linebuf[curr] = '\0'; + pshift(hshift - cshift); + } + if (r) + { + /* How many chars should caller back up? */ + r = (!utf_mode) ? 1 : mbc_buf_index; + } + return (r); +} + + static int +do_append(ch, rep, pos) + LWCHAR ch; + char *rep; + POSITION pos; +{ + register int a; + LWCHAR prev_ch; + + a = AT_NORMAL; + + if (ch == '\b') + { + if (bs_mode == BS_CONTROL) + goto do_control_char; + + /* + * A better test is needed here so we don't + * backspace over part of the printed + * representation of a binary character. + */ + if ( curr <= lmargin + || column <= lmargin + || (attr[curr - 1] & (AT_ANSI|AT_BINARY))) + STORE_PRCHAR('\b', pos); + else if (bs_mode == BS_NORMAL) + STORE_CHAR(ch, AT_NORMAL, NULL, pos); + else if (bs_mode == BS_SPECIAL) + overstrike = backc(); + + return 0; + } + + if (overstrike > 0) + { + /* + * Overstrike the character at the current position + * in the line buffer. This will cause either + * underline (if a "_" is overstruck), + * bold (if an identical character is overstruck), + * or just deletion of the character in the buffer. + */ + overstrike = utf_mode ? -1 : 0; + if (utf_mode) + { + /* To be correct, this must be a base character. */ + prev_ch = get_wchar(linebuf + curr); + } else + { + prev_ch = (unsigned char) linebuf[curr]; + } + a = attr[curr]; + if (ch == prev_ch) + { + /* + * Overstriking a char with itself means make it bold. + * But overstriking an underscore with itself is + * ambiguous. It could mean make it bold, or + * it could mean make it underlined. + * Use the previous overstrike to resolve it. + */ + if (ch == '_') + { + if ((a & (AT_BOLD|AT_UNDERLINE)) != AT_NORMAL) + a |= (AT_BOLD|AT_UNDERLINE); + else if (last_overstrike != AT_NORMAL) + a |= last_overstrike; + else + a |= AT_BOLD; + } else + a |= AT_BOLD; + } else if (ch == '_') + { + a |= AT_UNDERLINE; + ch = prev_ch; + rep = linebuf + curr; + } else if (prev_ch == '_') + { + a |= AT_UNDERLINE; + } + /* Else we replace prev_ch, but we keep its attributes. */ + } else if (overstrike < 0) + { + if ( is_composing_char(ch) + || is_combining_char(get_wchar(linebuf + curr), ch)) + /* Continuation of the same overstrike. */ + a = last_overstrike; + else + overstrike = 0; + } + + if (ch == '\t') + { + /* + * Expand a tab into spaces. + */ + switch (bs_mode) + { + case BS_CONTROL: + goto do_control_char; + case BS_NORMAL: + case BS_SPECIAL: + STORE_TAB(a, pos); + break; + } + } else if ((!utf_mode || is_ascii_char(ch)) && control_char((char)ch)) + { + do_control_char: + if (ctldisp == OPT_ON || (ctldisp == OPT_ONPLUS && IS_CSI_START(ch))) + { + /* + * Output as a normal character. + */ + STORE_CHAR(ch, AT_NORMAL, rep, pos); + } else + { + STORE_PRCHAR((char) ch, pos); + } + } else if (utf_mode && ctldisp != OPT_ON && is_ubin_char(ch)) + { + char *s; + + s = prutfchar(ch); + + if (column + (int) strlen(s) - 1 + + pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width) + return (1); + + for ( ; *s != 0; s++) + STORE_CHAR(*s, AT_BINARY, NULL, pos); + } else + { + STORE_CHAR(ch, a, rep, pos); + } + return (0); +} + +/* + * + */ + public int +pflushmbc() +{ + int r = 0; + + if (mbc_buf_len > 0) + { + /* Flush incomplete (truncated) sequence. */ + r = flush_mbc_buf(mbc_pos); + mbc_buf_len = 0; + } + return r; +} + +/* + * Terminate the line in the line buffer. + */ + public void +pdone(endline, forw) + int endline; + int forw; +{ + (void) pflushmbc(); + + if (pendc && (pendc != '\r' || !endline)) + /* + * If we had a pending character, put it in the buffer. + * But discard a pending CR if we are at end of line + * (that is, discard the CR in a CR/LF sequence). + */ + (void) do_append(pendc, NULL, pendpos); + + /* + * Make sure we've shifted the line, if we need to. + */ + if (cshift < hshift) + pshift(hshift - cshift); + + if (ctldisp == OPT_ONPLUS && is_ansi_end('m')) + { + /* Switch to normal attribute at end of line. */ + char *p = "\033[m"; + for ( ; *p != '\0'; p++) + { + linebuf[curr] = *p; + attr[curr++] = AT_ANSI; + } + } + + /* + * Add a newline if necessary, + * and append a '\0' to the end of the line. + * We output a newline if we're not at the right edge of the screen, + * or if the terminal doesn't auto wrap, + * or if this is really the end of the line AND the terminal ignores + * a newline at the right edge. + * (In the last case we don't want to output a newline if the terminal + * doesn't ignore it since that would produce an extra blank line. + * But we do want to output a newline if the terminal ignores it in case + * the next line is blank. In that case the single newline output for + * that blank line would be ignored!) + */ + if (column < sc_width || !auto_wrap || (endline && ignaw) || ctldisp == OPT_ON) + { + linebuf[curr] = '\n'; + attr[curr] = AT_NORMAL; + curr++; + } + else if (ignaw && column >= sc_width && forw) + { + /* + * Terminals with "ignaw" don't wrap until they *really* need + * to, i.e. when the character *after* the last one to fit on a + * line is output. But they are too hard to deal with when they + * get in the state where a full screen width of characters + * have been output but the cursor is sitting on the right edge + * instead of at the start of the next line. + * So we nudge them into wrapping by outputting a space + * character plus a backspace. But do this only if moving + * forward; if we're moving backward and drawing this line at + * the top of the screen, the space would overwrite the first + * char on the next line. We don't need to do this "nudge" + * at the top of the screen anyway. + */ + linebuf[curr] = ' '; + attr[curr++] = AT_NORMAL; + linebuf[curr] = '\b'; + attr[curr++] = AT_NORMAL; + } + linebuf[curr] = '\0'; + attr[curr] = AT_NORMAL; +} + +/* + * + */ + public void +set_status_col(char c) +{ + linebuf[0] = c; + attr[0] = AT_NORMAL|AT_HILITE; +} + +/* + * Get a character from the current line. + * Return the character as the function return value, + * and the character attribute in *ap. + */ + public int +gline(i, ap) + register int i; + register int *ap; +{ + if (is_null_line) + { + /* + * If there is no current line, we pretend the line is + * either "~" or "", depending on the "twiddle" flag. + */ + if (twiddle) + { + if (i == 0) + { + *ap = AT_BOLD; + return '~'; + } + --i; + } + /* Make sure we're back to AT_NORMAL before the '\n'. */ + *ap = AT_NORMAL; + return i ? '\0' : '\n'; + } + + *ap = attr[i]; + return (linebuf[i] & 0xFF); +} + +/* + * Indicate that there is no current line. + */ + public void +null_line() +{ + is_null_line = 1; + cshift = 0; +} + +/* + * Analogous to forw_line(), but deals with "raw lines": + * lines which are not split for screen width. + * {{ This is supposed to be more efficient than forw_line(). }} + */ + public POSITION +forw_raw_line(curr_pos, linep, line_lenp) + POSITION curr_pos; + char **linep; + int *line_lenp; +{ + register int n; + register int c; + POSITION new_pos; + + if (curr_pos == NULL_POSITION || ch_seek(curr_pos) || + (c = ch_forw_get()) == EOI) + return (NULL_POSITION); + + n = 0; + for (;;) + { + if (c == '\n' || c == EOI || ABORT_SIGS()) + { + new_pos = ch_tell(); + break; + } + if (n >= size_linebuf-1) + { + if (expand_linebuf()) + { + /* + * Overflowed the input buffer. + * Pretend the line ended here. + */ + new_pos = ch_tell() - 1; + break; + } + } + linebuf[n++] = c; + c = ch_forw_get(); + } + linebuf[n] = '\0'; + if (linep != NULL) + *linep = linebuf; + if (line_lenp != NULL) + *line_lenp = n; + return (new_pos); +} + +/* + * Analogous to back_line(), but deals with "raw lines". + * {{ This is supposed to be more efficient than back_line(). }} + */ + public POSITION +back_raw_line(curr_pos, linep, line_lenp) + POSITION curr_pos; + char **linep; + int *line_lenp; +{ + register int n; + register int c; + POSITION new_pos; + + if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() || + ch_seek(curr_pos-1)) + return (NULL_POSITION); + + n = size_linebuf; + linebuf[--n] = '\0'; + for (;;) + { + c = ch_back_get(); + if (c == '\n' || ABORT_SIGS()) + { + /* + * This is the newline ending the previous line. + * We have hit the beginning of the line. + */ + new_pos = ch_tell() + 1; + break; + } + if (c == EOI) + { + /* + * We have hit the beginning of the file. + * This must be the first line in the file. + * This must, of course, be the beginning of the line. + */ + new_pos = ch_zero(); + break; + } + if (n <= 0) + { + int old_size_linebuf = size_linebuf; + char *fm; + char *to; + if (expand_linebuf()) + { + /* + * Overflowed the input buffer. + * Pretend the line ended here. + */ + new_pos = ch_tell() + 1; + break; + } + /* + * Shift the data to the end of the new linebuf. + */ + for (fm = linebuf + old_size_linebuf - 1, + to = linebuf + size_linebuf - 1; + fm >= linebuf; fm--, to--) + *to = *fm; + n = size_linebuf - old_size_linebuf; + } + linebuf[--n] = c; + } + if (linep != NULL) + *linep = &linebuf[n]; + if (line_lenp != NULL) + *line_lenp = size_linebuf - 1 - n; + return (new_pos); +} + +/* + * Find the shift necessary to show the end of the longest displayed line. + */ + public int +rrshift() +{ + POSITION pos; + int save_width; + int line; + int longest = 0; + + save_width = sc_width; + sc_width = INT_MAX; + hshift = 0; + pos = position(TOP); + for (line = 0; line < sc_height && pos != NULL_POSITION; line++) + { + pos = forw_line(pos); + if (column > longest) + longest = column; + } + sc_width = save_width; + if (longest < sc_width) + return 0; + return longest - sc_width; +} diff --git a/files/Sources/files/less/linenum.c b/files/Sources/files/less/linenum.c new file mode 100644 index 00000000..c1a8128f --- /dev/null +++ b/files/Sources/files/less/linenum.c @@ -0,0 +1,470 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Code to handle displaying line numbers. + * + * Finding the line number of a given file position is rather tricky. + * We don't want to just start at the beginning of the file and + * count newlines, because that is slow for large files (and also + * wouldn't work if we couldn't get to the start of the file; e.g. + * if input is a long pipe). + * + * So we use the function add_lnum to cache line numbers. + * We try to be very clever and keep only the more interesting + * line numbers when we run out of space in our table. A line + * number is more interesting than another when it is far from + * other line numbers. For example, we'd rather keep lines + * 100,200,300 than 100,101,300. 200 is more interesting than + * 101 because 101 can be derived very cheaply from 100, while + * 200 is more expensive to derive from 100. + * + * The function currline() returns the line number of a given + * position in the file. As a side effect, it calls add_lnum + * to cache the line number. Therefore currline is occasionally + * called to make sure we cache line numbers often enough. + */ + +#include "less.h" + +/* + * Structure to keep track of a line number and the associated file position. + * A doubly-linked circular list of line numbers is kept ordered by line number. + */ +struct linenum_info +{ + struct linenum_info *next; /* Link to next in the list */ + struct linenum_info *prev; /* Line to previous in the list */ + POSITION pos; /* File position */ + POSITION gap; /* Gap between prev and next */ + LINENUM line; /* Line number */ +}; +/* + * "gap" needs some explanation: the gap of any particular line number + * is the distance between the previous one and the next one in the list. + * ("Distance" means difference in file position.) In other words, the + * gap of a line number is the gap which would be introduced if this + * line number were deleted. It is used to decide which one to replace + * when we have a new one to insert and the table is full. + */ + +#define NPOOL 200 /* Size of line number pool */ + +#define LONGTIME (2) /* In seconds */ + +static struct linenum_info anchor; /* Anchor of the list */ +static struct linenum_info *freelist; /* Anchor of the unused entries */ +static struct linenum_info pool[NPOOL]; /* The pool itself */ +static struct linenum_info *spare; /* We always keep one spare entry */ + +extern int linenums; +extern int sigs; +extern int sc_height; +extern int screen_trashed; + +/* + * Initialize the line number structures. + */ + public void +clr_linenum() +{ + register struct linenum_info *p; + + /* + * Put all the entries on the free list. + * Leave one for the "spare". + */ + for (p = pool; p < &pool[NPOOL-2]; p++) + p->next = p+1; + pool[NPOOL-2].next = NULL; + freelist = pool; + + spare = &pool[NPOOL-1]; + + /* + * Initialize the anchor. + */ + anchor.next = anchor.prev = &anchor; + anchor.gap = 0; + anchor.pos = (POSITION)0; + anchor.line = 1; +} + +/* + * Calculate the gap for an entry. + */ + static void +calcgap(p) + register struct linenum_info *p; +{ + /* + * Don't bother to compute a gap for the anchor. + * Also don't compute a gap for the last one in the list. + * The gap for that last one should be considered infinite, + * but we never look at it anyway. + */ + if (p == &anchor || p->next == &anchor) + return; + p->gap = p->next->pos - p->prev->pos; +} + +/* + * Add a new line number to the cache. + * The specified position (pos) should be the file position of the + * FIRST character in the specified line. + */ + public void +add_lnum(linenum, pos) + LINENUM linenum; + POSITION pos; +{ + register struct linenum_info *p; + register struct linenum_info *new; + register struct linenum_info *nextp; + register struct linenum_info *prevp; + register POSITION mingap; + + /* + * Find the proper place in the list for the new one. + * The entries are sorted by position. + */ + for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next) + if (p->line == linenum) + /* We already have this one. */ + return; + nextp = p; + prevp = p->prev; + + if (freelist != NULL) + { + /* + * We still have free (unused) entries. + * Use one of them. + */ + new = freelist; + freelist = freelist->next; + } else + { + /* + * No free entries. + * Use the "spare" entry. + */ + new = spare; + spare = NULL; + } + + /* + * Fill in the fields of the new entry, + * and insert it into the proper place in the list. + */ + new->next = nextp; + new->prev = prevp; + new->pos = pos; + new->line = linenum; + + nextp->prev = new; + prevp->next = new; + + /* + * Recalculate gaps for the new entry and the neighboring entries. + */ + calcgap(new); + calcgap(nextp); + calcgap(prevp); + + if (spare == NULL) + { + /* + * We have used the spare entry. + * Scan the list to find the one with the smallest + * gap, take it out and make it the spare. + * We should never remove the last one, so stop when + * we get to p->next == &anchor. This also avoids + * looking at the gap of the last one, which is + * not computed by calcgap. + */ + mingap = anchor.next->gap; + for (p = anchor.next; p->next != &anchor; p = p->next) + { + if (p->gap <= mingap) + { + spare = p; + mingap = p->gap; + } + } + spare->next->prev = spare->prev; + spare->prev->next = spare->next; + } +} + +/* + * If we get stuck in a long loop trying to figure out the + * line number, print a message to tell the user what we're doing. + */ + static void +longloopmessage() +{ + ierror("Calculating line numbers", NULL_PARG); +} + +static int loopcount; +#if HAVE_TIME +static time_type startime; +#endif + + static void +longish() +{ +#if HAVE_TIME + if (loopcount >= 0 && ++loopcount > 100) + { + loopcount = 0; + if (get_time() >= startime + LONGTIME) + { + longloopmessage(); + loopcount = -1; + } + } +#else + if (loopcount >= 0 && ++loopcount > LONGLOOP) + { + longloopmessage(); + loopcount = -1; + } +#endif +} + +/* + * Turn off line numbers because the user has interrupted + * a lengthy line number calculation. + */ + static void +abort_long() +{ + if (linenums == OPT_ONPLUS) + /* + * We were displaying line numbers, so need to repaint. + */ + screen_trashed = 1; + linenums = 0; + error("Line numbers turned off", NULL_PARG); +} + +/* + * Find the line number associated with a given position. + * Return 0 if we can't figure it out. + */ + public LINENUM +find_linenum(pos) + POSITION pos; +{ + register struct linenum_info *p; + register LINENUM linenum; + POSITION cpos; + + if (!linenums) + /* + * We're not using line numbers. + */ + return (0); + if (pos == NULL_POSITION) + /* + * Caller doesn't know what he's talking about. + */ + return (0); + if (pos <= ch_zero()) + /* + * Beginning of file is always line number 1. + */ + return (1); + + /* + * Find the entry nearest to the position we want. + */ + for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next) + continue; + if (p->pos == pos) + /* Found it exactly. */ + return (p->line); + + /* + * This is the (possibly) time-consuming part. + * We start at the line we just found and start + * reading the file forward or backward till we + * get to the place we want. + * + * First decide whether we should go forward from the + * previous one or backwards from the next one. + * The decision is based on which way involves + * traversing fewer bytes in the file. + */ +#if HAVE_TIME + startime = get_time(); +#endif + if (p == &anchor || pos - p->prev->pos < p->pos - pos) + { + /* + * Go forward. + */ + p = p->prev; + if (ch_seek(p->pos)) + return (0); + loopcount = 0; + for (linenum = p->line, cpos = p->pos; cpos < pos; linenum++) + { + /* + * Allow a signal to abort this loop. + */ + cpos = forw_raw_line(cpos, (char **)NULL, (int *)NULL); + if (ABORT_SIGS()) { + abort_long(); + return (0); + } + if (cpos == NULL_POSITION) + return (0); + longish(); + } + /* + * We might as well cache it. + */ + add_lnum(linenum, cpos); + /* + * If the given position is not at the start of a line, + * make sure we return the correct line number. + */ + if (cpos > pos) + linenum--; + } else + { + /* + * Go backward. + */ + if (ch_seek(p->pos)) + return (0); + loopcount = 0; + for (linenum = p->line, cpos = p->pos; cpos > pos; linenum--) + { + /* + * Allow a signal to abort this loop. + */ + cpos = back_raw_line(cpos, (char **)NULL, (int *)NULL); + if (ABORT_SIGS()) { + abort_long(); + return (0); + } + if (cpos == NULL_POSITION) + return (0); + longish(); + } + /* + * We might as well cache it. + */ + add_lnum(linenum, cpos); + } + + return (linenum); +} + +/* + * Find the position of a given line number. + * Return NULL_POSITION if we can't figure it out. + */ + public POSITION +find_pos(linenum) + LINENUM linenum; +{ + register struct linenum_info *p; + POSITION cpos; + LINENUM clinenum; + + if (linenum <= 1) + /* + * Line number 1 is beginning of file. + */ + return (ch_zero()); + + /* + * Find the entry nearest to the line number we want. + */ + for (p = anchor.next; p != &anchor && p->line < linenum; p = p->next) + continue; + if (p->line == linenum) + /* Found it exactly. */ + return (p->pos); + + if (p == &anchor || linenum - p->prev->line < p->line - linenum) + { + /* + * Go forward. + */ + p = p->prev; + if (ch_seek(p->pos)) + return (NULL_POSITION); + for (clinenum = p->line, cpos = p->pos; clinenum < linenum; clinenum++) + { + /* + * Allow a signal to abort this loop. + */ + cpos = forw_raw_line(cpos, (char **)NULL, (int *)NULL); + if (ABORT_SIGS()) + return (NULL_POSITION); + if (cpos == NULL_POSITION) + return (NULL_POSITION); + } + } else + { + /* + * Go backward. + */ + if (ch_seek(p->pos)) + return (NULL_POSITION); + for (clinenum = p->line, cpos = p->pos; clinenum > linenum; clinenum--) + { + /* + * Allow a signal to abort this loop. + */ + cpos = back_raw_line(cpos, (char **)NULL, (int *)NULL); + if (ABORT_SIGS()) + return (NULL_POSITION); + if (cpos == NULL_POSITION) + return (NULL_POSITION); + } + } + /* + * We might as well cache it. + */ + add_lnum(clinenum, cpos); + return (cpos); +} + +/* + * Return the line number of the "current" line. + * The argument "where" tells which line is to be considered + * the "current" line (e.g. TOP, BOTTOM, MIDDLE, etc). + */ + public LINENUM +currline(where) + int where; +{ + POSITION pos; + POSITION len; + LINENUM linenum; + + pos = position(where); + len = ch_length(); + while (pos == NULL_POSITION && where >= 0 && where < sc_height) + pos = position(++where); + if (pos == NULL_POSITION) + pos = len; + linenum = find_linenum(pos); + if (pos == len) + linenum--; + return (linenum); +} diff --git a/files/Sources/files/less/lsystem.c b/files/Sources/files/less/lsystem.c new file mode 100644 index 00000000..6a959bf0 --- /dev/null +++ b/files/Sources/files/less/lsystem.c @@ -0,0 +1,372 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Routines to execute other programs. + * Necessarily very OS dependent. + */ + +#include "less.h" +#include +#include "position.h" + +#if MSDOS_COMPILER +#include +#ifdef _MSC_VER +#include +#define setdisk(n) _chdrive((n)+1) +#else +#include +#endif +#endif + +extern int screen_trashed; +extern IFILE curr_ifile; + +#if HAVE_SYSTEM + +/* + * Pass the specified command to a shell to be executed. + * Like plain "system()", but handles resetting terminal modes, etc. + */ + public void +lsystem(cmd, donemsg) + char *cmd; + char *donemsg; +{ + register int inp; +#if HAVE_SHELL + register char *shell; + register char *p; +#endif + IFILE save_ifile; +#if MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C + char cwd[FILENAME_MAX+1]; +#endif + + /* + * Print the command which is to be executed, + * unless the command starts with a "-". + */ + if (cmd[0] == '-') + cmd++; + else + { + clear_bot(); + putstr("!"); + putstr(cmd); + putstr("\n"); + } + +#if MSDOS_COMPILER +#if MSDOS_COMPILER==WIN32C + if (*cmd == '\0') + cmd = getenv("COMSPEC"); +#else + /* + * Working directory is global on MSDOS. + * The child might change the working directory, so we + * must save and restore CWD across calls to "system", + * or else we won't find our file when we return and + * try to "reedit_ifile" it. + */ + getcwd(cwd, FILENAME_MAX); +#endif +#endif + + /* + * Close the current input file. + */ + save_ifile = save_curr_ifile(); + (void) edit_ifile(NULL_IFILE); + + /* + * De-initialize the terminal and take out of raw mode. + */ + deinit(); + flush(); /* Make sure the deinit chars get out */ + raw_mode(0); +#if MSDOS_COMPILER==WIN32C + close_getchr(); +#endif + + /* + * Restore signals to their defaults. + */ + init_signals(0); + +#if HAVE_DUP + /* + * Force standard input to be the user's terminal + * (the normal standard input), even if less's standard input + * is coming from a pipe. + */ + inp = dup(0); + close(0); +#if OS2 + /* The __open() system call translates "/dev/tty" to "con". */ + if (__open("/dev/tty", OPEN_READ) < 0) +#else + if (open("/dev/tty", OPEN_READ) < 0) +#endif + dup(inp); +#endif + + /* + * Pass the command to the system to be executed. + * If we have a SHELL environment variable, use + * <$SHELL -c "command"> instead of just . + * If the command is empty, just invoke a shell. + */ +#if HAVE_SHELL + p = NULL; + if ((shell = lgetenv("SHELL")) != NULL && *shell != '\0') + { + if (*cmd == '\0') + p = save(shell); + else + { + char *esccmd = shell_quote(cmd); + if (esccmd != NULL) + { + int len = (int) (strlen(shell) + strlen(esccmd) + 5); + p = (char *) ecalloc(len, sizeof(char)); + SNPRINTF3(p, len, "%s %s %s", shell, shell_coption(), esccmd); + free(esccmd); + } + } + } + if (p == NULL) + { + if (*cmd == '\0') + p = save("sh"); + else + p = save(cmd); + } + ios_system(p); + free(p); +#else +#if MSDOS_COMPILER==DJGPPC + /* + * Make stdin of the child be in cooked mode. + */ + setmode(0, O_TEXT); + /* + * We don't need to catch signals of the child (it + * also makes trouble with some DPMI servers). + */ + __djgpp_exception_toggle(); + system(cmd); + __djgpp_exception_toggle(); +#else + system(cmd); +#endif +#endif + +#if HAVE_DUP + /* + * Restore standard input, reset signals, raw mode, etc. + */ + close(0); + dup(inp); + close(inp); +#endif + +#if MSDOS_COMPILER==WIN32C + open_getchr(); +#endif + init_signals(1); + raw_mode(1); + if (donemsg != NULL) + { + putstr(donemsg); + putstr(" (press RETURN)"); + get_return(); + putchr('\n'); + flush(); + } + init(); + screen_trashed = 1; + +#if MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C + /* + * Restore the previous directory (possibly + * changed by the child program we just ran). + */ + chdir(cwd); +#if MSDOS_COMPILER != DJGPPC + /* + * Some versions of chdir() don't change to the drive + * which is part of CWD. (DJGPP does this in chdir.) + */ + if (cwd[1] == ':') + { + if (cwd[0] >= 'a' && cwd[0] <= 'z') + setdisk(cwd[0] - 'a'); + else if (cwd[0] >= 'A' && cwd[0] <= 'Z') + setdisk(cwd[0] - 'A'); + } +#endif +#endif + + /* + * Reopen the current input file. + */ + reedit_ifile(save_ifile); + +#if defined(SIGWINCH) || defined(SIGWIND) + /* + * Since we were ignoring window change signals while we executed + * the system command, we must assume the window changed. + * Warning: this leaves a signal pending (in "sigs"), + * so psignals() should be called soon after lsystem(). + */ + winch(0); +#endif +} + +#endif + +#if PIPEC + +/* + * Pipe a section of the input file into the given shell command. + * The section to be piped is the section "between" the current + * position and the position marked by the given letter. + * + * If the mark is after the current screen, the section between + * the top line displayed and the mark is piped. + * If the mark is before the current screen, the section between + * the mark and the bottom line displayed is piped. + * If the mark is on the current screen, or if the mark is ".", + * the whole current screen is piped. + */ + public int +pipe_mark(c, cmd) + int c; + char *cmd; +{ + POSITION mpos, tpos, bpos; + + /* + * mpos = the marked position. + * tpos = top of screen. + * bpos = bottom of screen. + */ + mpos = markpos(c); + if (mpos == NULL_POSITION) + return (-1); + tpos = position(TOP); + if (tpos == NULL_POSITION) + tpos = ch_zero(); + bpos = position(BOTTOM); + + if (c == '.') + return (pipe_data(cmd, tpos, bpos)); + else if (mpos <= tpos) + return (pipe_data(cmd, mpos, bpos)); + else if (bpos == NULL_POSITION) + return (pipe_data(cmd, tpos, bpos)); + else + return (pipe_data(cmd, tpos, mpos)); +} + +/* + * Create a pipe to the given shell command. + * Feed it the file contents between the positions spos and epos. + */ + public int +pipe_data(cmd, spos, epos) + char *cmd; + POSITION spos; + POSITION epos; +{ + register FILE *f; + register int c; + extern FILE *popen(); + + /* + * This is structured much like lsystem(). + * Since we're running a shell program, we must be careful + * to perform the necessary deinitialization before running + * the command, and reinitialization after it. + */ + if (ch_seek(spos) != 0) + { + error("Cannot seek to start position", NULL_PARG); + return (-1); + } + + if ((f = popen(cmd, "w")) == NULL) + { + error("Cannot create pipe", NULL_PARG); + return (-1); + } + clear_bot(); + putstr("!"); + putstr(cmd); + putstr("\n"); + + deinit(); + flush(); + raw_mode(0); + init_signals(0); +#if MSDOS_COMPILER==WIN32C + close_getchr(); +#endif +#ifdef SIGPIPE + LSIGNAL(SIGPIPE, SIG_IGN); +#endif + + c = EOI; + while (epos == NULL_POSITION || spos++ <= epos) + { + /* + * Read a character from the file and give it to the pipe. + */ + c = ch_forw_get(); + if (c == EOI) + break; + if (putc(c, f) == EOF) + break; + } + + /* + * Finish up the last line. + */ + while (c != '\n' && c != EOI ) + { + c = ch_forw_get(); + if (c == EOI) + break; + if (putc(c, f) == EOF) + break; + } + + pclose(f); + +#ifdef SIGPIPE + LSIGNAL(SIGPIPE, SIG_DFL); +#endif +#if MSDOS_COMPILER==WIN32C + open_getchr(); +#endif + init_signals(1); + raw_mode(1); + init(); + screen_trashed = 1; +#if defined(SIGWINCH) || defined(SIGWIND) + /* {{ Probably don't need this here. }} */ + winch(0); +#endif + return (0); +} + +#endif diff --git a/files/Sources/files/less/mark.c b/files/Sources/files/less/mark.c new file mode 100644 index 00000000..9d3e0496 --- /dev/null +++ b/files/Sources/files/less/mark.c @@ -0,0 +1,249 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +#include "less.h" + +extern IFILE curr_ifile; +extern int sc_height; +extern int jump_sline; + +/* + * The table of marks. + * Each mark is identified by a lowercase or uppercase letter. + * The final one is lmark, for the "last mark"; addressed by the apostrophe. + */ +#define NMARKS ((2*26)+1) /* a-z, A-Z, lastmark */ +#define LASTMARK (NMARKS-1) +static struct mark marks[NMARKS]; + +/* + * Initialize the mark table to show no marks are set. + */ + public void +init_mark() +{ + int i; + + for (i = 0; i < NMARKS; i++) + marks[i].m_scrpos.pos = NULL_POSITION; +} + +/* + * See if a mark letter is valid (between a and z). + */ + static struct mark * +getumark(c) + int c; +{ + if (c >= 'a' && c <= 'z') + return (&marks[c-'a']); + + if (c >= 'A' && c <= 'Z') + return (&marks[c-'A'+26]); + + error("Invalid mark letter", NULL_PARG); + return (NULL); +} + +/* + * Get the mark structure identified by a character. + * The mark struct may come either from the mark table + * or may be constructed on the fly for certain characters like ^, $. + */ + static struct mark * +getmark(c) + int c; +{ + register struct mark *m; + static struct mark sm; + + switch (c) + { + case '^': + /* + * Beginning of the current file. + */ + m = &sm; + m->m_scrpos.pos = ch_zero(); + m->m_scrpos.ln = 0; + m->m_ifile = curr_ifile; + break; + case '$': + /* + * End of the current file. + */ + if (ch_end_seek()) + { + error("Cannot seek to end of file", NULL_PARG); + return (NULL); + } + m = &sm; + m->m_scrpos.pos = ch_tell(); + m->m_scrpos.ln = sc_height-1; + m->m_ifile = curr_ifile; + break; + case '.': + /* + * Current position in the current file. + */ + m = &sm; + get_scrpos(&m->m_scrpos); + m->m_ifile = curr_ifile; + break; + case '\'': + /* + * The "last mark". + */ + m = &marks[LASTMARK]; + break; + default: + /* + * Must be a user-defined mark. + */ + m = getumark(c); + if (m == NULL) + break; + if (m->m_scrpos.pos == NULL_POSITION) + { + error("Mark not set", NULL_PARG); + return (NULL); + } + break; + } + return (m); +} + +/* + * Is a mark letter is invalid? + */ + public int +badmark(c) + int c; +{ + return (getmark(c) == NULL); +} + +/* + * Set a user-defined mark. + */ + public void +setmark(c) + int c; +{ + register struct mark *m; + struct scrpos scrpos; + + m = getumark(c); + if (m == NULL) + return; + get_scrpos(&scrpos); + m->m_scrpos = scrpos; + m->m_ifile = curr_ifile; +} + +/* + * Set lmark (the mark named by the apostrophe). + */ + public void +lastmark() +{ + struct scrpos scrpos; + + if (ch_getflags() & CH_HELPFILE) + return; + get_scrpos(&scrpos); + if (scrpos.pos == NULL_POSITION) + return; + marks[LASTMARK].m_scrpos = scrpos; + marks[LASTMARK].m_ifile = curr_ifile; +} + +/* + * Go to a mark. + */ + public void +gomark(c) + int c; +{ + register struct mark *m; + struct scrpos scrpos; + + m = getmark(c); + if (m == NULL) + return; + + /* + * If we're trying to go to the lastmark and + * it has not been set to anything yet, + * set it to the beginning of the current file. + */ + if (m == &marks[LASTMARK] && m->m_scrpos.pos == NULL_POSITION) + { + m->m_ifile = curr_ifile; + m->m_scrpos.pos = ch_zero(); + m->m_scrpos.ln = jump_sline; + } + + /* + * If we're using lmark, we must save the screen position now, + * because if we call edit_ifile() below, lmark will change. + * (We save the screen position even if we're not using lmark.) + */ + scrpos = m->m_scrpos; + if (m->m_ifile != curr_ifile) + { + /* + * Not in the current file; edit the correct file. + */ + if (edit_ifile(m->m_ifile)) + return; + } + + jump_loc(scrpos.pos, scrpos.ln); +} + +/* + * Return the position associated with a given mark letter. + * + * We don't return which screen line the position + * is associated with, but this doesn't matter much, + * because it's always the first non-blank line on the screen. + */ + public POSITION +markpos(c) + int c; +{ + register struct mark *m; + + m = getmark(c); + if (m == NULL) + return (NULL_POSITION); + + if (m->m_ifile != curr_ifile) + { + error("Mark not in current file", NULL_PARG); + return (NULL_POSITION); + } + return (m->m_scrpos.pos); +} + +/* + * Clear the marks associated with a specified ifile. + */ + public void +unmark(ifile) + IFILE ifile; +{ + int i; + + for (i = 0; i < NMARKS; i++) + if (marks[i].m_ifile == ifile) + marks[i].m_scrpos.pos = NULL_POSITION; +} diff --git a/files/Sources/files/less/mkfuncs.awk b/files/Sources/files/less/mkfuncs.awk new file mode 100755 index 00000000..dea28ace --- /dev/null +++ b/files/Sources/files/less/mkfuncs.awk @@ -0,0 +1,9 @@ +BEGIN { FS="("; state = 0 } + +/^ public/ { ftype = $0; state = 1 } + +{ if (state == 1) + state = 2 + else if (state == 2) + { print ftype,$1,"();"; state = 0 } +} diff --git a/files/Sources/files/less/mkhelp.c b/files/Sources/files/less/mkhelp.c new file mode 100644 index 00000000..fb486ef9 --- /dev/null +++ b/files/Sources/files/less/mkhelp.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Silly little program to generate the help.c source file + * from the less.hlp text file. + * help.c just contains a char array whose contents are + * the contents of less.hlp. + */ + +#include + + int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch; + int prevch; + + printf("/* This file was generated by mkhelp from less.hlp */\n"); + printf("#include \"less.h\"\n"); + printf("constant char helpdata[] = {\n"); + ch = 0; + while (prevch = ch, (ch = getchar()) != EOF) + { + switch (ch) + { + case '\'': + printf("'\\'',"); + break; + case '\\': + printf("'\\\\',"); + break; + case '\b': + printf("'\\b',"); + break; + case '\t': + printf("'\\t',"); + break; + case '\n': + if (prevch != '\r') + printf("'\\n',\n"); + break; + case '\r': + if (prevch != '\n') + printf("'\\n',\n"); + break; + default: + if (ch >= ' ' && ch < 0x7f) + printf("'%c',", ch); + else + printf("0x%02x,", ch); + break; + } + } + /* Add an extra null char to avoid having a trailing comma. */ + printf(" 0 };\n"); + printf("constant int size_helpdata = sizeof(helpdata) - 1;\n"); + return (0); +} diff --git a/files/Sources/files/less/mkinstalldirs b/files/Sources/files/less/mkinstalldirs new file mode 100755 index 00000000..91f6d04e --- /dev/null +++ b/files/Sources/files/less/mkinstalldirs @@ -0,0 +1,32 @@ +#!/bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman +# Created: 1993-05-16 +# Last modified: 1994-03-25 +# Public domain + +errstatus=0 + +for file in ${1+"$@"} ; do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d in ${1+"$@"} ; do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" 1>&2 + mkdir "$pathcomp" || errstatus=$? + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus + +# mkinstalldirs ends here diff --git a/files/Sources/files/less/mkutable b/files/Sources/files/less/mkutable new file mode 100755 index 00000000..ff107b51 --- /dev/null +++ b/files/Sources/files/less/mkutable @@ -0,0 +1,85 @@ +#! /usr/bin/perl +use strict; + +my $USAGE = <<__EOF__; + usage: mkutable [-n] [-f#] type... [--] [<] UnicodeData.txt + -n = take non-matching types + -f = zero-based type field (default 2) +__EOF__ + +use vars qw( $opt_f $opt_n ); +use Getopt::Std; +my $type_field = 2; + +exit (main() ? 0 : 1); + +sub main { + my $date = `date`; + chomp $date; + my $args = join ' ', @ARGV; + my $header = "/* Generated by \"$0 $args\" on $date */\n"; + + die $USAGE if not getopts('f:n'); + $type_field = $opt_f if $opt_f; + my %types; + my $arg; + while ($arg = shift @ARGV) { + last if $arg eq '--'; + $types{$arg} = 1; + } + my %out = ( 'types' => \%types ); + + print $header; + my $last_code = 0; + while (<>) { + chomp; + s/#.*//; + my @fields = split /;/; + next if not @fields; + my ($lo_code, $hi_code); + my $codes = $fields[0]; + if ($codes =~ /(\w+)\.\.(\w+)/) { + $lo_code = hex $1; + $hi_code = hex $2; + } else { + $lo_code = $hi_code = hex $fields[0]; + } + my $type = $fields[$type_field]; + $type =~ s/\s//g; + for ($last_code = $lo_code; $last_code <= $hi_code; ++$last_code) { + output(\%out, $last_code, $type); + } + } + output(\%out, $last_code); + return 1; +} + +sub output { + my ($out, $code, $type) = @_; + my $type_ok = ($type and ${${$out}{types}}{$type}); + $type_ok = not $type_ok if $opt_n; + my $prev_code = $$out{prev_code}; + + if (not $type_ok) { + end_run($out, $prev_code); + } elsif (not $$out{in_run} or $type ne $$out{run_type} or $code != $prev_code+1) { + end_run($out, $prev_code); + start_run($out, $code, $type); + } + $$out{prev_code} = $code; +} + +sub start_run { + my ($out, $code, $type) = @_; + $$out{start_code} = $code; + $$out{prev_code} = $code; + $$out{run_type} = $type; + $$out{in_run} = 1; +} + +sub end_run { + my ($out, $code) = @_; + return if not $$out{in_run}; + printf "\t{ 0x%04x, 0x%04x }, /* %s */\n", $$out{start_code}, $code, $$out{run_type}; + $$out{in_run} = 0; +} diff --git a/files/Sources/files/less/optfunc.c b/files/Sources/files/less/optfunc.c new file mode 100644 index 00000000..41215d9b --- /dev/null +++ b/files/Sources/files/less/optfunc.c @@ -0,0 +1,762 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Handling functions for command line options. + * + * Most options are handled by the generic code in option.c. + * But all string options, and a few non-string options, require + * special handling specific to the particular option. + * This special processing is done by the "handling functions" in this file. + * + * Each handling function is passed a "type" and, if it is a string + * option, the string which should be "assigned" to the option. + * The type may be one of: + * INIT The option is being initialized from the command line. + * TOGGLE The option is being changed from within the program. + * QUERY The setting of the option is merely being queried. + */ + +#include "less.h" +#include "option.h" + +extern int nbufs; +extern int bufspace; +extern int pr_type; +extern int plusoption; +extern int swindow; +extern int sc_width; +extern int sc_height; +extern int secure; +extern int dohelp; +extern int any_display; +extern char openquote; +extern char closequote; +extern char *prproto[]; +extern char *eqproto; +extern char *hproto; +extern char *wproto; +extern char *every_first_cmd; +extern IFILE curr_ifile; +extern char version[]; +extern int jump_sline; +extern int jump_sline_fraction; +extern int shift_count; +extern int shift_count_fraction; +extern int less_is_more; +extern char* dashp_commands; +#if LOGFILE +extern char *namelogfile; +extern int force_logfile; +extern int logfile; +#endif +#if TAGS +public char *tagoption = NULL; +extern char *tags; +extern char ztags[]; +#endif +#if MSDOS_COMPILER +extern int nm_fg_color, nm_bg_color; +extern int bo_fg_color, bo_bg_color; +extern int ul_fg_color, ul_bg_color; +extern int so_fg_color, so_bg_color; +extern int bl_fg_color, bl_bg_color; +extern int sgr_mode; +#endif + + +#if LOGFILE +/* + * Handler for -o option. + */ + public void +opt_o(type, s) + int type; + char *s; +{ + PARG parg; + + if (secure) + { + error("log file support is not available", NULL_PARG); + return; + } + switch (type) + { + case INIT: + namelogfile = save(s); + break; + case TOGGLE: + if (ch_getflags() & CH_CANSEEK) + { + error("Input is not a pipe", NULL_PARG); + return; + } + if (logfile >= 0) + { + error("Log file is already in use", NULL_PARG); + return; + } + s = skipsp(s); + if (namelogfile != NULL) + free(namelogfile); + namelogfile = lglob(s); + use_logfile(namelogfile); + sync_logfile(); + break; + case QUERY: + if (logfile < 0) + error("No log file", NULL_PARG); + else + { + parg.p_string = namelogfile; + error("Log file \"%s\"", &parg); + } + break; + } +} + +/* + * Handler for -O option. + */ + public void +opt__O(type, s) + int type; + char *s; +{ + force_logfile = TRUE; + opt_o(type, s); +} +#endif + +/* + * Handlers for -j option. + */ + public void +opt_j(type, s) + int type; + char *s; +{ + PARG parg; + char buf[16]; + int len; + int err; + + switch (type) + { + case INIT: + case TOGGLE: + if (*s == '.') + { + s++; + jump_sline_fraction = getfraction(&s, "j", &err); + if (err) + error("Invalid line fraction", NULL_PARG); + else + calc_jump_sline(); + } else + { + int sline = getnum(&s, "j", &err); + if (err) + error("Invalid line number", NULL_PARG); + else + { + jump_sline = sline; + jump_sline_fraction = -1; + } + } + break; + case QUERY: + if (jump_sline_fraction < 0) + { + parg.p_int = jump_sline; + error("Position target at screen line %d", &parg); + } else + { + + sprintf(buf, ".%06d", jump_sline_fraction); + len = (int) strlen(buf); + while (len > 2 && buf[len-1] == '0') + len--; + buf[len] = '\0'; + parg.p_string = buf; + error("Position target at screen position %s", &parg); + } + break; + } +} + + public void +calc_jump_sline() +{ + if (jump_sline_fraction < 0) + return; + jump_sline = sc_height * jump_sline_fraction / NUM_FRAC_DENOM; +} + +/* + * Handlers for -# option. + */ + public void +opt_shift(type, s) + int type; + char *s; +{ + PARG parg; + char buf[16]; + int len; + int err; + + switch (type) + { + case INIT: + case TOGGLE: + if (*s == '.') + { + s++; + shift_count_fraction = getfraction(&s, "#", &err); + if (err) + error("Invalid column fraction", NULL_PARG); + else + calc_shift_count(); + } else + { + int hs = getnum(&s, "#", &err); + if (err) + error("Invalid column number", NULL_PARG); + else + { + shift_count = hs; + shift_count_fraction = -1; + } + } + break; + case QUERY: + if (shift_count_fraction < 0) + { + parg.p_int = shift_count; + error("Horizontal shift %d columns", &parg); + } else + { + + sprintf(buf, ".%06d", shift_count_fraction); + len = (int) strlen(buf); + while (len > 2 && buf[len-1] == '0') + len--; + buf[len] = '\0'; + parg.p_string = buf; + error("Horizontal shift %s of screen width", &parg); + } + break; + } +} + public void +calc_shift_count() +{ + if (shift_count_fraction < 0) + return; + shift_count = sc_width * shift_count_fraction / NUM_FRAC_DENOM; +} + +#if USERFILE + public void +opt_k(type, s) + int type; + char *s; +{ + PARG parg; + + switch (type) + { + case INIT: + if (lesskey(s, 0)) + { + parg.p_string = s; + error("Cannot use lesskey file \"%s\"", &parg); + } + break; + } +} +#endif + +#if TAGS +/* + * Handler for -t option. + */ + public void +opt_t(type, s) + int type; + char *s; +{ + IFILE save_ifile; + POSITION pos; + + switch (type) + { + case INIT: + tagoption = save(s); + /* Do the rest in main() */ + break; + case TOGGLE: + if (secure) + { + error("tags support is not available", NULL_PARG); + break; + } + findtag(skipsp(s)); + save_ifile = save_curr_ifile(); + /* + * Try to open the file containing the tag + * and search for the tag in that file. + */ + if (edit_tagfile() || (pos = tagsearch()) == NULL_POSITION) + { + /* Failed: reopen the old file. */ + reedit_ifile(save_ifile); + break; + } + unsave_ifile(save_ifile); + jump_loc(pos, jump_sline); + break; + } +} + +/* + * Handler for -T option. + */ + public void +opt__T(type, s) + int type; + char *s; +{ + PARG parg; + + switch (type) + { + case INIT: + tags = save(s); + break; + case TOGGLE: + s = skipsp(s); + if (tags != NULL && tags != ztags) + free(tags); + tags = lglob(s); + break; + case QUERY: + parg.p_string = tags; + error("Tags file \"%s\"", &parg); + break; + } +} +#endif + +/* + * Handler for -p option. + */ + public void +opt_p(type, s) + int type; + register char *s; +{ + switch (type) + { + case INIT: + /* + * Unget a command for the specified string. + */ + if (less_is_more) + { + /* + * In "more" mode, the -p argument is a command, + * not a search string, so we don't need a slash. + */ + every_first_cmd = save(s); + } else + { + plusoption = TRUE; + ungetcc(CHAR_END_COMMAND); + ungetsc(s); + /* + * {{ This won't work if the "/" command is + * changed or invalidated by a .lesskey file. }} + */ + ungetsc("/"); + } + break; + } +} + +/* + * Handler for -P option. + */ + public void +opt__P(type, s) + int type; + register char *s; +{ + register char **proto; + PARG parg; + + switch (type) + { + case INIT: + case TOGGLE: + /* + * Figure out which prototype string should be changed. + */ + switch (*s) + { + case 's': proto = &prproto[PR_SHORT]; s++; break; + case 'm': proto = &prproto[PR_MEDIUM]; s++; break; + case 'M': proto = &prproto[PR_LONG]; s++; break; + case '=': proto = &eqproto; s++; break; + case 'h': proto = &hproto; s++; break; + case 'w': proto = &wproto; s++; break; + default: proto = &prproto[PR_SHORT]; break; + } + free(*proto); + *proto = save(s); + break; + case QUERY: + parg.p_string = prproto[pr_type]; + error("%s", &parg); + break; + } +} + +/* + * Handler for the -b option. + */ + /*ARGSUSED*/ + public void +opt_b(type, s) + int type; + char *s; +{ + switch (type) + { + case INIT: + case TOGGLE: + /* + * Set the new number of buffers. + */ + ch_setbufspace(bufspace); + break; + case QUERY: + break; + } +} + +/* + * Handler for the -i option. + */ + /*ARGSUSED*/ + public void +opt_i(type, s) + int type; + char *s; +{ + switch (type) + { + case TOGGLE: + chg_caseless(); + break; + case QUERY: + case INIT: + break; + } +} + +/* + * Handler for the -V option. + */ + /*ARGSUSED*/ + public void +opt__V(type, s) + int type; + char *s; +{ + switch (type) + { + case TOGGLE: + case QUERY: + dispversion(); + break; + case INIT: + /* + * Force output to stdout per GNU standard for --version output. + */ + any_display = 1; + putstr("less "); + putstr(version); + putstr(" ("); +#if HAVE_GNU_REGEX + putstr("GNU "); +#endif +#if HAVE_POSIX_REGCOMP + putstr("POSIX "); +#endif +#if HAVE_PCRE + putstr("PCRE "); +#endif +#if HAVE_RE_COMP + putstr("BSD "); +#endif +#if HAVE_REGCMP + putstr("V8 "); +#endif +#if HAVE_V8_REGCOMP + putstr("Spencer V8 "); +#endif +#if !HAVE_GNU_REGEX && !HAVE_POSIX_REGCOMP && !HAVE_PCRE && !HAVE_RE_COMP && !HAVE_REGCMP && !HAVE_V8_REGCOMP + putstr("no "); +#endif + putstr("regular expressions)\n"); + putstr("Copyright (C) 1984-2016 Mark Nudelman\n\n"); + putstr("less comes with NO WARRANTY, to the extent permitted by law.\n"); + putstr("For information about the terms of redistribution,\n"); + putstr("see the file named README in the less distribution.\n"); + putstr("Homepage: http://www.greenwoodsoftware.com/less\n"); + quit(QUIT_OK); + break; + } +} + +#if MSDOS_COMPILER +/* + * Parse an MSDOS color descriptor. + */ + static void +colordesc(s, fg_color, bg_color) + char *s; + int *fg_color; + int *bg_color; +{ + int fg, bg; + int err; + + fg = getnum(&s, "D", &err); + if (err) + { + error("Missing fg color in -D", NULL_PARG); + return; + } + if (*s != '.') + bg = nm_bg_color; + else + { + s++; + bg = getnum(&s, "D", &err); + if (err) + { + error("Missing bg color in -D", NULL_PARG); + return; + } + } + if (*s != '\0') + error("Extra characters at end of -D option", NULL_PARG); + *fg_color = fg; + *bg_color = bg; +} + +/* + * Handler for the -D option. + */ + /*ARGSUSED*/ + public void +opt_D(type, s) + int type; + char *s; +{ + PARG p; + + switch (type) + { + case INIT: + case TOGGLE: + switch (*s++) + { + case 'n': + colordesc(s, &nm_fg_color, &nm_bg_color); + break; + case 'd': + colordesc(s, &bo_fg_color, &bo_bg_color); + break; + case 'u': + colordesc(s, &ul_fg_color, &ul_bg_color); + break; + case 'k': + colordesc(s, &bl_fg_color, &bl_bg_color); + break; + case 's': + colordesc(s, &so_fg_color, &so_bg_color); + break; + case 'a': + sgr_mode = !sgr_mode; + break; + default: + error("-D must be followed by n, d, u, k, s or a", NULL_PARG); + break; + } + if (type == TOGGLE) + { + at_enter(AT_STANDOUT); + at_exit(); + } + break; + case QUERY: + p.p_string = (sgr_mode) ? "on" : "off"; + error("SGR mode is %s", &p); + break; + } +} +#endif + +/* + * Handler for the -x option. + */ + public void +opt_x(type, s) + int type; + register char *s; +{ + extern int tabstops[]; + extern int ntabstops; + extern int tabdefault; + char msg[60+(4*TABSTOP_MAX)]; + int i; + PARG p; + + switch (type) + { + case INIT: + case TOGGLE: + /* Start at 1 because tabstops[0] is always zero. */ + for (i = 1; i < TABSTOP_MAX; ) + { + int n = 0; + s = skipsp(s); + while (*s >= '0' && *s <= '9') + n = (10 * n) + (*s++ - '0'); + if (n > tabstops[i-1]) + tabstops[i++] = n; + s = skipsp(s); + if (*s++ != ',') + break; + } + if (i < 2) + return; + ntabstops = i; + tabdefault = tabstops[ntabstops-1] - tabstops[ntabstops-2]; + break; + case QUERY: + strcpy(msg, "Tab stops "); + if (ntabstops > 2) + { + for (i = 1; i < ntabstops; i++) + { + if (i > 1) + strcat(msg, ","); + sprintf(msg+strlen(msg), "%d", tabstops[i]); + } + sprintf(msg+strlen(msg), " and then "); + } + sprintf(msg+strlen(msg), "every %d spaces", + tabdefault); + p.p_string = msg; + error("%s", &p); + break; + } +} + + +/* + * Handler for the -" option. + */ + public void +opt_quote(type, s) + int type; + register char *s; +{ + char buf[3]; + PARG parg; + + switch (type) + { + case INIT: + case TOGGLE: + if (s[0] == '\0') + { + openquote = closequote = '\0'; + break; + } + if (s[1] != '\0' && s[2] != '\0') + { + error("-\" must be followed by 1 or 2 chars", NULL_PARG); + return; + } + openquote = s[0]; + if (s[1] == '\0') + closequote = openquote; + else + closequote = s[1]; + break; + case QUERY: + buf[0] = openquote; + buf[1] = closequote; + buf[2] = '\0'; + parg.p_string = buf; + error("quotes %s", &parg); + break; + } +} + +/* + * "-?" means display a help message. + * If from the command line, exit immediately. + */ + /*ARGSUSED*/ + public void +opt_query(type, s) + int type; + char *s; +{ + switch (type) + { + case QUERY: + case TOGGLE: + error("Use \"h\" for help", NULL_PARG); + break; + case INIT: + dohelp = 1; + } +} + +/* + * Get the "screen window" size. + */ + public int +get_swindow() +{ + if (swindow > 0) + return (swindow); + return (sc_height + swindow); +} + +/* Following handler function is used in Unix 2003 override of -p option. */ + + public void +opt_dashp(type,s) + int type; + char *s; +{ + dashp_commands = save(s); +} + diff --git a/files/Sources/files/less/option.c b/files/Sources/files/less/option.c new file mode 100644 index 00000000..72c2632e --- /dev/null +++ b/files/Sources/files/less/option.c @@ -0,0 +1,727 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Process command line options. + * + * Each option is a single letter which controls a program variable. + * The options have defaults which may be changed via + * the command line option, toggled via the "-" command, + * or queried via the "_" command. + */ + +#include "less.h" +#include "option.h" + +static struct loption *pendopt; +public int plusoption = FALSE; + +static char *optstring(); +static int flip_triple(); + +extern int screen_trashed; +extern int less_is_more; +extern int quit_at_eof; +extern char *every_first_cmd; +extern int opt_use_backslash; +extern int unix2003_compat; + +/* + * Return a printable description of an option. + */ + static char * +opt_desc(o) + struct loption *o; +{ + static char buf[OPTNAME_MAX + 10]; + if (o->oletter == OLETTER_NONE) + SNPRINTF1(buf, sizeof(buf), "--%s", o->onames->oname); + else + SNPRINTF2(buf, sizeof(buf), "-%c (--%s)", o->oletter, o->onames->oname); + return (buf); +} + +/* + * Return a string suitable for printing as the "name" of an option. + * For example, if the option letter is 'x', just return "-x". + */ + public char * +propt(c) + int c; +{ + static char buf[8]; + + sprintf(buf, "-%s", prchar(c)); + return (buf); +} + +/* + * Scan an argument (either from the command line or from the + * LESS environment variable) and process it. + */ + public void +scan_option(s) + char *s; +{ + register struct loption *o; + register int optc; + char *optname; + char *printopt; + char *str; + int set_default; + int lc; + int err; + PARG parg; + + if (s == NULL) + return; + + /* + * If we have a pending option which requires an argument, + * handle it now. + * This happens if the previous option was, for example, "-P" + * without a following string. In that case, the current + * option is simply the argument for the previous option. + */ + if (pendopt != NULL) + { + switch (pendopt->otype & OTYPE) + { + case STRING: + (*pendopt->ofunc)(INIT, s); + break; + case NUMBER: + printopt = opt_desc(pendopt); + *(pendopt->ovar) = getnum(&s, printopt, (int*)NULL); + break; + } + pendopt = NULL; + return; + } + + set_default = FALSE; + optname = NULL; + + while (*s != '\0') + { + /* + * Check some special cases first. + */ + switch (optc = *s++) + { + case ' ': + case '\t': + case END_OPTION_STRING: + continue; + case '-': + /* + * "--" indicates an option name instead of a letter. + */ + if (*s == '-') + { + optname = ++s; + break; + } + /* + * "-+" means set these options back to their defaults. + * (They may have been set otherwise by previous + * options.) + */ + set_default = (*s == '+'); + if (set_default) + s++; + continue; + case '+': + /* + * An option prefixed by a "+" is ungotten, so + * that it is interpreted as less commands + * processed at the start of the first input file. + * "++" means process the commands at the start of + * EVERY input file. + */ + plusoption = TRUE; + s = optstring(s, &str, propt('+'), NULL); + if (s == NULL) + return; + if (*str == '+') + every_first_cmd = save(str+1); + else + { + ungetcc(CHAR_END_COMMAND); + ungetsc(str); + } + free(str); + continue; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + /* + * Special "more" compatibility form "-" + * instead of -z to set the scrolling + * window size. + */ + s--; + optc = 'z'; + break; + case 'n': /* For more under Unix2003, -n acts differently */ + if (less_is_more) { + if (unix2003_compat) + optc = ']'; + else + optc = 'z'; + } + break; + case 'i': /* For more under Unix2003, -i acts like -I */ + if (less_is_more) { + if (unix2003_compat) + optc = 'I'; + } + break; + case 'p': /* For more under Unix2003, -p acts differently */ + if (less_is_more) { + if (unix2003_compat) + optc = '}'; + } + break; + } + + /* + * Not a special case. + * Look up the option letter in the option table. + */ + err = 0; + if (optname == NULL) + { + printopt = propt(optc); + lc = ASCII_IS_LOWER(optc); + o = findopt(optc); + } else + { + printopt = optname; + lc = ASCII_IS_LOWER(optname[0]); + o = findopt_name(&optname, NULL, &err); + s = optname; + optname = NULL; + if (*s == '\0' || *s == ' ') + { + /* + * The option name matches exactly. + */ + ; + } else if (*s == '=') + { + /* + * The option name is followed by "=value". + */ + if (o != NULL && + (o->otype & OTYPE) != STRING && + (o->otype & OTYPE) != NUMBER) + { + parg.p_string = printopt; + error("The %s option should not be followed by =", + &parg); + return; + } + s++; + } else + { + /* + * The specified name is longer than the + * real option name. + */ + o = NULL; + } + } + if (o == NULL) + { + parg.p_string = printopt; + if (err == OPT_AMBIG) + error("%s is an ambiguous abbreviation (\"less --help\" for help)", + &parg); + else + error("There is no %s option (\"less --help\" for help)", + &parg); + return; + } + + str = NULL; + switch (o->otype & OTYPE) + { + case BOOL: + if (set_default) + *(o->ovar) = o->odefault; + else + *(o->ovar) = ! o->odefault; + break; + case TRIPLE: + if (set_default) + *(o->ovar) = o->odefault; + else + *(o->ovar) = flip_triple(o->odefault, lc); + break; + case STRING: + if (*s == '\0') + { + /* + * Set pendopt and return. + * We will get the string next time + * scan_option is called. + */ + pendopt = o; + return; + } + /* + * Don't do anything here. + * All processing of STRING options is done by + * the handling function. + */ + while (*s == ' ') + s++; + s = optstring(s, &str, printopt, o->odesc[1]); + if (s == NULL) + return; + break; + case NUMBER: + if (*s == '\0') + { + pendopt = o; + return; + } + *(o->ovar) = getnum(&s, printopt, (int*)NULL); + break; + } + /* + * If the option has a handling function, call it. + */ + if (o->ofunc != NULL) + (*o->ofunc)(INIT, str); + if (str != NULL) + free(str); + } +} + +/* + * Toggle command line flags from within the program. + * Used by the "-" and "_" commands. + * how_toggle may be: + * OPT_NO_TOGGLE just report the current setting, without changing it. + * OPT_TOGGLE invert the current setting + * OPT_UNSET set to the default value + * OPT_SET set to the inverse of the default value + */ + public void +toggle_option(o, lower, s, how_toggle) + struct loption *o; + int lower; + char *s; + int how_toggle; +{ + register int num; + int no_prompt; + int err; + PARG parg; + + no_prompt = (how_toggle & OPT_NO_PROMPT); + how_toggle &= ~OPT_NO_PROMPT; + + if (o == NULL) + { + error("No such option", NULL_PARG); + return; + } + + if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE)) + { + parg.p_string = opt_desc(o); + error("Cannot change the %s option", &parg); + return; + } + + if (how_toggle == OPT_NO_TOGGLE && (o->otype & NO_QUERY)) + { + parg.p_string = opt_desc(o); + error("Cannot query the %s option", &parg); + return; + } + + /* + * Check for something which appears to be a do_toggle + * (because the "-" command was used), but really is not. + * This could be a string option with no string, or + * a number option with no number. + */ + switch (o->otype & OTYPE) + { + case STRING: + case NUMBER: + if (how_toggle == OPT_TOGGLE && *s == '\0') + how_toggle = OPT_NO_TOGGLE; + break; + } + +#if HILITE_SEARCH + if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT)) + repaint_hilite(0); +#endif + + /* + * Now actually toggle (change) the variable. + */ + if (how_toggle != OPT_NO_TOGGLE) + { + switch (o->otype & OTYPE) + { + case BOOL: + /* + * Boolean. + */ + switch (how_toggle) + { + case OPT_TOGGLE: + *(o->ovar) = ! *(o->ovar); + break; + case OPT_UNSET: + *(o->ovar) = o->odefault; + break; + case OPT_SET: + *(o->ovar) = ! o->odefault; + break; + } + break; + case TRIPLE: + /* + * Triple: + * If user gave the lower case letter, then switch + * to 1 unless already 1, in which case make it 0. + * If user gave the upper case letter, then switch + * to 2 unless already 2, in which case make it 0. + */ + switch (how_toggle) + { + case OPT_TOGGLE: + *(o->ovar) = flip_triple(*(o->ovar), lower); + break; + case OPT_UNSET: + *(o->ovar) = o->odefault; + break; + case OPT_SET: + *(o->ovar) = flip_triple(o->odefault, lower); + break; + } + break; + case STRING: + /* + * String: don't do anything here. + * The handling function will do everything. + */ + switch (how_toggle) + { + case OPT_SET: + case OPT_UNSET: + error("Cannot use \"-+\" or \"--\" for a string option", + NULL_PARG); + return; + } + break; + case NUMBER: + /* + * Number: set the variable to the given number. + */ + switch (how_toggle) + { + case OPT_TOGGLE: + num = getnum(&s, NULL, &err); + if (!err) + *(o->ovar) = num; + break; + case OPT_UNSET: + *(o->ovar) = o->odefault; + break; + case OPT_SET: + error("Can't use \"-!\" for a numeric option", + NULL_PARG); + return; + } + break; + } + } + + /* + * Call the handling function for any special action + * specific to this option. + */ + if (o->ofunc != NULL) + (*o->ofunc)((how_toggle==OPT_NO_TOGGLE) ? QUERY : TOGGLE, s); + +#if HILITE_SEARCH + if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT)) + chg_hilite(); +#endif + + if (!no_prompt) + { + /* + * Print a message describing the new setting. + */ + switch (o->otype & OTYPE) + { + case BOOL: + case TRIPLE: + /* + * Print the odesc message. + */ + error(o->odesc[*(o->ovar)], NULL_PARG); + break; + case NUMBER: + /* + * The message is in odesc[1] and has a %d for + * the value of the variable. + */ + parg.p_int = *(o->ovar); + error(o->odesc[1], &parg); + break; + case STRING: + /* + * Message was already printed by the handling function. + */ + break; + } + } + + if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT)) + screen_trashed = TRUE; +} + +/* + * "Toggle" a triple-valued option. + */ + static int +flip_triple(val, lc) + int val; + int lc; +{ + if (lc) + return ((val == OPT_ON) ? OPT_OFF : OPT_ON); + else + return ((val == OPT_ONPLUS) ? OPT_OFF : OPT_ONPLUS); +} + +/* + * Determine if an option takes a parameter. + */ + public int +opt_has_param(o) + struct loption *o; +{ + if (o == NULL) + return (0); + if (o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE)) + return (0); + return (1); +} + +/* + * Return the prompt to be used for a given option letter. + * Only string and number valued options have prompts. + */ + public char * +opt_prompt(o) + struct loption *o; +{ + if (o == NULL || (o->otype & (STRING|NUMBER)) == 0) + return ("?"); + return (o->odesc[0]); +} + +/* + * Return whether or not there is a string option pending; + * that is, if the previous option was a string-valued option letter + * (like -P) without a following string. + * In that case, the current option is taken to be the string for + * the previous option. + */ + public int +isoptpending() +{ + return (pendopt != NULL); +} + +/* + * Print error message about missing string. + */ + static void +nostring(printopt) + char *printopt; +{ + PARG parg; + parg.p_string = printopt; + error("Value is required after %s", &parg); +} + +/* + * Print error message if a STRING type option is not followed by a string. + */ + public void +nopendopt() +{ + nostring(opt_desc(pendopt)); + // iOS: + pendopt = NULL; +} + +/* + * Scan to end of string or to an END_OPTION_STRING character. + * In the latter case, replace the char with a null char. + * Return a pointer to the remainder of the string, if any. + */ + static char * +optstring(s, p_str, printopt, validchars) + char *s; + char **p_str; + char *printopt; + char *validchars; +{ + register char *p; + register char *out; + + if (*s == '\0') + { + nostring(printopt); + return (NULL); + } + /* Alloc could be more than needed, but not worth trimming. */ + *p_str = (char *) ecalloc(strlen(s)+1, sizeof(char)); + out = *p_str; + + for (p = s; *p != '\0'; p++) + { + if (opt_use_backslash && *p == '\\' && p[1] != '\0') + { + /* Take next char literally. */ + ++p; + } else + { + if (*p == END_OPTION_STRING || + (validchars != NULL && strchr(validchars, *p) == NULL)) + /* End of option string. */ + break; + } + *out++ = *p; + } + *out = '\0'; + return (p); +} + +/* + */ + static int +num_error(printopt, errp) + char *printopt; + int *errp; +{ + PARG parg; + + if (errp != NULL) + { + *errp = TRUE; + return (-1); + } + if (printopt != NULL) + { + parg.p_string = printopt; + error("Number is required after %s", &parg); + } + return (-1); +} + +/* + * Translate a string into a number. + * Like atoi(), but takes a pointer to a char *, and updates + * the char * to point after the translated number. + */ + public int +getnum(sp, printopt, errp) + char **sp; + char *printopt; + int *errp; +{ + register char *s; + register int n; + register int neg; + + s = skipsp(*sp); + neg = FALSE; + if (*s == '-') + { + neg = TRUE; + s++; + } + if (*s < '0' || *s > '9') + return (num_error(printopt, errp)); + + n = 0; + while (*s >= '0' && *s <= '9') + n = 10 * n + *s++ - '0'; + *sp = s; + if (errp != NULL) + *errp = FALSE; + if (neg) + n = -n; + return (n); +} + +/* + * Translate a string into a fraction, represented by the part of a + * number which would follow a decimal point. + * The value of the fraction is returned as parts per NUM_FRAC_DENOM. + * That is, if "n" is returned, the fraction intended is n/NUM_FRAC_DENOM. + */ + public long +getfraction(sp, printopt, errp) + char **sp; + char *printopt; + int *errp; +{ + register char *s; + long frac = 0; + int fraclen = 0; + + s = skipsp(*sp); + if (*s < '0' || *s > '9') + return (num_error(printopt, errp)); + + for ( ; *s >= '0' && *s <= '9'; s++) + { + frac = (frac * 10) + (*s - '0'); + fraclen++; + } + if (fraclen > NUM_LOG_FRAC_DENOM) + while (fraclen-- > NUM_LOG_FRAC_DENOM) + frac /= 10; + else + while (fraclen++ < NUM_LOG_FRAC_DENOM) + frac *= 10; + *sp = s; + if (errp != NULL) + *errp = FALSE; + return (frac); +} + + +/* + * Get the value of the -e flag. + */ + public int +get_quit_at_eof() +{ + if (!less_is_more) + return quit_at_eof; + /* When less_is_more is set, the -e flag semantics are different. */ + return quit_at_eof ? OPT_ONPLUS : OPT_ON; +} diff --git a/files/Sources/files/less/option.h b/files/Sources/files/less/option.h new file mode 100644 index 00000000..38e08f75 --- /dev/null +++ b/files/Sources/files/less/option.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +#define END_OPTION_STRING ('$') + +/* + * Types of options. + */ +#define BOOL 01 /* Boolean option: 0 or 1 */ +#define TRIPLE 02 /* Triple-valued option: 0, 1 or 2 */ +#define NUMBER 04 /* Numeric option */ +#define STRING 010 /* String-valued option */ +#define NOVAR 020 /* No associated variable */ +#define REPAINT 040 /* Repaint screen after toggling option */ +#define NO_TOGGLE 0100 /* Option cannot be toggled with "-" cmd */ +#define HL_REPAINT 0200 /* Repaint hilites after toggling option */ +#define NO_QUERY 0400 /* Option cannot be queried with "_" cmd */ +#define INIT_HANDLER 01000 /* Call option handler function at startup */ + +#define OTYPE (BOOL|TRIPLE|NUMBER|STRING|NOVAR) + +#define OLETTER_NONE '\1' /* Invalid option letter */ + +/* + * Argument to a handling function tells what type of activity: + */ +#define INIT 0 /* Initialization (from command line) */ +#define QUERY 1 /* Query (from _ or - command) */ +#define TOGGLE 2 /* Change value (from - command) */ + +/* Flag to toggle_option to specify how to "toggle" */ +#define OPT_NO_TOGGLE 0 +#define OPT_TOGGLE 1 +#define OPT_UNSET 2 +#define OPT_SET 3 +#define OPT_NO_PROMPT 0100 + +/* Error code from findopt_name */ +#define OPT_AMBIG 1 + +struct optname +{ + char *oname; /* Long (GNU-style) option name */ + struct optname *onext; /* List of synonymous option names */ +}; + +#define OPTNAME_MAX 32 /* Max length of long option name */ + +struct loption +{ + char oletter; /* The controlling letter (a-z) */ + struct optname *onames; /* Long (GNU-style) option name */ + int otype; /* Type of the option */ + int odefault; /* Default value */ + int *ovar; /* Pointer to the associated variable */ + void (*ofunc)(); /* Pointer to special handling function */ + char *odesc[3]; /* Description of each value */ +}; + diff --git a/files/Sources/files/less/opttbl.c b/files/Sources/files/less/opttbl.c new file mode 100644 index 00000000..689548fe --- /dev/null +++ b/files/Sources/files/less/opttbl.c @@ -0,0 +1,631 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * The option table. + */ + +#include "less.h" +#include "option.h" + +/* + * Variables controlled by command line options. + */ +public int quiet; /* Should we suppress the audible bell? */ +public int how_search; /* Where should forward searches start? */ +public int top_scroll; /* Repaint screen from top? + (alternative is scroll from bottom) */ +public int pr_type; /* Type of prompt (short, medium, long) */ +public int bs_mode; /* How to process backspaces */ +public int know_dumb; /* Don't complain about dumb terminals */ +public int quit_at_eof; /* Quit after hitting end of file twice */ +public int quit_if_one_screen; /* Quit if EOF on first screen */ +public int squeeze; /* Squeeze multiple blank lines into one */ +public int tabstop; /* Tab settings */ +public int back_scroll; /* Repaint screen on backwards movement */ +public int forw_scroll; /* Repaint screen on forward movement */ +public int caseless; /* Do "caseless" searches */ +public int linenums; /* Use line numbers */ +public int autobuf; /* Automatically allocate buffers as needed */ +public int bufspace; /* Max buffer space per file (K) */ +public int ctldisp; /* Send control chars to screen untranslated */ +public int force_open; /* Open the file even if not regular file */ +public int swindow; /* Size of scrolling window */ +public int jump_sline; /* Screen line of "jump target" */ +public long jump_sline_fraction = -1; +public long shift_count_fraction = -1; +public int chopline; /* Truncate displayed lines at screen width */ +public int no_init; /* Disable sending ti/te termcap strings */ +public int no_keypad; /* Disable sending ks/ke termcap strings */ +public int twiddle; /* Show tildes after EOF */ +public int show_attn; /* Hilite first unread line */ +public int shift_count; /* Number of positions to shift horizontally */ +public int dashn_numline_count; /* Number of lines override (Unix 2003) */ +public int status_col; /* Display a status column */ +public int use_lessopen; /* Use the LESSOPEN filter */ +public int quit_on_intr; /* Quit on interrupt */ +public int follow_mode; /* F cmd Follows file desc or file name? */ +public int oldbot; /* Old bottom of screen behavior {{REMOVE}} */ +public int opt_use_backslash; /* Use backslash escaping in option parsing */ +#if HILITE_SEARCH +public int hilite_search; /* Highlight matched search patterns? */ +#endif + +public int less_is_more = 0; /* Make compatible with POSIX more */ + +/* + * Long option names. + */ +static struct optname a_optname = { "search-skip-screen", NULL }; +static struct optname b_optname = { "buffers", NULL }; +static struct optname B__optname = { "auto-buffers", NULL }; +static struct optname c_optname = { "clear-screen", NULL }; +static struct optname d_optname = { "dumb", NULL }; +#if MSDOS_COMPILER +static struct optname D__optname = { "color", NULL }; +#endif +static struct optname e_optname = { "quit-at-eof", NULL }; +static struct optname f_optname = { "force", NULL }; +static struct optname F__optname = { "quit-if-one-screen", NULL }; +#if HILITE_SEARCH +static struct optname g_optname = { "hilite-search", NULL }; +#endif +static struct optname h_optname = { "max-back-scroll", NULL }; +static struct optname i_optname = { "ignore-case", NULL }; +static struct optname j_optname = { "jump-target", NULL }; +static struct optname J__optname = { "status-column", NULL }; +#if USERFILE +static struct optname k_optname = { "lesskey-file", NULL }; +#endif +static struct optname K__optname = { "quit-on-intr", NULL }; +static struct optname L__optname = { "no-lessopen", NULL }; +static struct optname m_optname = { "long-prompt", NULL }; +static struct optname n_optname = { "line-numbers", NULL }; +#if LOGFILE +static struct optname o_optname = { "log-file", NULL }; +static struct optname O__optname = { "LOG-FILE", NULL }; +#endif +static struct optname p_optname = { "pattern", NULL }; +static struct optname P__optname = { "prompt", NULL }; +static struct optname q2_optname = { "silent", NULL }; +static struct optname q_optname = { "quiet", &q2_optname }; +static struct optname r_optname = { "raw-control-chars", NULL }; +static struct optname s_optname = { "squeeze-blank-lines", NULL }; +static struct optname S__optname = { "chop-long-lines", NULL }; +#if TAGS +static struct optname t_optname = { "tag", NULL }; +static struct optname T__optname = { "tag-file", NULL }; +#endif +static struct optname u_optname = { "underline-special", NULL }; +static struct optname V__optname = { "version", NULL }; +static struct optname w_optname = { "hilite-unread", NULL }; +static struct optname x_optname = { "tabs", NULL }; +static struct optname X__optname = { "no-init", NULL }; +static struct optname y_optname = { "max-forw-scroll", NULL }; +static struct optname z_optname = { "window", NULL }; +static struct optname quote_optname = { "quotes", NULL }; +static struct optname tilde_optname = { "tilde", NULL }; +static struct optname query_optname = { "help", NULL }; +static struct optname pound_optname = { "shift", NULL }; +static struct optname keypad_optname = { "no-keypad", NULL }; +static struct optname oldbot_optname = { "old-bot", NULL }; +static struct optname follow_optname = { "follow-name", NULL }; +static struct optname use_backslash_optname = { "use-backslash", NULL }; +static struct optname unix2003_n_optname = { "unix2003-n", NULL }; +static struct optname unix2003_p_optname = { "unix2003-p", NULL }; + + +/* + * Table of all options and their semantics. + * + * For BOOL and TRIPLE options, odesc[0], odesc[1], odesc[2] are + * the description of the option when set to 0, 1 or 2, respectively. + * For NUMBER options, odesc[0] is the prompt to use when entering + * a new value, and odesc[1] is the description, which should contain + * one %d which is replaced by the value of the number. + * For STRING options, odesc[0] is the prompt to use when entering + * a new value, and odesc[1], if not NULL, is the set of characters + * that are valid in the string. + */ +static struct loption option[] = +{ + { 'a', &a_optname, + TRIPLE, OPT_ONPLUS, &how_search, NULL, + { + "Search includes displayed screen", + "Search skips displayed screen", + "Search includes all of displayed screen" + } + }, + + { 'b', &b_optname, + NUMBER|INIT_HANDLER, 64, &bufspace, opt_b, + { + "Max buffer space per file (K): ", + "Max buffer space per file: %dK", + NULL + } + }, + { 'B', &B__optname, + BOOL, OPT_ON, &autobuf, NULL, + { + "Don't automatically allocate buffers", + "Automatically allocate buffers when needed", + NULL + } + }, + { 'c', &c_optname, + TRIPLE, OPT_OFF, &top_scroll, NULL, + { + "Repaint by scrolling from bottom of screen", + "Repaint by painting from top of screen", + "Repaint by painting from top of screen" + } + }, + { 'd', &d_optname, + BOOL|NO_TOGGLE, OPT_OFF, &know_dumb, NULL, + { + "Assume intelligent terminal", + "Assume dumb terminal", + NULL + } + }, +#if MSDOS_COMPILER + { 'D', &D__optname, + STRING|REPAINT, 0, NULL, opt_D, + { + "color desc: ", + "Dadknsu0123456789.", + NULL + } + }, +#endif + { 'e', &e_optname, + TRIPLE, OPT_OFF, &quit_at_eof, NULL, + { + "Don't quit at end-of-file", + "Quit at end-of-file", + "Quit immediately at end-of-file" + } + }, + { 'f', &f_optname, + BOOL, OPT_OFF, &force_open, NULL, + { + "Open only regular files", + "Open even non-regular files", + NULL + } + }, + { 'F', &F__optname, + BOOL, OPT_OFF, &quit_if_one_screen, NULL, + { + "Don't quit if end-of-file on first screen", + "Quit if end-of-file on first screen", + NULL + } + }, +#if HILITE_SEARCH + { 'g', &g_optname, + TRIPLE|HL_REPAINT, OPT_ONPLUS, &hilite_search, NULL, + { + "Don't highlight search matches", + "Highlight matches for previous search only", + "Highlight all matches for previous search pattern", + } + }, +#endif + { 'h', &h_optname, + NUMBER, -1, &back_scroll, NULL, + { + "Backwards scroll limit: ", + "Backwards scroll limit is %d lines", + NULL + } + }, + { 'i', &i_optname, + TRIPLE|HL_REPAINT, OPT_OFF, &caseless, opt_i, + { + "Case is significant in searches", + "Ignore case in searches", + "Ignore case in searches and in patterns" + } + }, + { 'j', &j_optname, + STRING, 0, NULL, opt_j, + { + "Target line: ", + "0123456789.-", + NULL + } + }, + { 'J', &J__optname, + BOOL|REPAINT, OPT_OFF, &status_col, NULL, + { + "Don't display a status column", + "Display a status column", + NULL + } + }, +#if USERFILE + { 'k', &k_optname, + STRING|NO_TOGGLE|NO_QUERY, 0, NULL, opt_k, + { NULL, NULL, NULL } + }, +#endif + { 'K', &K__optname, + BOOL, OPT_OFF, &quit_on_intr, NULL, + { + "Interrupt (ctrl-C) returns to prompt", + "Interrupt (ctrl-C) exits less", + NULL + } + }, + { 'L', &L__optname, + BOOL, OPT_ON, &use_lessopen, NULL, + { + "Don't use the LESSOPEN filter", + "Use the LESSOPEN filter", + NULL + } + }, + { 'm', &m_optname, + TRIPLE, OPT_OFF, &pr_type, NULL, + { + "Short prompt", + "Medium prompt", + "Long prompt" + } + }, + { 'n', &n_optname, + TRIPLE|REPAINT, OPT_ON, &linenums, NULL, + { + "Don't use line numbers", + "Use line numbers", + "Constantly display line numbers" + } + }, +#if LOGFILE + { 'o', &o_optname, + STRING, 0, NULL, opt_o, + { "log file: ", NULL, NULL } + }, + { 'O', &O__optname, + STRING, 0, NULL, opt__O, + { "Log file: ", NULL, NULL } + }, +#endif + { 'p', &p_optname, + STRING|NO_TOGGLE|NO_QUERY, 0, NULL, opt_p, + { NULL, NULL, NULL } + }, + { 'P', &P__optname, + STRING, 0, NULL, opt__P, + { "prompt: ", NULL, NULL } + }, + { 'q', &q_optname, + TRIPLE, OPT_OFF, &quiet, NULL, + { + "Ring the bell for errors AND at eof/bof", + "Ring the bell for errors but not at eof/bof", + "Never ring the bell" + } + }, + { 'r', &r_optname, + TRIPLE|REPAINT, OPT_OFF, &ctldisp, NULL, + { + "Display control characters as ^X", + "Display control characters directly", + "Display control characters directly, processing ANSI sequences" + } + }, + { 's', &s_optname, + BOOL|REPAINT, OPT_OFF, &squeeze, NULL, + { + "Display all blank lines", + "Squeeze multiple blank lines", + NULL + } + }, + { 'S', &S__optname, + BOOL|REPAINT, OPT_OFF, &chopline, NULL, + { + "Fold long lines", + "Chop long lines", + NULL + } + }, +#if TAGS + { 't', &t_optname, + STRING|NO_QUERY, 0, NULL, opt_t, + { "tag: ", NULL, NULL } + }, + { 'T', &T__optname, + STRING, 0, NULL, opt__T, + { "tags file: ", NULL, NULL } + }, +#endif + { 'u', &u_optname, + TRIPLE|REPAINT, OPT_OFF, &bs_mode, NULL, + { + "Display underlined text in underline mode", + "Backspaces cause overstrike", + "Print backspace as ^H" + } + }, + { 'V', &V__optname, + NOVAR, 0, NULL, opt__V, + { NULL, NULL, NULL } + }, + { 'w', &w_optname, + TRIPLE|REPAINT, OPT_OFF, &show_attn, NULL, + { + "Don't highlight first unread line", + "Highlight first unread line after forward-screen", + "Highlight first unread line after any forward movement", + } + }, + { 'x', &x_optname, + STRING|REPAINT, 0, NULL, opt_x, + { + "Tab stops: ", + "0123456789,", + NULL + } + }, + { 'X', &X__optname, + BOOL|NO_TOGGLE, OPT_OFF, &no_init, NULL, + { + "Send init/deinit strings to terminal", + "Don't use init/deinit strings", + NULL + } + }, + { 'y', &y_optname, + NUMBER, -1, &forw_scroll, NULL, + { + "Forward scroll limit: ", + "Forward scroll limit is %d lines", + NULL + } + }, + { 'z', &z_optname, + NUMBER, -1, &swindow, NULL, + { + "Scroll window size: ", + "Scroll window size is %d lines", + NULL + } + }, + { '"', "e_optname, + STRING, 0, NULL, opt_quote, + { "quotes: ", NULL, NULL } + }, + { '~', &tilde_optname, + BOOL|REPAINT, OPT_ON, &twiddle, NULL, + { + "Don't show tildes after end of file", + "Show tildes after end of file", + NULL + } + }, + { '?', &query_optname, + NOVAR, 0, NULL, opt_query, + { NULL, NULL, NULL } + }, + { '#', £_optname, + STRING, 0, NULL, opt_shift, + { + "Horizontal shift: ", + "0123456789.", + NULL + } + }, + { OLETTER_NONE, &keypad_optname, + BOOL|NO_TOGGLE, OPT_OFF, &no_keypad, NULL, + { + "Use keypad mode", + "Don't use keypad mode", + NULL + } + }, + { OLETTER_NONE, &oldbot_optname, + BOOL, OPT_OFF, &oldbot, NULL, + { + "Use new bottom of screen behavior", + "Use old bottom of screen behavior", + NULL + } + }, + { OLETTER_NONE, &follow_optname, + BOOL, FOLLOW_DESC, &follow_mode, NULL, + { + "F command follows file descriptor", + "F command follows file name", + NULL + } + }, + { OLETTER_NONE, &use_backslash_optname, + BOOL, OPT_OFF, &opt_use_backslash, NULL, + { + "Use backslash escaping in command line parameters", + "Don't use backslash escaping in command line parameters", + NULL + } + }, + /* The following entries are added to support UNIX 2003 + compatibility. Each is used because the original option + was already defined in "less". + */ + { ']', &unix2003_n_optname, + NUMBER, 0, &dashn_numline_count, NULL, + { /* This entry maps from the -n option */ + NULL, + NULL, + NULL + } + }, + { '}', &unix2003_p_optname, + STRING, 0, NULL, opt_dashp, + { /* This entry maps from the -p option */ + NULL, + NULL, + NULL + } + }, + { '\0', NULL, NOVAR, 0, NULL, NULL, { NULL, NULL, NULL } } +}; + + +/* + * Initialize each option to its default value. + */ + public void +init_option() +{ + register struct loption *o; + char *p; + + p = lgetenv("LESS_IS_MORE"); + if (p != NULL && *p != '\0') + less_is_more = 1; + + for (o = option; o->oletter != '\0'; o++) + { + /* + * Set each variable to its default. + */ + if (o->ovar != NULL) + *(o->ovar) = o->odefault; + if (o->otype & INIT_HANDLER) + (*(o->ofunc))(INIT, (char *) NULL); + } +} + +/* + * Find an option in the option table, given its option letter. + */ + public struct loption * +findopt(c) + int c; +{ + register struct loption *o; + + for (o = option; o->oletter != '\0'; o++) + { + if (o->oletter == c) + return (o); + if ((o->otype & TRIPLE) && ASCII_TO_UPPER(o->oletter) == c) + return (o); + } + return (NULL); +} + +/* + * + */ + static int +is_optchar(c) + char c; +{ + if (ASCII_IS_UPPER(c)) + return 1; + if (ASCII_IS_LOWER(c)) + return 1; + if (c == '-') + return 1; + return 0; +} + +/* + * Find an option in the option table, given its option name. + * p_optname is the (possibly partial) name to look for, and + * is updated to point after the matched name. + * p_oname if non-NULL is set to point to the full option name. + */ + public struct loption * +findopt_name(p_optname, p_oname, p_err) + char **p_optname; + char **p_oname; + int *p_err; +{ + char *optname = *p_optname; + register struct loption *o; + register struct optname *oname; + register int len; + int uppercase; + struct loption *maxo = NULL; + struct optname *maxoname = NULL; + int maxlen = 0; + int ambig = 0; + int exact = 0; + + /* + * Check all options. + */ + for (o = option; o->oletter != '\0'; o++) + { + /* + * Check all names for this option. + */ + for (oname = o->onames; oname != NULL; oname = oname->onext) + { + /* + * Try normal match first (uppercase == 0), + * then, then if it's a TRIPLE option, + * try uppercase match (uppercase == 1). + */ + for (uppercase = 0; uppercase <= 1; uppercase++) + { + len = sprefix(optname, oname->oname, uppercase); + if (len <= 0 || is_optchar(optname[len])) + { + /* + * We didn't use all of the option name. + */ + continue; + } + if (!exact && len == maxlen) + /* + * Already had a partial match, + * and now there's another one that + * matches the same length. + */ + ambig = 1; + else if (len > maxlen) + { + /* + * Found a better match than + * the one we had. + */ + maxo = o; + maxoname = oname; + maxlen = len; + ambig = 0; + exact = (len == (int)strlen(oname->oname)); + } + if (!(o->otype & TRIPLE)) + break; + } + } + } + if (ambig) + { + /* + * Name matched more than one option. + */ + if (p_err != NULL) + *p_err = OPT_AMBIG; + return (NULL); + } + *p_optname = optname + maxlen; + if (p_oname != NULL) + *p_oname = maxoname == NULL ? NULL : maxoname->oname; + return (maxo); +} diff --git a/files/Sources/files/less/os.c b/files/Sources/files/less/os.c new file mode 100644 index 00000000..2f7aa0ac --- /dev/null +++ b/files/Sources/files/less/os.c @@ -0,0 +1,358 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Operating system dependent routines. + * + * Most of the stuff in here is based on Unix, but an attempt + * has been made to make things work on other operating systems. + * This will sometimes result in a loss of functionality, unless + * someone rewrites code specifically for the new operating system. + * + * The makefile provides defines to decide whether various + * Unix features are present. + */ + +#include "less.h" +#include +#include +#if HAVE_TIME_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_VALUES_H +#include +#endif + +/* + * BSD setjmp() saves (and longjmp() restores) the signal mask. + * This costs a system call or two per setjmp(), so if possible we clear the + * signal mask with sigsetmask(), and use _setjmp()/_longjmp() instead. + * On other systems, setjmp() doesn't affect the signal mask and so + * _setjmp() does not exist; we just use setjmp(). + */ +#if HAVE__SETJMP && HAVE_SIGSETMASK +#define SET_JUMP _setjmp +#define LONG_JUMP _longjmp +#else +#define SET_JUMP setjmp +#define LONG_JUMP longjmp +#endif + +public int reading; + +static jmp_buf read_label; + +extern int sigs; + +/* + * Like read() system call, but is deliberately interruptible. + * A call to intread() from a signal handler will interrupt + * any pending iread(). + */ + public int +iread(fd, buf, len) + int fd; + char *buf; + unsigned int len; +{ + register int n; + +start: +#if MSDOS_COMPILER==WIN32C + if (ABORT_SIGS()) + return (READ_INTR); +#else +#if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC + if (kbhit()) + { + int c; + + c = getch(); + if (c == '\003') + return (READ_INTR); + ungetch(c); + } +#endif +#endif + if (SET_JUMP(read_label)) + { + /* + * We jumped here from intread. + */ + reading = 0; +#if HAVE_SIGPROCMASK + { + sigset_t mask; + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, NULL); + } +#else +#if HAVE_SIGSETMASK + sigsetmask(0); +#else +#ifdef _OSK + sigmask(~0); +#endif +#endif +#endif + return (READ_INTR); + } + + flush(); + reading = 1; +#if MSDOS_COMPILER==DJGPPC + if (ios_isatty(fd)) + { + /* + * Don't try reading from a TTY until a character is + * available, because that makes some background programs + * believe DOS is busy in a way that prevents those + * programs from working while "less" waits. + */ + fd_set readfds; + + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + if (select(fd+1, &readfds, 0, 0, 0) == -1) + return (-1); + } +#endif + n = read(fd, buf, len); +#if 1 + /* + * This is a kludge to workaround a problem on some systems + * where terminating a remote tty connection causes read() to + * start returning 0 forever, instead of -1. + */ + { + extern int ignore_eoi; + if (!ignore_eoi) + { + static int consecutive_nulls = 0; + if (n == 0) + consecutive_nulls++; + else + consecutive_nulls = 0; + if (consecutive_nulls > 20) + quit(QUIT_ERROR); + } + } +#endif + reading = 0; + if (n < 0) + { +#if HAVE_ERRNO + /* + * Certain values of errno indicate we should just retry the read. + */ +#if MUST_DEFINE_ERRNO + extern int errno; +#endif +#ifdef EINTR + if (errno == EINTR) + goto start; +#endif +#ifdef EAGAIN + if (errno == EAGAIN) + goto start; +#endif +#endif + return (-1); + } + return (n); +} + +/* + * Interrupt a pending iread(). + */ + public void +intread() +{ + LONG_JUMP(read_label, 1); +} + +/* + * Return the current time. + */ +#if HAVE_TIME + public time_type +get_time() +{ + time_type t; + + time(&t); + return (t); +} +#endif + + +#if !HAVE_STRERROR +/* + * Local version of strerror, if not available from the system. + */ + static char * +strerror(err) + int err; +{ +#if HAVE_SYS_ERRLIST + static char buf[16]; + extern char *sys_errlist[]; + extern int sys_nerr; + + if (err < sys_nerr) + return sys_errlist[err]; + sprintf(buf, "Error %d", err); + return buf; +#else + return ("cannot open"); +#endif +} +#endif + +/* + * errno_message: Return an error message based on the value of "errno". + */ + public char * +errno_message(filename) + char *filename; +{ + register char *p; + register char *m; + int len; +#if HAVE_ERRNO +#if MUST_DEFINE_ERRNO + extern int errno; +#endif + p = strerror(errno); +#else + p = "cannot open"; +#endif + len = (int) (strlen(filename) + strlen(p) + 3); + m = (char *) ecalloc(len, sizeof(char)); + SNPRINTF2(m, len, "%s: %s", filename, p); + return (m); +} + +/* #define HAVE_FLOAT 0 */ + + static POSITION +muldiv(val, num, den) + POSITION val, num, den; +{ +#if HAVE_FLOAT + double v = (((double) val) * num) / den; + return ((POSITION) (v + 0.5)); +#else + POSITION v = ((POSITION) val) * num; + + if (v / num == val) + /* No overflow */ + return (POSITION) (v / den); + else + /* Above calculation overflows; + * use a method that is less precise but won't overflow. */ + return (POSITION) (val / (den / num)); +#endif +} + +/* + * Return the ratio of two POSITIONS, as a percentage. + * {{ Assumes a POSITION is a long int. }} + */ + public int +percentage(num, den) + POSITION num; + POSITION den; +{ + return (int) muldiv(num, (POSITION) 100, den); +} + +/* + * Return the specified percentage of a POSITION. + */ + public POSITION +percent_pos(pos, percent, fraction) + POSITION pos; + int percent; + long fraction; +{ + /* Change percent (parts per 100) to perden (parts per NUM_FRAC_DENOM). */ + POSITION perden = (percent * (NUM_FRAC_DENOM / 100)) + (fraction / 100); + + if (perden == 0) + return (0); + return (POSITION) muldiv(pos, perden, (POSITION) NUM_FRAC_DENOM); +} + +#if !HAVE_STRCHR +/* + * strchr is used by regexp.c. + */ + char * +strchr(s, c) + char *s; + int c; +{ + for ( ; *s != '\0'; s++) + if (*s == c) + return (s); + if (c == '\0') + return (s); + return (NULL); +} +#endif + +#if !HAVE_MEMCPY + VOID_POINTER +memcpy(dst, src, len) + VOID_POINTER dst; + VOID_POINTER src; + int len; +{ + char *dstp = (char *) dst; + char *srcp = (char *) src; + int i; + + for (i = 0; i < len; i++) + dstp[i] = srcp[i]; + return (dst); +} +#endif + +#ifdef _OSK_MWC32 + +/* + * This implements an ANSI-style intercept setup for Microware C 3.2 + */ + public int +os9_signal(type, handler) + int type; + RETSIGTYPE (*handler)(); +{ + intercept(handler); +} + +#include + + int +isatty(f) + int f; +{ + struct sgbuf sgbuf; + + if (_gs_opt(f, &sgbuf) < 0) + return -1; + return (sgbuf.sg_class == 0); +} + +#endif diff --git a/files/Sources/files/less/output.c b/files/Sources/files/less/output.c new file mode 100644 index 00000000..77e6b395 --- /dev/null +++ b/files/Sources/files/less/output.c @@ -0,0 +1,669 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * High level routines dealing with the output to the screen. + */ + +#include "less.h" +#if MSDOS_COMPILER==WIN32C +#include "windows.h" +#endif + +public int errmsgs; /* Count of messages displayed by error() */ +public int need_clr; +public int final_attr; +public int at_prompt; + +extern int sigs; +extern int sc_width; +extern int so_s_width, so_e_width; +extern int screen_trashed; +extern int any_display; +extern int is_tty; +extern int oldbot; + +#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC +extern int ctldisp; +extern int nm_fg_color, nm_bg_color; +extern int bo_fg_color, bo_bg_color; +extern int ul_fg_color, ul_bg_color; +extern int so_fg_color, so_bg_color; +extern int bl_fg_color, bl_bg_color; +extern int sgr_mode; +#endif + +/* + * Display the line which is in the line buffer. + */ + public void +put_line() +{ + register int c; + register int i; + int a; + + if (ABORT_SIGS()) + { + /* + * Don't output if a signal is pending. + */ + screen_trashed = 1; + return; + } + + final_attr = AT_NORMAL; + + for (i = 0; (c = gline(i, &a)) != '\0'; i++) + { + at_switch(a); + final_attr = a; + if (c == '\b') + putbs(); + else + putchr(c); + } + + at_exit(); +} + +static char obuf[OUTBUF_SIZE]; +static char *ob = obuf; + +/* + * Flush buffered output. + * + * If we haven't displayed any file data yet, + * output messages on error output (file descriptor 2), + * otherwise output on standard output (file descriptor 1). + * + * This has the desirable effect of producing all + * error messages on error output if standard output + * is directed to a file. It also does the same if + * we never produce any real output; for example, if + * the input file(s) cannot be opened. If we do + * eventually produce output, code in edit() makes + * sure these messages can be seen before they are + * overwritten or scrolled away. + */ + public void +flush() +{ + register int n; + register int fd; + + n = (int) (ob - obuf); + if (n == 0) + return; + +#if MSDOS_COMPILER==MSOFTC + if (is_tty && any_display) + { + *ob = '\0'; + _outtext(obuf); + ob = obuf; + return; + } +#else +#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC + if (is_tty && any_display) + { + *ob = '\0'; + if (ctldisp != OPT_ONPLUS) + WIN32textout(obuf, ob - obuf); + else + { + /* + * Look for SGR escape sequences, and convert them + * to color commands. Replace bold, underline, + * and italic escapes into colors specified via + * the -D command-line option. + */ + char *anchor, *p, *p_next; + static unsigned char fg, fgi, bg, bgi; + static unsigned char at; + unsigned char f, b; +#if MSDOS_COMPILER==WIN32C + /* Screen colors used by 3x and 4x SGR commands. */ + static unsigned char screen_color[] = { + 0, /* BLACK */ + FOREGROUND_RED, + FOREGROUND_GREEN, + FOREGROUND_RED|FOREGROUND_GREEN, + FOREGROUND_BLUE, + FOREGROUND_BLUE|FOREGROUND_RED, + FOREGROUND_BLUE|FOREGROUND_GREEN, + FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED + }; +#else + static enum COLORS screen_color[] = { + BLACK, RED, GREEN, BROWN, + BLUE, MAGENTA, CYAN, LIGHTGRAY + }; +#endif + + if (fg == 0 && bg == 0) + { + fg = nm_fg_color & 7; + fgi = nm_fg_color & 8; + bg = nm_bg_color & 7; + bgi = nm_bg_color & 8; + } + for (anchor = p_next = obuf; + (p_next = memchr(p_next, ESC, ob - p_next)) != NULL; ) + { + p = p_next; + if (p[1] == '[') /* "ESC-[" sequence */ + { + if (p > anchor) + { + /* + * If some chars seen since + * the last escape sequence, + * write them out to the screen. + */ + WIN32textout(anchor, p-anchor); + anchor = p; + } + p += 2; /* Skip the "ESC-[" */ + if (is_ansi_end(*p)) + { + /* + * Handle null escape sequence + * "ESC[m", which restores + * the normal color. + */ + p++; + anchor = p_next = p; + fg = nm_fg_color & 7; + fgi = nm_fg_color & 8; + bg = nm_bg_color & 7; + bgi = nm_bg_color & 8; + at = 0; + WIN32setcolors(nm_fg_color, nm_bg_color); + continue; + } + p_next = p; + at &= ~32; + + /* + * Select foreground/background colors + * based on the escape sequence. + */ + while (!is_ansi_end(*p)) + { + char *q; + long code = strtol(p, &q, 10); + + if (*q == '\0') + { + /* + * Incomplete sequence. + * Leave it unprocessed + * in the buffer. + */ + int slop = (int) (q - anchor); + /* {{ strcpy args overlap! }} */ + strcpy(obuf, anchor); + ob = &obuf[slop]; + return; + } + + if (q == p || + code > 49 || code < 0 || + (!is_ansi_end(*q) && *q != ';')) + { + p_next = q; + break; + } + if (*q == ';') + { + q++; + at |= 32; + } + + switch (code) + { + default: + /* case 0: all attrs off */ + fg = nm_fg_color & 7; + bg = nm_bg_color & 7; + at &= 32; + /* + * \e[0m use normal + * intensities, but + * \e[0;...m resets them + */ + if (at & 32) + { + fgi = 0; + bgi = 0; + } else + { + fgi = nm_fg_color & 8; + bgi = nm_bg_color & 8; + } + break; + case 1: /* bold on */ + fgi = 8; + at |= 1; + break; + case 3: /* italic on */ + case 7: /* inverse on */ + at |= 2; + break; + case 4: /* underline on */ + bgi = 8; + at |= 4; + break; + case 5: /* slow blink on */ + case 6: /* fast blink on */ + bgi = 8; + at |= 8; + break; + case 8: /* concealed on */ + at |= 16; + break; + case 22: /* bold off */ + fgi = 0; + at &= ~1; + break; + case 23: /* italic off */ + case 27: /* inverse off */ + at &= ~2; + break; + case 24: /* underline off */ + bgi = 0; + at &= ~4; + break; + case 28: /* concealed off */ + at &= ~16; + break; + case 30: case 31: case 32: + case 33: case 34: case 35: + case 36: case 37: + fg = screen_color[code - 30]; + at |= 32; + break; + case 39: /* default fg */ + fg = nm_fg_color & 7; + at |= 32; + break; + case 40: case 41: case 42: + case 43: case 44: case 45: + case 46: case 47: + bg = screen_color[code - 40]; + at |= 32; + break; + case 49: /* default bg */ + bg = nm_bg_color & 7; + at |= 32; + break; + } + p = q; + } + if (!is_ansi_end(*p) || p == p_next) + break; + /* + * In SGR mode, the ANSI sequence is + * always honored; otherwise if an attr + * is used by itself ("\e[1m" versus + * "\e[1;33m", for example), set the + * color assigned to that attribute. + */ + if (sgr_mode || (at & 32)) + { + if (at & 2) + { + f = bg | bgi; + b = fg | fgi; + } else + { + f = fg | fgi; + b = bg | bgi; + } + } else + { + if (at & 1) + { + f = bo_fg_color; + b = bo_bg_color; + } else if (at & 2) + { + f = so_fg_color; + b = so_bg_color; + } else if (at & 4) + { + f = ul_fg_color; + b = ul_bg_color; + } else if (at & 8) + { + f = bl_fg_color; + b = bl_bg_color; + } else + { + f = nm_fg_color; + b = nm_bg_color; + } + } + if (at & 16) + f = b ^ 8; + f &= 0xf; + b &= 0xf; + WIN32setcolors(f, b); + p_next = anchor = p + 1; + } else + p_next++; + } + + /* Output what's left in the buffer. */ + WIN32textout(anchor, ob - anchor); + } + ob = obuf; + return; + } +#endif +#endif + fd = (any_display) ? 1 : 2; + if (write(fd, obuf, n) != n) + screen_trashed = 1; + ob = obuf; +} + +/* + * Output a character. + */ + public int +putchr(c) + int c; +{ +#if 0 /* fake UTF-8 output for testing */ + extern int utf_mode; + if (utf_mode) + { + static char ubuf[MAX_UTF_CHAR_LEN]; + static int ubuf_len = 0; + static int ubuf_index = 0; + if (ubuf_len == 0) + { + ubuf_len = utf_len(c); + ubuf_index = 0; + } + ubuf[ubuf_index++] = c; + if (ubuf_index < ubuf_len) + return c; + c = get_wchar(ubuf) & 0xFF; + ubuf_len = 0; + } +#endif + if (need_clr) + { + need_clr = 0; + clear_bot(); + } +#if MSDOS_COMPILER + if (c == '\n' && is_tty) + { + /* remove_top(1); */ + putchr('\r'); + } +#else +#ifdef _OSK + if (c == '\n' && is_tty) /* In OS-9, '\n' == 0x0D */ + putchr(0x0A); +#endif +#endif + /* + * Some versions of flush() write to *ob, so we must flush + * when we are still one char from the end of obuf. + */ + if (ob >= &obuf[sizeof(obuf)-1]) + flush(); + *ob++ = c; + at_prompt = 0; + return (c); +} + +/* + * Output a string. + */ + public void +putstr(s) + register char *s; +{ + while (*s != '\0') + putchr(*s++); +} + + +/* + * Convert an integral type to a string. + */ +#define TYPE_TO_A_FUNC(funcname, type) \ +void funcname(num, buf) \ + type num; \ + char *buf; \ +{ \ + int neg = (num < 0); \ + char tbuf[INT_STRLEN_BOUND(num)+2]; \ + register char *s = tbuf + sizeof(tbuf); \ + if (neg) num = -num; \ + *--s = '\0'; \ + do { \ + *--s = (num % 10) + '0'; \ + } while ((num /= 10) != 0); \ + if (neg) *--s = '-'; \ + strcpy(buf, s); \ +} + +TYPE_TO_A_FUNC(postoa, POSITION) +TYPE_TO_A_FUNC(linenumtoa, LINENUM) +TYPE_TO_A_FUNC(inttoa, int) + +/* + * Output an integer in a given radix. + */ + static int +iprint_int(num) + int num; +{ + char buf[INT_STRLEN_BOUND(num)]; + + inttoa(num, buf); + putstr(buf); + return ((int) strlen(buf)); +} + +/* + * Output a line number in a given radix. + */ + static int +iprint_linenum(num) + LINENUM num; +{ + char buf[INT_STRLEN_BOUND(num)]; + + linenumtoa(num, buf); + putstr(buf); + return ((int) strlen(buf)); +} + +/* + * This function implements printf-like functionality + * using a more portable argument list mechanism than printf's. + */ + static int +less_printf(fmt, parg) + register char *fmt; + PARG *parg; +{ + register char *s; + register int col; + + col = 0; + while (*fmt != '\0') + { + if (*fmt != '%') + { + putchr(*fmt++); + col++; + } else + { + ++fmt; + switch (*fmt++) + { + case 's': + s = parg->p_string; + parg++; + while (*s != '\0') + { + putchr(*s++); + col++; + } + break; + case 'd': + col += iprint_int(parg->p_int); + parg++; + break; + case 'n': + col += iprint_linenum(parg->p_linenum); + parg++; + break; + } + } + } + return (col); +} + +/* + * Get a RETURN. + * If some other non-trivial char is pressed, unget it, so it will + * become the next command. + */ + public void +get_return() +{ + int c; + +#if ONLY_RETURN + while ((c = getchr()) != '\n' && c != '\r') + bell(); +#else + c = getchr(); + if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR) + ungetcc(c); +#endif +} + +/* + * Output a message in the lower left corner of the screen + * and wait for carriage return. + */ + public void +error(fmt, parg) + char *fmt; + PARG *parg; +{ + int col = 0; + static char return_to_continue[] = " (press RETURN)"; + + errmsgs++; + + if (any_display && is_tty) + { + if (!oldbot) + squish_check(); + at_exit(); + clear_bot(); + at_enter(AT_STANDOUT); + col += so_s_width; + } + + col += less_printf(fmt, parg); + + if (!(any_display && is_tty)) + { + putchr('\n'); + return; + } + + putstr(return_to_continue); + at_exit(); + col += sizeof(return_to_continue) + so_e_width; + + get_return(); + lower_left(); + clear_eol(); + + if (col >= sc_width) + /* + * Printing the message has probably scrolled the screen. + * {{ Unless the terminal doesn't have auto margins, + * in which case we just hammered on the right margin. }} + */ + screen_trashed = 1; + + flush(); +} + +static char intr_to_abort[] = "... (interrupt to abort)"; + +/* + * Output a message in the lower left corner of the screen + * and don't wait for carriage return. + * Usually used to warn that we are beginning a potentially + * time-consuming operation. + */ + public void +ierror(fmt, parg) + char *fmt; + PARG *parg; +{ + at_exit(); + clear_bot(); + at_enter(AT_STANDOUT); + (void) less_printf(fmt, parg); + putstr(intr_to_abort); + at_exit(); + flush(); + need_clr = 1; +} + +/* + * Output a message in the lower left corner of the screen + * and return a single-character response. + */ + public int +query(fmt, parg) + char *fmt; + PARG *parg; +{ + register int c; + int col = 0; + + if (any_display && is_tty) + clear_bot(); + + (void) less_printf(fmt, parg); + c = getchr(); + + if (!(any_display && is_tty)) + { + putchr('\n'); + return (c); + } + + lower_left(); + if (col >= sc_width) + screen_trashed = 1; + flush(); + + return (c); +} diff --git a/files/Sources/files/less/pattern.c b/files/Sources/files/less/pattern.c new file mode 100644 index 00000000..ddd6ec42 --- /dev/null +++ b/files/Sources/files/less/pattern.c @@ -0,0 +1,405 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + +/* + * Routines to do pattern matching. + */ + +#include "less.h" +extern int less_is_more; +#include "pattern.h" + +extern int caseless; + +/* + * Compile a search pattern, for future use by match_pattern. + */ + static int +compile_pattern2(pattern, search_type, comp_pattern, show_error) + char *pattern; + int search_type; + void **comp_pattern; + int show_error; +{ + if (search_type & SRCH_NO_REGEX) + return (0); + { +#if HAVE_GNU_REGEX + struct re_pattern_buffer *comp = (struct re_pattern_buffer *) + ecalloc(1, sizeof(struct re_pattern_buffer)); + struct re_pattern_buffer **pcomp = + (struct re_pattern_buffer **) comp_pattern; + re_set_syntax(RE_SYNTAX_POSIX_EXTENDED); + if (re_compile_pattern(pattern, strlen(pattern), comp)) + { + free(comp); + if (show_error) + error("Invalid pattern", NULL_PARG); + return (-1); + } + if (*pcomp != NULL) + regfree(*pcomp); + *pcomp = comp; +#endif +#if HAVE_POSIX_REGCOMP + regex_t *comp = (regex_t *) ecalloc(1, sizeof(regex_t)); + regex_t **pcomp = (regex_t **) comp_pattern; + if (regcomp(comp, pattern, REGCOMP_FLAG)) + { + free(comp); + if (show_error) + error("Invalid pattern", NULL_PARG); + return (-1); + } + if (*pcomp != NULL) + regfree(*pcomp); + *pcomp = comp; +#endif +#if HAVE_PCRE + pcre *comp; + pcre **pcomp = (pcre **) comp_pattern; + constant char *errstring; + int erroffset; + PARG parg; + comp = pcre_compile(pattern, 0, + &errstring, &erroffset, NULL); + if (comp == NULL) + { + parg.p_string = (char *) errstring; + if (show_error) + error("%s", &parg); + return (-1); + } + *pcomp = comp; +#endif +#if HAVE_RE_COMP + PARG parg; + int *pcomp = (int *) comp_pattern; + if ((parg.p_string = re_comp(pattern)) != NULL) + { + if (show_error) + error("%s", &parg); + return (-1); + } + *pcomp = 1; +#endif +#if HAVE_REGCMP + char *comp; + char **pcomp = (char **) comp_pattern; + if ((comp = regcmp(pattern, 0)) == NULL) + { + if (show_error) + error("Invalid pattern", NULL_PARG); + return (-1); + } + if (pcomp != NULL) + free(*pcomp); + *pcomp = comp; +#endif +#if HAVE_V8_REGCOMP + struct regexp *comp; + struct regexp **pcomp = (struct regexp **) comp_pattern; + reg_show_error = show_error; + comp = regcomp(pattern); + reg_show_error = 1; + if (comp == NULL) + { + /* + * regcomp has already printed an error message + * via regerror(). + */ + return (-1); + } + if (*pcomp != NULL) + free(*pcomp); + *pcomp = comp; +#endif + } + return (0); +} + +/* + * Like compile_pattern2, but convert the pattern to lowercase if necessary. + */ + public int +compile_pattern(pattern, search_type, comp_pattern) + char *pattern; + int search_type; + void **comp_pattern; +{ + char *cvt_pattern; + int result; + + if (caseless != OPT_ONPLUS) + cvt_pattern = pattern; + else + { + cvt_pattern = (char*) ecalloc(1, cvt_length(strlen(pattern), CVT_TO_LC)); + cvt_text(cvt_pattern, pattern, (int *)NULL, (int *)NULL, CVT_TO_LC); + } + result = compile_pattern2(cvt_pattern, search_type, comp_pattern, 1); + if (cvt_pattern != pattern) + free(cvt_pattern); + return (result); +} + +/* + * Forget that we have a compiled pattern. + */ + public void +uncompile_pattern(pattern) + void **pattern; +{ +#if HAVE_GNU_REGEX + struct re_pattern_buffer **pcomp = (struct re_pattern_buffer **) pattern; + if (*pcomp != NULL) + regfree(*pcomp); + *pcomp = NULL; +#endif +#if HAVE_POSIX_REGCOMP + regex_t **pcomp = (regex_t **) pattern; + if (*pcomp != NULL) + regfree(*pcomp); + *pcomp = NULL; +#endif +#if HAVE_PCRE + pcre **pcomp = (pcre **) pattern; + if (*pcomp != NULL) + pcre_free(*pcomp); + *pcomp = NULL; +#endif +#if HAVE_RE_COMP + int *pcomp = (int *) pattern; + *pcomp = 0; +#endif +#if HAVE_REGCMP + char **pcomp = (char **) pattern; + if (*pcomp != NULL) + free(*pcomp); + *pcomp = NULL; +#endif +#if HAVE_V8_REGCOMP + struct regexp **pcomp = (struct regexp **) pattern; + if (*pcomp != NULL) + free(*pcomp); + *pcomp = NULL; +#endif +} + +/* + * Can a pattern be successfully compiled? + */ + public int +valid_pattern(pattern) + char *pattern; +{ + void *comp_pattern; + int result; + + CLEAR_PATTERN(comp_pattern); + result = compile_pattern2(pattern, 0, &comp_pattern, 0); + if (result != 0) + return (0); + uncompile_pattern(&comp_pattern); + return (1); +} + +/* + * Is a compiled pattern null? + */ + public int +is_null_pattern(pattern) + void *pattern; +{ +#if HAVE_GNU_REGEX + return (pattern == NULL); +#endif +#if HAVE_POSIX_REGCOMP + return (pattern == NULL); +#endif +#if HAVE_PCRE + return (pattern == NULL); +#endif +#if HAVE_RE_COMP + return (pattern == 0); +#endif +#if HAVE_REGCMP + return (pattern == NULL); +#endif +#if HAVE_V8_REGCOMP + return (pattern == NULL); +#endif +#if NO_REGEX + return (pattern == NULL); +#endif +} + +/* + * Simple pattern matching function. + * It supports no metacharacters like *, etc. + */ + static int +match(pattern, pattern_len, buf, buf_len, pfound, pend) + char *pattern; + int pattern_len; + char *buf; + int buf_len; + char **pfound, **pend; +{ + register char *pp, *lp; + register char *pattern_end = pattern + pattern_len; + register char *buf_end = buf + buf_len; + + for ( ; buf < buf_end; buf++) + { + for (pp = pattern, lp = buf; ; pp++, lp++) + { + char cp = *pp; + char cl = *lp; + if (caseless == OPT_ONPLUS && ASCII_IS_UPPER(cp)) + cp = ASCII_TO_LOWER(cp); + if (cp != cl) + break; + if (pp == pattern_end || lp == buf_end) + break; + } + if (pp == pattern_end) + { + if (pfound != NULL) + *pfound = buf; + if (pend != NULL) + *pend = lp; + return (1); + } + } + return (0); +} + +/* + * Perform a pattern match with the previously compiled pattern. + * Set sp and ep to the start and end of the matched string. + */ + public int +match_pattern(pattern, tpattern, line, line_len, sp, ep, notbol, search_type) + void *pattern; + char *tpattern; + char *line; + int line_len; + char **sp; + char **ep; + int notbol; + int search_type; +{ + int matched; +#if HAVE_GNU_REGEX + struct re_pattern_buffer *spattern = (struct re_pattern_buffer *) pattern; +#endif +#if HAVE_POSIX_REGCOMP + regex_t *spattern = (regex_t *) pattern; +#endif +#if HAVE_PCRE + pcre *spattern = (pcre *) pattern; +#endif +#if HAVE_RE_COMP + int spattern = (int) pattern; +#endif +#if HAVE_REGCMP + char *spattern = (char *) pattern; +#endif +#if HAVE_V8_REGCOMP + struct regexp *spattern = (struct regexp *) pattern; +#endif + + *sp = *ep = NULL; +#if NO_REGEX + search_type |= SRCH_NO_REGEX; +#endif + if (search_type & SRCH_NO_REGEX) + matched = match(tpattern, strlen(tpattern), line, line_len, sp, ep); + else + { +#if HAVE_GNU_REGEX + { + struct re_registers search_regs; + spattern->not_bol = notbol; + spattern->regs_allocated = REGS_UNALLOCATED; + matched = re_search(spattern, line, line_len, 0, line_len, &search_regs) >= 0; + if (matched) + { + *sp = line + search_regs.start[0]; + *ep = line + search_regs.end[0]; + } + } +#endif +#if HAVE_POSIX_REGCOMP + { + regmatch_t rm; + int flags = (notbol) ? REG_NOTBOL : 0; +#ifdef REG_STARTEND + flags |= REG_STARTEND; + rm.rm_so = 0; + rm.rm_eo = line_len; +#endif + matched = !regexec(spattern, line, 1, &rm, flags); + if (matched) + { +#ifndef __WATCOMC__ + *sp = line + rm.rm_so; + *ep = line + rm.rm_eo; +#else + *sp = rm.rm_sp; + *ep = rm.rm_ep; +#endif + } + } +#endif +#if HAVE_PCRE + { + int flags = (notbol) ? PCRE_NOTBOL : 0; + int ovector[3]; + matched = pcre_exec(spattern, NULL, line, line_len, + 0, flags, ovector, 3) >= 0; + if (matched) + { + *sp = line + ovector[0]; + *ep = line + ovector[1]; + } + } +#endif +#if HAVE_RE_COMP + matched = (re_exec(line) == 1); + /* + * re_exec doesn't seem to provide a way to get the matched string. + */ + *sp = *ep = NULL; +#endif +#if HAVE_REGCMP + *ep = regex(spattern, line); + matched = (*ep != NULL); + if (matched) + *sp = __loc1; +#endif +#if HAVE_V8_REGCOMP +#if HAVE_REGEXEC2 + matched = regexec2(spattern, line, notbol); +#else + matched = regexec(spattern, line); +#endif + if (matched) + { + *sp = spattern->startp[0]; + *ep = spattern->endp[0]; + } +#endif + } + matched = (!(search_type & SRCH_NO_MATCH) && matched) || + ((search_type & SRCH_NO_MATCH) && !matched); + return (matched); +} + diff --git a/files/Sources/files/less/pattern.h b/files/Sources/files/less/pattern.h new file mode 100644 index 00000000..df8c04d8 --- /dev/null +++ b/files/Sources/files/less/pattern.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + +#if HAVE_GNU_REGEX +#define __USE_GNU 1 +#include +#define DEFINE_PATTERN(name) struct re_pattern_buffer *name +#define CLEAR_PATTERN(name) name = NULL +#endif + +#if HAVE_POSIX_REGCOMP +#include +#ifdef REG_EXTENDED +#define REGCOMP_FLAG (less_is_more ? 0 : REG_EXTENDED) +#else +#define REGCOMP_FLAG 0 +#endif +#define DEFINE_PATTERN(name) regex_t *name +#define CLEAR_PATTERN(name) name = NULL +#endif + +#if HAVE_PCRE +#include +#define DEFINE_PATTERN(name) pcre *name +#define CLEAR_PATTERN(name) name = NULL +#endif + +#if HAVE_RE_COMP +char *re_comp(); +int re_exec(); +#define DEFINE_PATTERN(name) int name +#define CLEAR_PATTERN(name) name = 0 +#endif + +#if HAVE_REGCMP +char *regcmp(); +char *regex(); +extern char *__loc1; +#define DEFINE_PATTERN(name) char *name +#define CLEAR_PATTERN(name) name = NULL +#endif + +#if HAVE_V8_REGCOMP +#include "regexp.h" +extern int reg_show_error; +#define DEFINE_PATTERN(name) struct regexp *name +#define CLEAR_PATTERN(name) name = NULL +#endif + +#if NO_REGEX +#define DEFINE_PATTERN(name) +#define CLEAR_PATTERN(name) +#endif diff --git a/files/Sources/files/less/pckeys.h b/files/Sources/files/less/pckeys.h new file mode 100644 index 00000000..61493ecb --- /dev/null +++ b/files/Sources/files/less/pckeys.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Definitions of keys on the PC. + * Special (non-ASCII) keys on the PC send a two-byte sequence, + * where the first byte is 0 and the second is as defined below. + */ +#define PCK_SHIFT_TAB '\017' +#define PCK_ALT_E '\022' +#define PCK_CAPS_LOCK '\072' +#define PCK_F1 '\073' +#define PCK_NUM_LOCK '\105' +#define PCK_HOME '\107' +#define PCK_UP '\110' +#define PCK_PAGEUP '\111' +#define PCK_LEFT '\113' +#define PCK_RIGHT '\115' +#define PCK_END '\117' +#define PCK_DOWN '\120' +#define PCK_PAGEDOWN '\121' +#define PCK_INSERT '\122' +#define PCK_DELETE '\123' +#define PCK_CTL_LEFT '\163' +#define PCK_CTL_RIGHT '\164' +#define PCK_CTL_DELETE '\223' diff --git a/files/Sources/files/less/position.c b/files/Sources/files/less/position.c new file mode 100644 index 00000000..813d85a7 --- /dev/null +++ b/files/Sources/files/less/position.c @@ -0,0 +1,231 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Routines dealing with the "position" table. + * This is a table which tells the position (in the input file) of the + * first char on each currently displayed line. + * + * {{ The position table is scrolled by moving all the entries. + * Would be better to have a circular table + * and just change a couple of pointers. }} + */ + +#include "less.h" +#include "position.h" + +static POSITION *table = NULL; /* The position table */ +static int table_size; + +extern int sc_width, sc_height; + +/* + * Return the starting file position of a line displayed on the screen. + * The line may be specified as a line number relative to the top + * of the screen, but is usually one of these special cases: + * the top (first) line on the screen + * the second line on the screen + * the bottom line on the screen + * the line after the bottom line on the screen + */ + public POSITION +position(where) + int where; +{ + switch (where) + { + case BOTTOM: + where = sc_height - 2; + break; + case BOTTOM_PLUS_ONE: + where = sc_height - 1; + break; + case MIDDLE: + where = (sc_height - 1) / 2; + } + return (table[where]); +} + +/* + * Add a new file position to the bottom of the position table. + */ + public void +add_forw_pos(pos) + POSITION pos; +{ + register int i; + + /* + * Scroll the position table up. + */ + for (i = 1; i < sc_height; i++) + table[i-1] = table[i]; + table[sc_height - 1] = pos; +} + +/* + * Add a new file position to the top of the position table. + */ + public void +add_back_pos(pos) + POSITION pos; +{ + register int i; + + /* + * Scroll the position table down. + */ + for (i = sc_height - 1; i > 0; i--) + table[i] = table[i-1]; + table[0] = pos; +} + +/* + * Initialize the position table, done whenever we clear the screen. + */ + public void +pos_clear() +{ + register int i; + + for (i = 0; i < sc_height; i++) + table[i] = NULL_POSITION; +} + +/* + * Allocate or reallocate the position table. + */ + public void +pos_init() +{ + struct scrpos scrpos; + + if (sc_height <= table_size) + return; + /* + * If we already have a table, remember the first line in it + * before we free it, so we can copy that line to the new table. + */ + if (table != NULL) + { + get_scrpos(&scrpos); + free((char*)table); + } else + scrpos.pos = NULL_POSITION; + table = (POSITION *) ecalloc(sc_height, sizeof(POSITION)); + table_size = sc_height; + pos_clear(); + if (scrpos.pos != NULL_POSITION) + table[scrpos.ln-1] = scrpos.pos; +} + +/* + * See if the byte at a specified position is currently on the screen. + * Check the position table to see if the position falls within its range. + * Return the position table entry if found, -1 if not. + */ + public int +onscreen(pos) + POSITION pos; +{ + register int i; + + if (pos < table[0]) + return (-1); + for (i = 1; i < sc_height; i++) + if (pos < table[i]) + return (i-1); + return (-1); +} + +/* + * See if the entire screen is empty. + */ + public int +empty_screen() +{ + return (empty_lines(0, sc_height-1)); +} + + public int +empty_lines(s, e) + int s; + int e; +{ + register int i; + + for (i = s; i <= e; i++) + if (table[i] != NULL_POSITION && table[i] != 0) + return (0); + return (1); +} + +/* + * Get the current screen position. + * The screen position consists of both a file position and + * a screen line number where the file position is placed on the screen. + * Normally the screen line number is 0, but if we are positioned + * such that the top few lines are empty, we may have to set + * the screen line to a number > 0. + */ + public void +get_scrpos(scrpos) + struct scrpos *scrpos; +{ + register int i; + + /* + * Find the first line on the screen which has something on it, + * and return the screen line number and the file position. + */ + for (i = 0; i < table_size; i++) + if (table[i] != NULL_POSITION) + { + scrpos->ln = i+1; + scrpos->pos = table[i]; + return; + } + /* + * The screen is empty. + */ + scrpos->pos = NULL_POSITION; +} + +/* + * Adjust a screen line number to be a simple positive integer + * in the range { 0 .. sc_height-2 }. + * (The bottom line, sc_height-1, is reserved for prompts, etc.) + * The given "sline" may be in the range { 1 .. sc_height-1 } + * to refer to lines relative to the top of the screen (starting from 1), + * or it may be in { -1 .. -(sc_height-1) } to refer to lines + * relative to the bottom of the screen. + */ + public int +adjsline(sline) + int sline; +{ + /* + * Negative screen line number means + * relative to the bottom of the screen. + */ + if (sline < 0) + sline += sc_height; + /* + * Can't be less than 1 or greater than sc_height-1. + */ + if (sline <= 0) + sline = 1; + if (sline >= sc_height) + sline = sc_height - 1; + /* + * Return zero-based line number, not one-based. + */ + return (sline-1); +} diff --git a/files/Sources/files/less/position.h b/files/Sources/files/less/position.h new file mode 100644 index 00000000..a0b20c5b --- /dev/null +++ b/files/Sources/files/less/position.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Include file for interfacing to position.c modules. + */ +#define TOP (0) +#define TOP_PLUS_ONE (1) +#define BOTTOM (-1) +#define BOTTOM_PLUS_ONE (-2) +#define MIDDLE (-3) diff --git a/files/Sources/files/less/prompt.c b/files/Sources/files/less/prompt.c new file mode 100644 index 00000000..bd2213ca --- /dev/null +++ b/files/Sources/files/less/prompt.c @@ -0,0 +1,586 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Prompting and other messages. + * There are three flavors of prompts, SHORT, MEDIUM and LONG, + * selected by the -m/-M options. + * There is also the "equals message", printed by the = command. + * A prompt is a message composed of various pieces, such as the + * name of the file being viewed, the percentage into the file, etc. + */ + +#include "less.h" +#include "position.h" + +extern int pr_type; +extern int new_file; +extern int sc_width; +extern int so_s_width, so_e_width; +extern int linenums; +extern int hshift; +extern int sc_height; +extern int jump_sline; +extern int less_is_more; +extern IFILE curr_ifile; +#if EDITOR +extern char *editor; +extern char *editproto; +#endif + +/* + * Prototypes for the three flavors of prompts. + * These strings are expanded by pr_expand(). + */ +static constant char s_proto[] = + "?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x..%t"; +static constant char m_proto[] = + "?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t"; +static constant char M_proto[] = + "?f%f .?n?m(%T %i of %m) ..?ltlines %lt-%lb?L/%L. :byte %bB?s/%s. .?e(END) ?x- Next\\: %x.:?pB%pB\\%..%t"; +static constant char e_proto[] = + "?f%f .?m(%T %i of %m) .?ltlines %lt-%lb?L/%L. .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t"; +static constant char h_proto[] = + "HELP -- ?eEND -- Press g to see it again:Press RETURN for more., or q when done"; +static constant char w_proto[] = + "Waiting for data"; +static constant char more_proto[] = + "--More--(?eEND ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t)"; + +public char *prproto[3]; +public char constant *eqproto = e_proto; +public char constant *hproto = h_proto; +public char constant *wproto = w_proto; + +static char message[PROMPT_SIZE]; +static char *mp; + +/* + * Initialize the prompt prototype strings. + */ + public void +init_prompt() +{ + prproto[0] = save(s_proto); + prproto[1] = save(less_is_more ? more_proto : m_proto); + prproto[2] = save(M_proto); + eqproto = save(e_proto); + hproto = save(h_proto); + wproto = save(w_proto); +} + +/* + * Append a string to the end of the message. + */ + static void +ap_str(s) + char *s; +{ + int len; + + len = (int) strlen(s); + if (mp + len >= message + PROMPT_SIZE) + len = (int) (message + PROMPT_SIZE - mp - 1); + strncpy(mp, s, len); + mp += len; + *mp = '\0'; +} + +/* + * Append a character to the end of the message. + */ + static void +ap_char(c) + char c; +{ + char buf[2]; + + buf[0] = c; + buf[1] = '\0'; + ap_str(buf); +} + +/* + * Append a POSITION (as a decimal integer) to the end of the message. + */ + static void +ap_pos(pos) + POSITION pos; +{ + char buf[INT_STRLEN_BOUND(pos) + 2]; + + postoa(pos, buf); + ap_str(buf); +} + +/* + * Append a line number to the end of the message. + */ + static void +ap_linenum(linenum) + LINENUM linenum; +{ + char buf[INT_STRLEN_BOUND(linenum) + 2]; + + linenumtoa(linenum, buf); + ap_str(buf); +} + +/* + * Append an integer to the end of the message. + */ + static void +ap_int(num) + int num; +{ + char buf[INT_STRLEN_BOUND(num) + 2]; + + inttoa(num, buf); + ap_str(buf); +} + +/* + * Append a question mark to the end of the message. + */ + static void +ap_quest() +{ + ap_str("?"); +} + +/* + * Return the "current" byte offset in the file. + */ + static POSITION +curr_byte(where) + int where; +{ + POSITION pos; + + pos = position(where); + while (pos == NULL_POSITION && where >= 0 && where < sc_height-1) + pos = position(++where); + if (pos == NULL_POSITION) + pos = ch_length(); + return (pos); +} + +/* + * Return the value of a prototype conditional. + * A prototype string may include conditionals which consist of a + * question mark followed by a single letter. + * Here we decode that letter and return the appropriate boolean value. + */ + static int +cond(c, where) + char c; + int where; +{ + POSITION len; + + switch (c) + { + case 'a': /* Anything in the message yet? */ + return (mp > message); + case 'b': /* Current byte offset known? */ + return (curr_byte(where) != NULL_POSITION); + case 'c': + return (hshift != 0); + case 'e': /* At end of file? */ + return (eof_displayed()); + case 'f': /* Filename known? */ + return (strcmp(get_filename(curr_ifile), "-") != 0); + case 'l': /* Line number known? */ + case 'd': /* Same as l */ + return (linenums); + case 'L': /* Final line number known? */ + case 'D': /* Final page number known? */ + return (linenums && ch_length() != NULL_POSITION); + case 'm': /* More than one file? */ +#if TAGS + return (ntags() ? (ntags() > 1) : (nifile() > 1)); +#else + return (nifile() > 1); +#endif + case 'n': /* First prompt in a new file? */ +#if TAGS + return (ntags() ? 1 : new_file); +#else + return (new_file); +#endif + case 'p': /* Percent into file (bytes) known? */ + return (curr_byte(where) != NULL_POSITION && + ch_length() > 0); + case 'P': /* Percent into file (lines) known? */ + return (currline(where) != 0 && + (len = ch_length()) > 0 && + find_linenum(len) != 0); + case 's': /* Size of file known? */ + case 'B': + return (ch_length() != NULL_POSITION); + case 'x': /* Is there a "next" file? */ +#if TAGS + if (ntags()) + return (0); +#endif + return (next_ifile(curr_ifile) != NULL_IFILE); + } + return (0); +} + +/* + * Decode a "percent" prototype character. + * A prototype string may include various "percent" escapes; + * that is, a percent sign followed by a single letter. + * Here we decode that letter and take the appropriate action, + * usually by appending something to the message being built. + */ + static void +protochar(c, where, iseditproto) + int c; + int where; + int iseditproto; +{ + POSITION pos; + POSITION len; + int n; + LINENUM linenum; + LINENUM last_linenum; + IFILE h; + +#undef PAGE_NUM +#define PAGE_NUM(linenum) ((((linenum) - 1) / (sc_height - 1)) + 1) + + switch (c) + { + case 'b': /* Current byte offset */ + pos = curr_byte(where); + if (pos != NULL_POSITION) + ap_pos(pos); + else + ap_quest(); + break; + case 'c': + ap_int(hshift); + break; + case 'd': /* Current page number */ + linenum = currline(where); + if (linenum > 0 && sc_height > 1) + ap_linenum(PAGE_NUM(linenum)); + else + ap_quest(); + break; + case 'D': /* Final page number */ + /* Find the page number of the last byte in the file (len-1). */ + len = ch_length(); + if (len == NULL_POSITION) + ap_quest(); + else if (len == 0) + /* An empty file has no pages. */ + ap_linenum(0); + else + { + linenum = find_linenum(len - 1); + if (linenum <= 0) + ap_quest(); + else + ap_linenum(PAGE_NUM(linenum)); + } + break; +#if EDITOR + case 'E': /* Editor name */ + ap_str(editor); + break; +#endif + case 'f': /* File name */ + ap_str(get_filename(curr_ifile)); + break; + case 'F': /* Last component of file name */ + ap_str(last_component(get_filename(curr_ifile))); + break; + case 'i': /* Index into list of files */ +#if TAGS + if (ntags()) + ap_int(curr_tag()); + else +#endif + ap_int(get_index(curr_ifile)); + break; + case 'l': /* Current line number */ + linenum = currline(where); + if (linenum != 0) + ap_linenum(linenum); + else + ap_quest(); + break; + case 'L': /* Final line number */ + len = ch_length(); + if (len == NULL_POSITION || len == ch_zero() || + (linenum = find_linenum(len)) <= 0) + ap_quest(); + else + ap_linenum(linenum-1); + break; + case 'm': /* Number of files */ +#if TAGS + n = ntags(); + if (n) + ap_int(n); + else +#endif + ap_int(nifile()); + break; + case 'p': /* Percent into file (bytes) */ + pos = curr_byte(where); + len = ch_length(); + if (pos != NULL_POSITION && len > 0) + ap_int(percentage(pos,len)); + else + ap_quest(); + break; + case 'P': /* Percent into file (lines) */ + linenum = currline(where); + if (linenum == 0 || + (len = ch_length()) == NULL_POSITION || len == ch_zero() || + (last_linenum = find_linenum(len)) <= 0) + ap_quest(); + else + ap_int(percentage(linenum, last_linenum)); + break; + case 's': /* Size of file */ + case 'B': + len = ch_length(); + if (len != NULL_POSITION) + ap_pos(len); + else + ap_quest(); + break; + case 't': /* Truncate trailing spaces in the message */ + while (mp > message && mp[-1] == ' ') + mp--; + *mp = '\0'; + break; + case 'T': /* Type of list */ +#if TAGS + if (ntags()) + ap_str("tag"); + else +#endif + ap_str("file"); + break; + case 'x': /* Name of next file */ + h = next_ifile(curr_ifile); + if (h != NULL_IFILE) + ap_str(get_filename(h)); + else + ap_quest(); + break; + } +} + +/* + * Skip a false conditional. + * When a false condition is found (either a false IF or the ELSE part + * of a true IF), this routine scans the prototype string to decide + * where to resume parsing the string. + * We must keep track of nested IFs and skip them properly. + */ + static constant char * +skipcond(p) + register constant char *p; +{ + register int iflevel; + + /* + * We came in here after processing a ? or :, + * so we start nested one level deep. + */ + iflevel = 1; + + for (;;) switch (*++p) + { + case '?': + /* + * Start of a nested IF. + */ + iflevel++; + break; + case ':': + /* + * Else. + * If this matches the IF we came in here with, + * then we're done. + */ + if (iflevel == 1) + return (p); + break; + case '.': + /* + * Endif. + * If this matches the IF we came in here with, + * then we're done. + */ + if (--iflevel == 0) + return (p); + break; + case '\\': + /* + * Backslash escapes the next character. + */ + ++p; + break; + case '\0': + /* + * Whoops. Hit end of string. + * This is a malformed conditional, but just treat it + * as if all active conditionals ends here. + */ + return (p-1); + } + /*NOTREACHED*/ +} + +/* + * Decode a char that represents a position on the screen. + */ + static constant char * +wherechar(p, wp) + char constant *p; + int *wp; +{ + switch (*p) + { + case 'b': case 'd': case 'l': case 'p': case 'P': + switch (*++p) + { + case 't': *wp = TOP; break; + case 'm': *wp = MIDDLE; break; + case 'b': *wp = BOTTOM; break; + case 'B': *wp = BOTTOM_PLUS_ONE; break; + case 'j': *wp = adjsline(jump_sline); break; + default: *wp = TOP; p--; break; + } + } + return (p); +} + +/* + * Construct a message based on a prototype string. + */ + public char * +pr_expand(proto, maxwidth) + constant char *proto; + int maxwidth; +{ + register constant char *p; + register int c; + int where; + + mp = message; + + if (*proto == '\0') + return (""); + + for (p = proto; *p != '\0'; p++) + { + switch (*p) + { + default: /* Just put the character in the message */ + ap_char(*p); + break; + case '\\': /* Backslash escapes the next character */ + p++; + ap_char(*p); + break; + case '?': /* Conditional (IF) */ + if ((c = *++p) == '\0') + --p; + else + { + where = 0; + p = wherechar(p, &where); + if (!cond(c, where)) + p = skipcond(p); + } + break; + case ':': /* ELSE */ + p = skipcond(p); + break; + case '.': /* ENDIF */ + break; + case '%': /* Percent escape */ + if ((c = *++p) == '\0') + --p; + else + { + where = 0; + p = wherechar(p, &where); + protochar(c, where, +#if EDITOR + (proto == editproto)); +#else + 0); +#endif + + } + break; + } + } + + if (mp == message) + return (""); + if (maxwidth > 0 && mp >= message + maxwidth) + { + /* + * Message is too long. + * Return just the final portion of it. + */ + return (mp - maxwidth); + } + return (message); +} + +/* + * Return a message suitable for printing by the "=" command. + */ + public char * +eq_message() +{ + return (pr_expand(eqproto, 0)); +} + +/* + * Return a prompt. + * This depends on the prompt type (SHORT, MEDIUM, LONG), etc. + * If we can't come up with an appropriate prompt, return NULL + * and the caller will prompt with a colon. + */ + public char * +pr_string() +{ + char *prompt; + int type; + + type = (!less_is_more) ? pr_type : pr_type ? 0 : 1; + prompt = pr_expand((ch_getflags() & CH_HELPFILE) ? + hproto : prproto[type], + sc_width-so_s_width-so_e_width-2); + new_file = 0; + return (prompt); +} + +/* + * Return a message suitable for printing while waiting in the F command. + */ + public char * +wait_message() +{ + return (pr_expand(wproto, sc_width-so_s_width-so_e_width-2)); +} diff --git a/files/Sources/files/less/screen.c b/files/Sources/files/less/screen.c new file mode 100644 index 00000000..bd48f438 --- /dev/null +++ b/files/Sources/files/less/screen.c @@ -0,0 +1,2661 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Routines which deal with the characteristics of the terminal. + * Uses termcap to be as terminal-independent as possible. + */ + +#include "less.h" +#include "cmd.h" + +#if MSDOS_COMPILER +#include "pckeys.h" +#if MSDOS_COMPILER==MSOFTC +#include +#else +#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC +#include +#if MSDOS_COMPILER==DJGPPC +#include +extern int fd0; +#endif +#else +#if MSDOS_COMPILER==WIN32C +#include +#endif +#endif +#endif +#include + +#else + +#if HAVE_SYS_IOCTL_H +#include +#endif + +#if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS +#include +#else +#if HAVE_TERMIO_H +#include +#else +#if HAVE_SGSTAT_H +#include +#else +#include +#endif +#endif +#endif + +#if HAVE_TERMCAP_H +#include "termcap.h" +#endif +#ifdef _OSK +#include +#endif +#if OS2 +#include +#include "pckeys.h" +#endif +#if HAVE_SYS_STREAM_H +#include +#endif +#if HAVE_SYS_PTEM_H +#include +#endif + +#endif /* MSDOS_COMPILER */ + +/* + * Check for broken termios package that forces you to manually + * set the line discipline. + */ +#ifdef __ultrix__ +#define MUST_SET_LINE_DISCIPLINE 1 +#else +#define MUST_SET_LINE_DISCIPLINE 0 +#endif + +#if OS2 +#define DEFAULT_TERM "ansi" +static char *windowid; +#else +#define DEFAULT_TERM "unknown" +#endif + +#if MSDOS_COMPILER==MSOFTC +static int videopages; +static long msec_loops; +static int flash_created = 0; +#define SETCOLORS(fg,bg) { _settextcolor(fg); _setbkcolor(bg); } +#endif + +#if MSDOS_COMPILER==BORLANDC +static unsigned short *whitescreen; +static int flash_created = 0; +#endif +#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC +#define _settextposition(y,x) gotoxy(x,y) +#define _clearscreen(m) clrscr() +#define _outtext(s) cputs(s) +#define SETCOLORS(fg,bg) { textcolor(fg); textbackground(bg); } +extern int sc_height; +#endif + +#if MSDOS_COMPILER==WIN32C +struct keyRecord +{ + int ascii; + int scan; +} currentKey; + +static int keyCount = 0; +static WORD curr_attr; +static int pending_scancode = 0; +static WORD *whitescreen; + +static HANDLE con_out_save = INVALID_HANDLE_VALUE; /* previous console */ +static HANDLE con_out_ours = INVALID_HANDLE_VALUE; /* our own */ +HANDLE con_out = INVALID_HANDLE_VALUE; /* current console */ + +extern int quitting; +static void win32_init_term(); +static void win32_deinit_term(); + +#define FG_COLORS (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY) +#define BG_COLORS (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY) +#define MAKEATTR(fg,bg) ((WORD)((fg)|((bg)<<4))) +#define SETCOLORS(fg,bg) { curr_attr = MAKEATTR(fg,bg); \ + if (SetConsoleTextAttribute(con_out, curr_attr) == 0) \ + error("SETCOLORS failed"); } +#endif + +#if MSDOS_COMPILER +public int nm_fg_color; /* Color of normal text */ +public int nm_bg_color; +public int bo_fg_color; /* Color of bold text */ +public int bo_bg_color; +public int ul_fg_color; /* Color of underlined text */ +public int ul_bg_color; +public int so_fg_color; /* Color of standout text */ +public int so_bg_color; +public int bl_fg_color; /* Color of blinking text */ +public int bl_bg_color; +static int sy_fg_color; /* Color of system text (before less) */ +static int sy_bg_color; +public int sgr_mode; /* Honor ANSI sequences rather than using above */ + +#else + +/* + * Strings passed to tputs() to do various terminal functions. + */ +static char + *sc_pad, /* Pad string */ + *sc_home, /* Cursor home */ + *sc_addline, /* Add line, scroll down following lines */ + *sc_lower_left, /* Cursor to last line, first column */ + *sc_return, /* Cursor to beginning of current line */ + *sc_move, /* General cursor positioning */ + *sc_clear, /* Clear screen */ + *sc_eol_clear, /* Clear to end of line */ + *sc_eos_clear, /* Clear to end of screen */ + *sc_s_in, /* Enter standout (highlighted) mode */ + *sc_s_out, /* Exit standout mode */ + *sc_u_in, /* Enter underline mode */ + *sc_u_out, /* Exit underline mode */ + *sc_b_in, /* Enter bold mode */ + *sc_b_out, /* Exit bold mode */ + *sc_bl_in, /* Enter blink mode */ + *sc_bl_out, /* Exit blink mode */ + *sc_visual_bell, /* Visual bell (flash screen) sequence */ + *sc_backspace, /* Backspace cursor */ + *sc_s_keypad, /* Start keypad mode */ + *sc_e_keypad, /* End keypad mode */ + *sc_init, /* Startup terminal initialization */ + *sc_deinit; /* Exit terminal de-initialization */ +#endif + +static int init_done = 0; + +public int auto_wrap; /* Terminal does \r\n when write past margin */ +public int ignaw; /* Terminal ignores \n immediately after wrap */ +public int erase_char; /* The user's erase char */ +public int erase2_char; /* The user's other erase char */ +public int kill_char; /* The user's line-kill char */ +public int werase_char; /* The user's word-erase char */ +public int sc_width, sc_height; /* Height & width of screen */ +public int bo_s_width, bo_e_width; /* Printing width of boldface seq */ +public int ul_s_width, ul_e_width; /* Printing width of underline seq */ +public int so_s_width, so_e_width; /* Printing width of standout seq */ +public int bl_s_width, bl_e_width; /* Printing width of blink seq */ +public int above_mem, below_mem; /* Memory retained above/below screen */ +public int can_goto_line; /* Can move cursor to any line */ +public int clear_bg; /* Clear fills with background color */ +public int missing_cap = 0; /* Some capability is missing */ + +static int attrmode = AT_NORMAL; +extern int binattr; + +#if !MSDOS_COMPILER +static char *cheaper(char *t1, char *t2, char *def); +static void tmodes(char *incap, char *outcap, char **instr, char **outstr, char *def_instr, char *def_outstr, char **spp); +#endif + +/* + * These two variables are sometimes defined in, + * and needed by, the termcap library. + */ +#if MUST_DEFINE_OSPEED +extern short ospeed; /* Terminal output baud rate */ +extern char PC; /* Pad character */ +#endif +#ifdef _OSK +short ospeed; +char PC_, *UP, *BC; +#endif + +extern int quiet; /* If VERY_QUIET, use visual bell for bell */ +extern int no_back_scroll; +extern int swindow; +extern int no_init; +extern int quit_at_eof; +extern int less_is_more; +extern int no_keypad; +extern int sigs; +extern int wscroll; +extern int screen_trashed; +extern int tty; +extern int top_scroll; +extern int oldbot; +#if HILITE_SEARCH +extern int hilite_search; +#endif + +// iOS: these functions are forbidden in the AppStore: +// extern char *tgetstr(); +// extern char *tgoto(); + +extern int unix2003_compat; +extern int dashn_numline_count; + +/* + * Change terminal to "raw mode", or restore to "normal" mode. + * "Raw mode" means + * 1. An outstanding read will complete on receipt of a single keystroke. + * 2. Input is not echoed. + * 3. On output, \n is mapped to \r\n. + * 4. \t is NOT expanded into spaces. + * 5. Signal-causing characters such as ctrl-C (interrupt), + * etc. are NOT disabled. + * It doesn't matter whether an input \n is mapped to \r, or vice versa. + */ + public void +raw_mode(on) + int on; +{ + static int curr_on = 0; + + if (on == curr_on) + return; + + erase2_char = '\b'; /* in case OS doesn't know about erase2 */ +#if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS + { + struct termios s; + static struct termios save_term; + static int saved_term = 0; + + if (on) + { + /* + * Get terminal modes. + */ + tcgetattr(tty, &s); + + /* + * Save modes and set certain variables dependent on modes. + */ + if (!saved_term) + { + save_term = s; + saved_term = 1; + } +#if HAVE_OSPEED + switch (cfgetospeed(&s)) + { +#ifdef B0 + case B0: ospeed = 0; break; +#endif +#ifdef B50 + case B50: ospeed = 1; break; +#endif +#ifdef B75 + case B75: ospeed = 2; break; +#endif +#ifdef B110 + case B110: ospeed = 3; break; +#endif +#ifdef B134 + case B134: ospeed = 4; break; +#endif +#ifdef B150 + case B150: ospeed = 5; break; +#endif +#ifdef B200 + case B200: ospeed = 6; break; +#endif +#ifdef B300 + case B300: ospeed = 7; break; +#endif +#ifdef B600 + case B600: ospeed = 8; break; +#endif +#ifdef B1200 + case B1200: ospeed = 9; break; +#endif +#ifdef B1800 + case B1800: ospeed = 10; break; +#endif +#ifdef B2400 + case B2400: ospeed = 11; break; +#endif +#ifdef B4800 + case B4800: ospeed = 12; break; +#endif +#ifdef B9600 + case B9600: ospeed = 13; break; +#endif +#ifdef EXTA + case EXTA: ospeed = 14; break; +#endif +#ifdef EXTB + case EXTB: ospeed = 15; break; +#endif +#ifdef B57600 + case B57600: ospeed = 16; break; +#endif +#ifdef B115200 + case B115200: ospeed = 17; break; +#endif + default: ; + } +#endif + // erase_char = s.c_cc[VERASE]; + erase_char = 0x7f; /* iOS: ^? for erase */ +#ifdef VERASE2 + erase2_char = s.c_cc[VERASE2]; +#endif + // kill_char = s.c_cc[VKILL]; + kill_char = CONTROL('C');; /* iOS: ^c for kill char */ +#ifdef VWERASE + werase_char = s.c_cc[VWERASE]; +#else + werase_char = CONTROL('W'); +#endif + werase_char = CONTROL('W');; /* iOS: ^w for w erase */ + + /* + * Set the modes to the way we want them. + */ + s.c_lflag &= ~(0 +#ifdef ICANON + | ICANON +#endif +#ifdef ECHO + | ECHO +#endif +#ifdef ECHOE + | ECHOE +#endif +#ifdef ECHOK + | ECHOK +#endif +#if ECHONL + | ECHONL +#endif + ); + + s.c_oflag |= (0 +#ifdef OXTABS + | OXTABS +#else +#ifdef TAB3 + | TAB3 +#else +#ifdef XTABS + | XTABS +#endif +#endif +#endif +#ifdef OPOST + | OPOST +#endif +#ifdef ONLCR + | ONLCR +#endif + ); + + s.c_oflag &= ~(0 +#ifdef ONOEOT + | ONOEOT +#endif +#ifdef OCRNL + | OCRNL +#endif +#ifdef ONOCR + | ONOCR +#endif +#ifdef ONLRET + | ONLRET +#endif + ); + s.c_cc[VMIN] = 1; + s.c_cc[VTIME] = 0; +#ifdef VLNEXT + s.c_cc[VLNEXT] = 0; +#endif +#ifdef VDSUSP + s.c_cc[VDSUSP] = 0; +#endif +#if MUST_SET_LINE_DISCIPLINE + /* + * System's termios is broken; need to explicitly + * request TERMIODISC line discipline. + */ + s.c_line = TERMIODISC; +#endif + } else + { + /* + * Restore saved modes. + */ + s = save_term; + } +#if HAVE_FSYNC + fsync(tty); +#endif + tcsetattr(tty, TCSADRAIN, &s); +#if MUST_SET_LINE_DISCIPLINE + if (!on) + { + /* + * Broken termios *ignores* any line discipline + * except TERMIODISC. A different old line discipline + * is therefore not restored, yet. Restore the old + * line discipline by hand. + */ + ioctl(tty, TIOCSETD, &save_term.c_line); + } +#endif + } +#else +#ifdef TCGETA + { + struct termio s; + static struct termio save_term; + static int saved_term = 0; + + if (on) + { + /* + * Get terminal modes. + */ + ioctl(tty, TCGETA, &s); + + /* + * Save modes and set certain variables dependent on modes. + */ + if (!saved_term) + { + save_term = s; + saved_term = 1; + } +#if HAVE_OSPEED + ospeed = s.c_cflag & CBAUD; +#endif + erase_char = s.c_cc[VERASE]; + kill_char = s.c_cc[VKILL]; +#ifdef VWERASE + werase_char = s.c_cc[VWERASE]; +#else + werase_char = CONTROL('W'); +#endif + + /* + * Set the modes to the way we want them. + */ + s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL); + s.c_oflag |= (OPOST|ONLCR|TAB3); + s.c_oflag &= ~(OCRNL|ONOCR|ONLRET); + s.c_cc[VMIN] = 1; + s.c_cc[VTIME] = 0; + } else + { + /* + * Restore saved modes. + */ + s = save_term; + } + ioctl(tty, TCSETAW, &s); + } +#else +#ifdef TIOCGETP + { + struct sgttyb s; + static struct sgttyb save_term; + static int saved_term = 0; + + if (on) + { + /* + * Get terminal modes. + */ + ioctl(tty, TIOCGETP, &s); + + /* + * Save modes and set certain variables dependent on modes. + */ + if (!saved_term) + { + save_term = s; + saved_term = 1; + } +#if HAVE_OSPEED + ospeed = s.sg_ospeed; +#endif + erase_char = s.sg_erase; + kill_char = s.sg_kill; + werase_char = CONTROL('W'); + + /* + * Set the modes to the way we want them. + */ + s.sg_flags |= CBREAK; + s.sg_flags &= ~(ECHO|XTABS); + } else + { + /* + * Restore saved modes. + */ + s = save_term; + } + ioctl(tty, TIOCSETN, &s); + } +#else +#ifdef _OSK + { + struct sgbuf s; + static struct sgbuf save_term; + static int saved_term = 0; + + if (on) + { + /* + * Get terminal modes. + */ + _gs_opt(tty, &s); + + /* + * Save modes and set certain variables dependent on modes. + */ + if (!saved_term) + { + save_term = s; + saved_term = 1; + } + erase_char = s.sg_bspch; + kill_char = s.sg_dlnch; + werase_char = CONTROL('W'); + + /* + * Set the modes to the way we want them. + */ + s.sg_echo = 0; + s.sg_eofch = 0; + s.sg_pause = 0; + s.sg_psch = 0; + } else + { + /* + * Restore saved modes. + */ + s = save_term; + } + _ss_opt(tty, &s); + } +#else + /* MS-DOS, Windows, or OS2 */ +#if OS2 + /* OS2 */ + LSIGNAL(SIGINT, SIG_IGN); +#endif + erase_char = '\b'; +#if MSDOS_COMPILER==DJGPPC + kill_char = CONTROL('U'); + /* + * So that when we shell out or run another program, its + * stdin is in cooked mode. We do not switch stdin to binary + * mode if fd0 is zero, since that means we were called before + * tty was reopened in open_getchr, in which case we would be + * changing the original stdin device outside less. + */ + if (fd0 != fileno(thread_stdin)) + setmode(0, on ? O_BINARY : O_TEXT); +#else + kill_char = ESC; +#endif + werase_char = CONTROL('W'); +#endif +#endif +#endif +#endif + curr_on = on; +} + +#if !MSDOS_COMPILER +/* + * Some glue to prevent calling termcap functions if tgetent() failed. + */ +static int hardcopy; + + static char * +ltget_env(capname) + char *capname; +{ + char name[16]; + char *s; + + s = lgetenv("LESS_TERMCAP_DEBUG"); + if (s != NULL && *s != '\0') + { + struct env { struct env *next; char *name; char *value; }; + static struct env *envs = NULL; + struct env *p; + for (p = envs; p != NULL; p = p->next) + if (strcmp(p->name, capname) == 0) + return p->value; + p = (struct env *) ecalloc(1, sizeof(struct env)); + p->name = save(capname); + p->value = (char *) ecalloc(strlen(capname)+3, sizeof(char)); + sprintf(p->value, "<%s>", capname); + p->next = envs; + envs = p; + return p->value; + } + strcpy(name, "LESS_TERMCAP_"); + strcat(name, capname); + return (lgetenv(name)); +} + + static int +ltgetflag(capname) + char *capname; +{ + char *s; + + if ((s = ltget_env(capname)) != NULL) + return (*s != '\0' && *s != '0'); + if (hardcopy) + return (0); + // iOS: tgetflag is forbidden: + if (strcmp(capname, "hc") == 0) // hardcopy (e.g. printer) + return 0; + if (strcmp(capname, "am") == 0)/* Terminal does \r\n when write past margin */ + return 1; + if (strcmp(capname, "xn") == 0) /* Terminal ignores \n immediately after wrap */ + return 0; + if (strcmp(capname, "da") == 0) // memory retained above screen + return 1; + if (strcmp(capname, "db") == 0) // memory retained below screen + return 1; + if (strcmp(capname, "ut") == 0) /* Clear fills with background color */ + return 1; + if (strcmp(capname, "bs") == 0) // backspace + return 0; + return 0; + // return (tgetflag(capname)); +} + +/* iOS: can't use tgetnum + static int +ltgetnum(capname) + char *capname; +{ + char *s; + + if ((s = ltget_env(capname)) != NULL) + return (atoi(s)); + if (hardcopy) + return (-1); + return (tgetnum(capname)); +} +*/ + + static char * +ltgetstr(capname, pp) + char *capname; + char **pp; +{ + char *s; + + if ((s = ltget_env(capname)) != NULL) + return (s); + if (hardcopy) + return (NULL); + if (strcmp(capname, "ku") == 0) // up arrow + return "\033[A"; + if (strcmp(capname, "kd") == 0) // down arrow + return "\033[B"; + if (strcmp(capname, "kr") == 0) // right arrow + return "\033[C"; + if (strcmp(capname, "kl") == 0) // left arrow + return "\033[D"; + if (strcmp(capname, "kP") == 0) // page up + return "\033[5;~"; + if (strcmp(capname, "kN") == 0) // page down + return "\033[6;~"; + if (strcmp(capname, "kh") == 0) // home + return "\033[1;~"; + if (strcmp(capname, "@7") == 0) // end + return "\033[4;~"; + if (strcmp(capname, "kD") == 0) // delete + return "\033[3;~"; + if (strcmp(capname, "kD") == 0) // delete + return "\033[3;~"; + if (strcmp(capname, "ks") == 0) // start keypad mode + return NULL; + if (strcmp(capname, "ke") == 0) // end keypad mode + return NULL; + if (strcmp(capname, "ti") == 0) // Startup terminal initialization + return NULL; + if (strcmp(capname, "te") == 0) // End terminal initialization + return NULL; + if (strcmp(capname, "ce") == 0) // Clear to end of line + return "\033[K"; + if (strcmp(capname, "cd") == 0) // Clear to end of screen + return "\033[0J"; + if (strcmp(capname, "cl") == 0) // Clear screen + return "\033[2J"; + if (strcmp(capname, "cm") == 0) // General cursor positioning + return "\033[H"; + if (strcmp(capname, "vb") == 0) // Visual bell + return NULL; + if (strcmp(capname, "bs") == 0) // Backspace? + return "\b"; + if (strcmp(capname, "ho") == 0) // Home + return NULL; + if (strcmp(capname, "ll") == 0) // lower-left + return NULL; + if (strcmp(capname, "cr") == 0) // carriage return + return NULL; + if (strcmp(capname, "al") == 0) // add line + return NULL; + if (strcmp(capname, "sr") == 0) // scroll reverse + return "\033[1S"; + if (strcmp(capname, "pc") == 0) // Pad string + return NULL; + if (strcmp(capname, "bc") == 0) // backspace char + return "\b"; + if (strcmp(capname, "so") == 0) // Enter standout (highlighted) mode + return "\033[7m"; + if (strcmp(capname, "se") == 0) // Exit standout mode + return "\033[0m"; + if (strcmp(capname, "us") == 0) // Enter underline mode + return "\033[4m"; + if (strcmp(capname, "ue") == 0) // Exit underline mode + return "\033[24m"; + if (strcmp(capname, "md") == 0) // Enter bold mode + return "\033[1m"; + if (strcmp(capname, "me") == 0) // Exit bold mode + return "\033[0m"; + if (strcmp(capname, "mb") == 0) // Enter blink mode + return "\033[5m"; + // fprintf(stderr, "Called tgetstr %s\n", capname); + return NULL; + // return (tgetstr(capname, pp)); +} +#endif /* MSDOS_COMPILER */ + +/* + * Get size of the output screen. + */ + public void +scrsize() +{ + register char *s; + int sys_height; + int sys_width; +#if !MSDOS_COMPILER + int n; +#endif + +#define DEF_SC_WIDTH 80 +#if MSDOS_COMPILER +#define DEF_SC_HEIGHT 25 +#else +#define DEF_SC_HEIGHT 24 +#endif + + + sys_width = sys_height = 0; + +#if MSDOS_COMPILER==MSOFTC + { + struct videoconfig w; + _getvideoconfig(&w); + sys_height = w.numtextrows; + sys_width = w.numtextcols; + } +#else +#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC + { + struct text_info w; + gettextinfo(&w); + sys_height = w.screenheight; + sys_width = w.screenwidth; + } +#else +#if MSDOS_COMPILER==WIN32C + { + CONSOLE_SCREEN_BUFFER_INFO scr; + GetConsoleScreenBufferInfo(con_out, &scr); + sys_height = scr.srWindow.Bottom - scr.srWindow.Top + 1; + sys_width = scr.srWindow.Right - scr.srWindow.Left + 1; + } +#else +#if OS2 + { + int s[2]; + _scrsize(s); + sys_width = s[0]; + sys_height = s[1]; + /* + * When using terminal emulators for XFree86/OS2, the + * _scrsize function does not work well. + * Call the scrsize.exe program to get the window size. + */ + windowid = getenv("WINDOWID"); + if (windowid != NULL) + { + FILE *fd = popen("scrsize", "rt"); + if (fd != NULL) + { + int w, h; + fscanf(fd, "%i %i", &w, &h); + if (w > 0 && h > 0) + { + sys_width = w; + sys_height = h; + } + pclose(fd); + } + } + } +#else +#ifdef TIOCGWINSZ + { + struct winsize w; + if (ioctl(2, TIOCGWINSZ, &w) == 0) + { + if (w.ws_row > 0) + sys_height = w.ws_row; + if (w.ws_col > 0) + sys_width = w.ws_col; + } + } +#else +#ifdef WIOCGETD + { + struct uwdata w; + if (ioctl(2, WIOCGETD, &w) == 0) + { + if (w.uw_height > 0) + sys_height = w.uw_height / w.uw_vs; + if (w.uw_width > 0) + sys_width = w.uw_width / w.uw_hs; + } + } +#endif +#endif +#endif +#endif +#endif +#endif + + if (unix2003_compat) { + if (dashn_numline_count) { /* Overrides all other sources */ + sc_height = dashn_numline_count; + goto done_height; + } + } + if (sys_height > 0) { + sc_height = sys_height; + if (!unix2003_compat) { + goto done_height; + } + } + if ((s = lgetenv("LINES")) != NULL) + sc_height = atoi(s); +#if !MSDOS_COMPILER + // iOS: can't use tgetnum + // else if ((n = ltgetnum("li")) > 0) + // sc_height = n; +#endif +done_height: + if (sc_height <= 0) + sc_height = DEF_SC_HEIGHT; + if (sys_width > 0) { + sc_width = sys_width; + if (!unix2003_compat) { + goto done; + } + } + if ((s = lgetenv("COLUMNS")) != NULL) + sc_width = atoi(s); +#if !MSDOS_COMPILER + // else if ((n = ltgetnum("co")) > 0) + // sc_width = n; +#endif +done: + if (sc_width <= 0) + sc_width = DEF_SC_WIDTH; +} + +#if MSDOS_COMPILER==MSOFTC +/* + * Figure out how many empty loops it takes to delay a millisecond. + */ + static void +get_clock() +{ + clock_t start; + + /* + * Get synchronized at the start of a tick. + */ + start = clock(); + while (clock() == start) + ; + /* + * Now count loops till the next tick. + */ + start = clock(); + msec_loops = 0; + while (clock() == start) + msec_loops++; + /* + * Convert from (loops per clock) to (loops per millisecond). + */ + msec_loops *= CLOCKS_PER_SEC; + msec_loops /= 1000; +} + +/* + * Delay for a specified number of milliseconds. + */ + static void +dummy_func() +{ + static long delay_dummy = 0; + delay_dummy++; +} + + static void +delay(msec) + int msec; +{ + long i; + + while (msec-- > 0) + { + for (i = 0; i < msec_loops; i++) + { + /* + * Make it look like we're doing something here, + * so the optimizer doesn't remove the whole loop. + */ + dummy_func(); + } + } +} +#endif + +/* + * Return the characters actually input by a "special" key. + */ + public char * +special_key_str(key) + int key; +{ + static char tbuf[40]; + char *s; +#if MSDOS_COMPILER || OS2 + static char k_right[] = { '\340', PCK_RIGHT, 0 }; + static char k_left[] = { '\340', PCK_LEFT, 0 }; + static char k_ctl_right[] = { '\340', PCK_CTL_RIGHT, 0 }; + static char k_ctl_left[] = { '\340', PCK_CTL_LEFT, 0 }; + static char k_insert[] = { '\340', PCK_INSERT, 0 }; + static char k_delete[] = { '\340', PCK_DELETE, 0 }; + static char k_ctl_delete[] = { '\340', PCK_CTL_DELETE, 0 }; + static char k_ctl_backspace[] = { '\177', 0 }; + static char k_home[] = { '\340', PCK_HOME, 0 }; + static char k_end[] = { '\340', PCK_END, 0 }; + static char k_up[] = { '\340', PCK_UP, 0 }; + static char k_down[] = { '\340', PCK_DOWN, 0 }; + static char k_backtab[] = { '\340', PCK_SHIFT_TAB, 0 }; + static char k_pagedown[] = { '\340', PCK_PAGEDOWN, 0 }; + static char k_pageup[] = { '\340', PCK_PAGEUP, 0 }; + static char k_f1[] = { '\340', PCK_F1, 0 }; +#endif +#if !MSDOS_COMPILER + char *sp = tbuf; +#endif + + switch (key) + { +#if OS2 + /* + * If windowid is not NULL, assume less is executed in + * the XFree86 environment. + */ + case SK_RIGHT_ARROW: + s = windowid ? ltgetstr("kr", &sp) : k_right; + break; + case SK_LEFT_ARROW: + s = windowid ? ltgetstr("kl", &sp) : k_left; + break; + case SK_UP_ARROW: + s = windowid ? ltgetstr("ku", &sp) : k_up; + break; + case SK_DOWN_ARROW: + s = windowid ? ltgetstr("kd", &sp) : k_down; + break; + case SK_PAGE_UP: + s = windowid ? ltgetstr("kP", &sp) : k_pageup; + break; + case SK_PAGE_DOWN: + s = windowid ? ltgetstr("kN", &sp) : k_pagedown; + break; + case SK_HOME: + s = windowid ? ltgetstr("kh", &sp) : k_home; + break; + case SK_END: + s = windowid ? ltgetstr("@7", &sp) : k_end; + break; + case SK_DELETE: + if (windowid) + { + s = ltgetstr("kD", &sp); + if (s == NULL) + { + tbuf[0] = '\177'; + tbuf[1] = '\0'; + s = tbuf; + } + } else + s = k_delete; + break; +#endif +#if MSDOS_COMPILER + case SK_RIGHT_ARROW: + s = k_right; + break; + case SK_LEFT_ARROW: + s = k_left; + break; + case SK_UP_ARROW: + s = k_up; + break; + case SK_DOWN_ARROW: + s = k_down; + break; + case SK_PAGE_UP: + s = k_pageup; + break; + case SK_PAGE_DOWN: + s = k_pagedown; + break; + case SK_HOME: + s = k_home; + break; + case SK_END: + s = k_end; + break; + case SK_DELETE: + s = k_delete; + break; +#endif +#if MSDOS_COMPILER || OS2 + case SK_INSERT: + s = k_insert; + break; + case SK_CTL_LEFT_ARROW: + s = k_ctl_left; + break; + case SK_CTL_RIGHT_ARROW: + s = k_ctl_right; + break; + case SK_CTL_BACKSPACE: + s = k_ctl_backspace; + break; + case SK_CTL_DELETE: + s = k_ctl_delete; + break; + case SK_F1: + s = k_f1; + break; + case SK_BACKTAB: + s = k_backtab; + break; +#else + case SK_RIGHT_ARROW: + s = ltgetstr("kr", &sp); + break; + case SK_LEFT_ARROW: + s = ltgetstr("kl", &sp); + break; + case SK_UP_ARROW: + s = ltgetstr("ku", &sp); + break; + case SK_DOWN_ARROW: + s = ltgetstr("kd", &sp); + break; + case SK_PAGE_UP: + s = ltgetstr("kP", &sp); + break; + case SK_PAGE_DOWN: + s = ltgetstr("kN", &sp); + break; + case SK_HOME: + s = ltgetstr("kh", &sp); + break; + case SK_END: + s = ltgetstr("@7", &sp); + break; + case SK_DELETE: + s = ltgetstr("kD", &sp); + if (s == NULL) + { + tbuf[0] = '\177'; + tbuf[1] = '\0'; + s = tbuf; + } + break; +#endif + case SK_CONTROL_K: + tbuf[0] = CONTROL('K'); + tbuf[1] = '\0'; + s = tbuf; + break; + default: + return (NULL); + } + return (s); +} + +/* + * Get terminal capabilities via termcap. + */ + public void +get_term() +{ +#if MSDOS_COMPILER + auto_wrap = 1; + ignaw = 0; + can_goto_line = 1; + clear_bg = 1; + /* + * Set up default colors. + * The xx_s_width and xx_e_width vars are already initialized to 0. + */ +#if MSDOS_COMPILER==MSOFTC + sy_bg_color = _getbkcolor(); + sy_fg_color = _gettextcolor(); + get_clock(); +#else +#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC + { + struct text_info w; + gettextinfo(&w); + sy_bg_color = (w.attribute >> 4) & 0x0F; + sy_fg_color = (w.attribute >> 0) & 0x0F; + } +#else +#if MSDOS_COMPILER==WIN32C + { + DWORD nread; + CONSOLE_SCREEN_BUFFER_INFO scr; + + con_out_save = con_out = GetStdHandle(STD_OUTPUT_HANDLE); + /* + * Always open stdin in binary. Note this *must* be done + * before any file operations have been done on fd0. + */ + SET_BINARY(0); + GetConsoleScreenBufferInfo(con_out, &scr); + ReadConsoleOutputAttribute(con_out, &curr_attr, + 1, scr.dwCursorPosition, &nread); + sy_bg_color = (curr_attr & BG_COLORS) >> 4; /* normalize */ + sy_fg_color = curr_attr & FG_COLORS; + } +#endif +#endif +#endif + nm_fg_color = sy_fg_color; + nm_bg_color = sy_bg_color; + bo_fg_color = 11; + bo_bg_color = 0; + ul_fg_color = 9; + ul_bg_color = 0; + so_fg_color = 15; + so_bg_color = 9; + bl_fg_color = 15; + bl_bg_color = 0; + sgr_mode = 0; + + /* + * Get size of the screen. + */ + scrsize(); + pos_init(); + + +#else /* !MSDOS_COMPILER */ + + char *sp; + register char *t1, *t2; + char *term; + // char termbuf[TERMBUF_SIZE]; + + static char sbuf[TERMSBUF_SIZE]; + +#if OS2 + /* + * Make sure the termcap database is available. + */ + sp = lgetenv("TERMCAP"); + if (sp == NULL || *sp == '\0') + { + char *termcap; + if ((sp = homefile("termcap.dat")) != NULL) + { + termcap = (char *) ecalloc(strlen(sp)+9, sizeof(char)); + sprintf(termcap, "TERMCAP=%s", sp); + free(sp); + putenv(termcap); + } + } +#endif + /* + * Find out what kind of terminal this is. + */ + if ((term = lgetenv("TERM")) == NULL) + term = DEFAULT_TERM; + hardcopy = 0; + // if (tgetent(termbuf, term) != TGETENT_OK) + // hardcopy = 1; + if (ltgetflag("hc")) + hardcopy = 1; + + /* + * Get size of the screen. + */ + scrsize(); + pos_init(); + + auto_wrap = ltgetflag("am"); + ignaw = ltgetflag("xn"); + above_mem = ltgetflag("da"); + below_mem = ltgetflag("db"); + clear_bg = ltgetflag("ut"); + + /* + * Assumes termcap variable "sg" is the printing width of: + * the standout sequence, the end standout sequence, + * the underline sequence, the end underline sequence, + * the boldface sequence, and the end boldface sequence. + */ + // iOS: printing width = 4 (esc + [ + 1 + m) + if ((so_s_width = 4 /* ltgetnum("sg") */ ) < 0) + so_s_width = 0; + so_e_width = so_s_width; + + bo_s_width = bo_e_width = so_s_width; + ul_s_width = ul_e_width = so_s_width; + bl_s_width = bl_e_width = so_s_width; + +#if HILITE_SEARCH + if (so_s_width > 0 || so_e_width > 0) + /* + * Disable highlighting by default on magic cookie terminals. + * Turning on highlighting might change the displayed width + * of a line, causing the display to get messed up. + * The user can turn it back on with -g, + * but she won't like the results. + */ + hilite_search = 0; +#endif + + /* + * Get various string-valued capabilities. + */ + sp = sbuf; + +#if HAVE_OSPEED + sc_pad = ltgetstr("pc", &sp); + if (sc_pad != NULL) + PC = *sc_pad; +#endif + + sc_s_keypad = ltgetstr("ks", &sp); + if (sc_s_keypad == NULL) + sc_s_keypad = ""; + sc_e_keypad = ltgetstr("ke", &sp); + if (sc_e_keypad == NULL) + sc_e_keypad = ""; + + sc_init = ltgetstr("ti", &sp); + if (sc_init == NULL) + sc_init = ""; + + sc_deinit= ltgetstr("te", &sp); + if (sc_deinit == NULL) + sc_deinit = ""; + + sc_eol_clear = ltgetstr("ce", &sp); + if (sc_eol_clear == NULL || *sc_eol_clear == '\0') + { + missing_cap = 1; + sc_eol_clear = ""; + } + + sc_eos_clear = ltgetstr("cd", &sp); + if (below_mem && (sc_eos_clear == NULL || *sc_eos_clear == '\0')) + { + missing_cap = 1; + sc_eos_clear = ""; + } + + sc_clear = ltgetstr("cl", &sp); + if (sc_clear == NULL || *sc_clear == '\0') + { + missing_cap = 1; + sc_clear = "\n\n"; + } + + sc_move = ltgetstr("cm", &sp); + if (sc_move == NULL || *sc_move == '\0') + { + /* + * This is not an error here, because we don't + * always need sc_move. + * We need it only if we don't have home or lower-left. + */ + sc_move = ""; + can_goto_line = 0; + } else + can_goto_line = 1; + + tmodes("so", "se", &sc_s_in, &sc_s_out, "", "", &sp); + tmodes("us", "ue", &sc_u_in, &sc_u_out, sc_s_in, sc_s_out, &sp); + tmodes("md", "me", &sc_b_in, &sc_b_out, sc_s_in, sc_s_out, &sp); + tmodes("mb", "me", &sc_bl_in, &sc_bl_out, sc_s_in, sc_s_out, &sp); + + sc_visual_bell = ltgetstr("vb", &sp); + if (sc_visual_bell == NULL) + sc_visual_bell = ""; + + if (ltgetflag("bs")) + sc_backspace = "\b"; + else + { + sc_backspace = ltgetstr("bc", &sp); + if (sc_backspace == NULL || *sc_backspace == '\0') + sc_backspace = "\b"; + } + + /* + * Choose between using "ho" and "cm" ("home" and "cursor move") + * to move the cursor to the upper left corner of the screen. + */ + t1 = ltgetstr("ho", &sp); + if (t1 == NULL) + t1 = ""; + if (*sc_move == '\0') + t2 = ""; + else + { + // strcpy(sp, tgoto(sc_move, 0, 0)); + strcpy(sp, "\033[1;1H"); + t2 = sp; + sp += strlen(sp) + 1; + } + sc_home = cheaper(t1, t2, "|\b^"); + + /* + * Choose between using "ll" and "cm" ("lower left" and "cursor move") + * to move the cursor to the lower left corner of the screen. + */ + t1 = ltgetstr("ll", &sp); + if (t1 == NULL) + t1 = ""; + if (*sc_move == '\0') + t2 = ""; + else + { + // strcpy(sp, tgoto(sc_move, 0, sc_height-1)); + char gotobuf[10]; + sprintf(gotobuf, "\033[%d;1H", sc_height); + strcpy(sp, gotobuf); + t2 = sp; + sp += strlen(sp) + 1; + } + sc_lower_left = cheaper(t1, t2, "\r"); + + /* + * Get carriage return string. + */ + sc_return = ltgetstr("cr", &sp); + if (sc_return == NULL) + sc_return = "\r"; + + /* + * Choose between using "al" or "sr" ("add line" or "scroll reverse") + * to add a line at the top of the screen. + */ + t1 = ltgetstr("al", &sp); + if (t1 == NULL) + t1 = ""; + t2 = ltgetstr("sr", &sp); + if (t2 == NULL) + t2 = ""; +#if OS2 + if (*t1 == '\0' && *t2 == '\0') + sc_addline = ""; + else +#endif + if (above_mem) + sc_addline = t1; + else + sc_addline = cheaper(t1, t2, ""); + if (*sc_addline == '\0') + { + /* + * Force repaint on any backward movement. + */ + no_back_scroll = 1; + } +#endif /* MSDOS_COMPILER */ +} + +#if !MSDOS_COMPILER +/* + * Return the cost of displaying a termcap string. + * We use the trick of calling tputs, but as a char printing function + * we give it inc_costcount, which just increments "costcount". + * This tells us how many chars would be printed by using this string. + * {{ Couldn't we just use strlen? }} + */ +static int costcount; + +/*ARGSUSED*/ + static int +inc_costcount(c) + int c; +{ + costcount++; + return (c); +} + + static int +cost(t) + char *t; +{ + costcount = 0; + // tputs(t, sc_height, inc_costcount); + inc_costcount(t); // influence of sc_height? + return (costcount); +} + +/* + * Return the "best" of the two given termcap strings. + * The best, if both exist, is the one with the lower + * cost (see cost() function). + */ + static char * +cheaper(t1, t2, def) + char *t1, *t2; + char *def; +{ + if (*t1 == '\0' && *t2 == '\0') + { + missing_cap = 1; + return (def); + } + if (*t1 == '\0') + return (t2); + if (*t2 == '\0') + return (t1); + if (cost(t1) < cost(t2)) + return (t1); + return (t2); +} + + static void +tmodes(incap, outcap, instr, outstr, def_instr, def_outstr, spp) + char *incap; + char *outcap; + char **instr; + char **outstr; + char *def_instr; + char *def_outstr; + char **spp; +{ + *instr = ltgetstr(incap, spp); + if (*instr == NULL) + { + /* Use defaults. */ + *instr = def_instr; + *outstr = def_outstr; + return; + } + + *outstr = ltgetstr(outcap, spp); + if (*outstr == NULL) + /* No specific out capability; use "me". */ + *outstr = ltgetstr("me", spp); + if (*outstr == NULL) + /* Don't even have "me"; use a null string. */ + *outstr = ""; +} + +#endif /* MSDOS_COMPILER */ + + +/* + * Below are the functions which perform all the + * terminal-specific screen manipulation. + */ + + +#if MSDOS_COMPILER + +#if MSDOS_COMPILER==WIN32C + static void +_settextposition(int row, int col) +{ + COORD cpos; + CONSOLE_SCREEN_BUFFER_INFO csbi; + + GetConsoleScreenBufferInfo(con_out, &csbi); + cpos.X = csbi.srWindow.Left + (col - 1); + cpos.Y = csbi.srWindow.Top + (row - 1); + SetConsoleCursorPosition(con_out, cpos); +} +#endif + +/* + * Initialize the screen to the correct color at startup. + */ + static void +initcolor() +{ +#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC + intensevideo(); +#endif + SETCOLORS(nm_fg_color, nm_bg_color); +#if 0 + /* + * This clears the screen at startup. This is different from + * the behavior of other versions of less. Disable it for now. + */ + char *blanks; + int row; + int col; + + /* + * Create a complete, blank screen using "normal" colors. + */ + SETCOLORS(nm_fg_color, nm_bg_color); + blanks = (char *) ecalloc(width+1, sizeof(char)); + for (col = 0; col < sc_width; col++) + blanks[col] = ' '; + blanks[sc_width] = '\0'; + for (row = 0; row < sc_height; row++) + _outtext(blanks); + free(blanks); +#endif +} +#endif + +#if MSDOS_COMPILER==WIN32C + +/* + * Termcap-like init with a private win32 console. + */ + static void +win32_init_term() +{ + CONSOLE_SCREEN_BUFFER_INFO scr; + COORD size; + + if (con_out_save == INVALID_HANDLE_VALUE) + return; + + GetConsoleScreenBufferInfo(con_out_save, &scr); + + if (con_out_ours == INVALID_HANDLE_VALUE) + { + /* + * Create our own screen buffer, so that we + * may restore the original when done. + */ + con_out_ours = CreateConsoleScreenBuffer( + GENERIC_WRITE | GENERIC_READ, + FILE_SHARE_WRITE | FILE_SHARE_READ, + (LPSECURITY_ATTRIBUTES) NULL, + CONSOLE_TEXTMODE_BUFFER, + (LPVOID) NULL); + } + + size.X = scr.srWindow.Right - scr.srWindow.Left + 1; + size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1; + SetConsoleScreenBufferSize(con_out_ours, size); + SetConsoleActiveScreenBuffer(con_out_ours); + con_out = con_out_ours; +} + +/* + * Restore the startup console. + */ +static void +win32_deinit_term() +{ + if (con_out_save == INVALID_HANDLE_VALUE) + return; + if (quitting) + (void) CloseHandle(con_out_ours); + SetConsoleActiveScreenBuffer(con_out_save); + con_out = con_out_save; +} + +#endif + +/* + * Initialize terminal + */ + public void +init() +{ + // iOS: + init_done = 0; +#if !MSDOS_COMPILER + if (!no_init) + // tputs(sc_init, sc_height, putchr); + putstr(sc_init); + if (!no_keypad) + putstr(sc_s_keypad); + // tputs(sc_s_keypad, sc_height, putchr); + if (top_scroll) + { + int i; + + /* + * This is nice to terminals with no alternate screen, + * but with saved scrolled-off-the-top lines. This way, + * no previous line is lost, but we start with a whole + * screen to ourself. + */ + for (i = 1; i < sc_height; i++) + putchr('\n'); + } else + line_left(); +#else +#if MSDOS_COMPILER==WIN32C + if (!no_init) + win32_init_term(); +#endif + initcolor(); + flush(); +#endif + init_done = 1; +} + +/* + * Deinitialize terminal + */ + public void +deinit() +{ + if (!init_done) + return; +#if !MSDOS_COMPILER + if (!no_keypad) + putstr(sc_e_keypad); + // tputs(sc_e_keypad, sc_height, putchr); + if (!no_init) + putstr(sc_deinit); + // tputs(sc_deinit, sc_height, putchr); +#else + /* Restore system colors. */ + SETCOLORS(sy_fg_color, sy_bg_color); +#if MSDOS_COMPILER==WIN32C + if (!no_init) + win32_deinit_term(); +#else + /* Need clreol to make SETCOLORS take effect. */ + clreol(); +#endif +#endif + init_done = 0; +} + +/* + * Home cursor (move to upper left corner of screen). + */ + public void +home() +{ +#if !MSDOS_COMPILER + putstr(sc_home); + // tputs(sc_home, 1, putchr); +#else + flush(); + _settextposition(1,1); +#endif +} + +/* + * Add a blank line (called with cursor at home). + * Should scroll the display down. + */ + public void +add_line() +{ +#if !MSDOS_COMPILER + putstr(sc_addline); + // tputs(sc_addline, sc_height, putchr); +#else + flush(); +#if MSDOS_COMPILER==MSOFTC + _scrolltextwindow(_GSCROLLDOWN); + _settextposition(1,1); +#else +#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC + movetext(1,1, sc_width,sc_height-1, 1,2); + gotoxy(1,1); + clreol(); +#else +#if MSDOS_COMPILER==WIN32C + { + CHAR_INFO fillchar; + SMALL_RECT rcSrc, rcClip; + COORD new_org; + CONSOLE_SCREEN_BUFFER_INFO csbi; + + GetConsoleScreenBufferInfo(con_out,&csbi); + + /* The clip rectangle is the entire visible screen. */ + rcClip.Left = csbi.srWindow.Left; + rcClip.Top = csbi.srWindow.Top; + rcClip.Right = csbi.srWindow.Right; + rcClip.Bottom = csbi.srWindow.Bottom; + + /* The source rectangle is the visible screen minus the last line. */ + rcSrc = rcClip; + rcSrc.Bottom--; + + /* Move the top left corner of the source window down one row. */ + new_org.X = rcSrc.Left; + new_org.Y = rcSrc.Top + 1; + + /* Fill the right character and attributes. */ + fillchar.Char.AsciiChar = ' '; + curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); + fillchar.Attributes = curr_attr; + ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); + _settextposition(1,1); + } +#endif +#endif +#endif +#endif +} + +#if 0 +/* + * Remove the n topmost lines and scroll everything below it in the + * window upward. This is needed to stop leaking the topmost line + * into the scrollback buffer when we go down-one-line (in WIN32). + */ + public void +remove_top(n) + int n; +{ +#if MSDOS_COMPILER==WIN32C + SMALL_RECT rcSrc, rcClip; + CHAR_INFO fillchar; + COORD new_org; + CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */ + + if (n >= sc_height - 1) + { + clear(); + home(); + return; + } + + flush(); + + GetConsoleScreenBufferInfo(con_out, &csbi); + + /* Get the extent of all-visible-rows-but-the-last. */ + rcSrc.Left = csbi.srWindow.Left; + rcSrc.Top = csbi.srWindow.Top + n; + rcSrc.Right = csbi.srWindow.Right; + rcSrc.Bottom = csbi.srWindow.Bottom; + + /* Get the clip rectangle. */ + rcClip.Left = rcSrc.Left; + rcClip.Top = csbi.srWindow.Top; + rcClip.Right = rcSrc.Right; + rcClip.Bottom = rcSrc.Bottom ; + + /* Move the source window up n rows. */ + new_org.X = rcSrc.Left; + new_org.Y = rcSrc.Top - n; + + /* Fill the right character and attributes. */ + fillchar.Char.AsciiChar = ' '; + curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); + fillchar.Attributes = curr_attr; + + ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); + + /* Position cursor on first blank line. */ + goto_line(sc_height - n - 1); +#endif +} +#endif + +#if MSDOS_COMPILER==WIN32C +/* + * Clear the screen. + */ + static void +win32_clear() +{ + /* + * This will clear only the currently visible rows of the NT + * console buffer, which means none of the precious scrollback + * rows are touched making for faster scrolling. Note that, if + * the window has fewer columns than the console buffer (i.e. + * there is a horizontal scrollbar as well), the entire width + * of the visible rows will be cleared. + */ + COORD topleft; + DWORD nchars; + DWORD winsz; + CONSOLE_SCREEN_BUFFER_INFO csbi; + + /* get the number of cells in the current buffer */ + GetConsoleScreenBufferInfo(con_out, &csbi); + winsz = csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1); + topleft.X = 0; + topleft.Y = csbi.srWindow.Top; + + curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); + FillConsoleOutputCharacter(con_out, ' ', winsz, topleft, &nchars); + FillConsoleOutputAttribute(con_out, curr_attr, winsz, topleft, &nchars); +} + +/* + * Remove the n topmost lines and scroll everything below it in the + * window upward. + */ + public void +win32_scroll_up(n) + int n; +{ + SMALL_RECT rcSrc, rcClip; + CHAR_INFO fillchar; + COORD topleft; + COORD new_org; + DWORD nchars; + DWORD size; + CONSOLE_SCREEN_BUFFER_INFO csbi; + + if (n <= 0) + return; + + if (n >= sc_height - 1) + { + win32_clear(); + _settextposition(1,1); + return; + } + + /* Get the extent of what will remain visible after scrolling. */ + GetConsoleScreenBufferInfo(con_out, &csbi); + rcSrc.Left = csbi.srWindow.Left; + rcSrc.Top = csbi.srWindow.Top + n; + rcSrc.Right = csbi.srWindow.Right; + rcSrc.Bottom = csbi.srWindow.Bottom; + + /* Get the clip rectangle. */ + rcClip.Left = rcSrc.Left; + rcClip.Top = csbi.srWindow.Top; + rcClip.Right = rcSrc.Right; + rcClip.Bottom = rcSrc.Bottom ; + + /* Move the source text to the top of the screen. */ + new_org.X = rcSrc.Left; + new_org.Y = rcClip.Top; + + /* Fill the right character and attributes. */ + fillchar.Char.AsciiChar = ' '; + fillchar.Attributes = MAKEATTR(nm_fg_color, nm_bg_color); + + /* Scroll the window. */ + SetConsoleTextAttribute(con_out, fillchar.Attributes); + ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); + + /* Clear remaining lines at bottom. */ + topleft.X = csbi.dwCursorPosition.X; + topleft.Y = rcSrc.Bottom - n; + size = (n * csbi.dwSize.X) + (rcSrc.Right - topleft.X); + FillConsoleOutputCharacter(con_out, ' ', size, topleft, + &nchars); + FillConsoleOutputAttribute(con_out, fillchar.Attributes, size, topleft, + &nchars); + SetConsoleTextAttribute(con_out, curr_attr); + + /* Move cursor n lines up from where it was. */ + csbi.dwCursorPosition.Y -= n; + SetConsoleCursorPosition(con_out, csbi.dwCursorPosition); +} +#endif + +/* + * Move cursor to lower left corner of screen. + */ + public void +lower_left() +{ +#if !MSDOS_COMPILER + putstr(sc_lower_left); + // tputs(sc_lower_left, 1, putchr); +#else + flush(); + _settextposition(sc_height, 1); +#endif +} + +/* + * Move cursor to left position of current line. + */ + public void +line_left() +{ +#if !MSDOS_COMPILER + putstr(sc_return); + // tputs(sc_return, 1, putchr); +#else + int row; + flush(); +#if MSDOS_COMPILER==WIN32C + { + CONSOLE_SCREEN_BUFFER_INFO scr; + GetConsoleScreenBufferInfo(con_out, &scr); + row = scr.dwCursorPosition.Y - scr.srWindow.Top + 1; + } +#else +#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC + row = wherey(); +#else + { + struct rccoord tpos = _gettextposition(); + row = tpos.row; + } +#endif +#endif + _settextposition(row, 1); +#endif +} + +/* + * Check if the console size has changed and reset internals + * (in lieu of SIGWINCH for WIN32). + */ + public void +check_winch() +{ +#if MSDOS_COMPILER==WIN32C + CONSOLE_SCREEN_BUFFER_INFO scr; + COORD size; + + if (con_out == INVALID_HANDLE_VALUE) + return; + + flush(); + GetConsoleScreenBufferInfo(con_out, &scr); + size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1; + size.X = scr.srWindow.Right - scr.srWindow.Left + 1; + if (size.Y != sc_height || size.X != sc_width) + { + sc_height = size.Y; + sc_width = size.X; + if (!no_init && con_out_ours == con_out) + SetConsoleScreenBufferSize(con_out, size); + pos_init(); + wscroll = (sc_height + 1) / 2; + screen_trashed = 1; + } +#endif +} + +/* + * Goto a specific line on the screen. + */ + public void +goto_line(slinenum) + int slinenum; +{ +#if !MSDOS_COMPILER + printf("\033[%d;1H", slinenum+1); + // tputs(tgoto(sc_move, 0, slinenum), 1, putchr); +#else + flush(); + _settextposition(slinenum+1, 1); +#endif +} + +#if MSDOS_COMPILER==MSOFTC || MSDOS_COMPILER==BORLANDC +/* + * Create an alternate screen which is all white. + * This screen is used to create a "flash" effect, by displaying it + * briefly and then switching back to the normal screen. + * {{ Yuck! There must be a better way to get a visual bell. }} + */ + static void +create_flash() +{ +#if MSDOS_COMPILER==MSOFTC + struct videoconfig w; + char *blanks; + int row, col; + + _getvideoconfig(&w); + videopages = w.numvideopages; + if (videopages < 2) + { + at_enter(AT_STANDOUT); + at_exit(); + } else + { + _setactivepage(1); + at_enter(AT_STANDOUT); + blanks = (char *) ecalloc(w.numtextcols, sizeof(char)); + for (col = 0; col < w.numtextcols; col++) + blanks[col] = ' '; + for (row = w.numtextrows; row > 0; row--) + _outmem(blanks, w.numtextcols); + _setactivepage(0); + _setvisualpage(0); + free(blanks); + at_exit(); + } +#else +#if MSDOS_COMPILER==BORLANDC + register int n; + + whitescreen = (unsigned short *) + malloc(sc_width * sc_height * sizeof(short)); + if (whitescreen == NULL) + return; + for (n = 0; n < sc_width * sc_height; n++) + whitescreen[n] = 0x7020; +#else +#if MSDOS_COMPILER==WIN32C + register int n; + + whitescreen = (WORD *) + malloc(sc_height * sc_width * sizeof(WORD)); + if (whitescreen == NULL) + return; + /* Invert the standard colors. */ + for (n = 0; n < sc_width * sc_height; n++) + whitescreen[n] = (WORD)((nm_fg_color << 4) | nm_bg_color); +#endif +#endif +#endif + flash_created = 1; +} +#endif /* MSDOS_COMPILER */ + +/* + * Output the "visual bell", if there is one. + */ + public void +vbell() +{ +#if !MSDOS_COMPILER + if (*sc_visual_bell == '\0') + return; + putstr(sc_visual_bell); + // tputs(sc_visual_bell, sc_height, putchr); +#else +#if MSDOS_COMPILER==DJGPPC + ScreenVisualBell(); +#else +#if MSDOS_COMPILER==MSOFTC + /* + * Create a flash screen on the second video page. + * Switch to that page, then switch back. + */ + if (!flash_created) + create_flash(); + if (videopages < 2) + return; + _setvisualpage(1); + delay(100); + _setvisualpage(0); +#else +#if MSDOS_COMPILER==BORLANDC + unsigned short *currscreen; + + /* + * Get a copy of the current screen. + * Display the flash screen. + * Then restore the old screen. + */ + if (!flash_created) + create_flash(); + if (whitescreen == NULL) + return; + currscreen = (unsigned short *) + malloc(sc_width * sc_height * sizeof(short)); + if (currscreen == NULL) return; + gettext(1, 1, sc_width, sc_height, currscreen); + puttext(1, 1, sc_width, sc_height, whitescreen); + delay(100); + puttext(1, 1, sc_width, sc_height, currscreen); + free(currscreen); +#else +#if MSDOS_COMPILER==WIN32C + /* paint screen with an inverse color */ + clear(); + + /* leave it displayed for 100 msec. */ + Sleep(100); + + /* restore with a redraw */ + repaint(); +#endif +#endif +#endif +#endif +#endif +} + +/* + * Make a noise. + */ + static void +beep() +{ +#if !MSDOS_COMPILER + putchr(CONTROL('G')); +#else +#if MSDOS_COMPILER==WIN32C + MessageBeep(0); +#else + write(1, "\7", 1); +#endif +#endif +} + +/* + * Ring the terminal bell. + */ + public void +bell() +{ + if (quiet == VERY_QUIET) + vbell(); + else + beep(); +} + +/* + * Clear the screen. + */ + public void +clear() +{ +#if !MSDOS_COMPILER + putstr(sc_clear); + // tputs(sc_clear, sc_height, putchr); +#else + flush(); +#if MSDOS_COMPILER==WIN32C + win32_clear(); +#else + _clearscreen(_GCLEARSCREEN); +#endif +#endif +} + +/* + * Clear from the cursor to the end of the cursor's line. + * {{ This must not move the cursor. }} + */ + public void +clear_eol() +{ +#if !MSDOS_COMPILER + putstr(sc_eol_clear); + // tputs(sc_eol_clear, 1, putchr); +#else +#if MSDOS_COMPILER==MSOFTC + short top, left; + short bot, right; + struct rccoord tpos; + + flush(); + /* + * Save current state. + */ + tpos = _gettextposition(); + _gettextwindow(&top, &left, &bot, &right); + /* + * Set a temporary window to the current line, + * from the cursor's position to the right edge of the screen. + * Then clear that window. + */ + _settextwindow(tpos.row, tpos.col, tpos.row, sc_width); + _clearscreen(_GWINDOW); + /* + * Restore state. + */ + _settextwindow(top, left, bot, right); + _settextposition(tpos.row, tpos.col); +#else +#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC + flush(); + clreol(); +#else +#if MSDOS_COMPILER==WIN32C + DWORD nchars; + COORD cpos; + CONSOLE_SCREEN_BUFFER_INFO scr; + + flush(); + memset(&scr, 0, sizeof(scr)); + GetConsoleScreenBufferInfo(con_out, &scr); + cpos.X = scr.dwCursorPosition.X; + cpos.Y = scr.dwCursorPosition.Y; + curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); + FillConsoleOutputAttribute(con_out, curr_attr, + scr.dwSize.X - cpos.X, cpos, &nchars); + FillConsoleOutputCharacter(con_out, ' ', + scr.dwSize.X - cpos.X, cpos, &nchars); +#endif +#endif +#endif +#endif +} + +/* + * Clear the current line. + * Clear the screen if there's off-screen memory below the display. + */ + static void +clear_eol_bot() +{ +#if MSDOS_COMPILER + clear_eol(); +#else + if (below_mem) + putstr(sc_eos_clear); + // tputs(sc_eos_clear, 1, putchr); + else + putstr(sc_eol_clear); + // tputs(sc_eol_clear, 1, putchr); +#endif +} + +/* + * Clear the bottom line of the display. + * Leave the cursor at the beginning of the bottom line. + */ + public void +clear_bot() +{ + /* + * If we're in a non-normal attribute mode, temporarily exit + * the mode while we do the clear. Some terminals fill the + * cleared area with the current attribute. + */ + if (oldbot) + lower_left(); + else + line_left(); + + if (attrmode == AT_NORMAL) + clear_eol_bot(); + else + { + int saved_attrmode = attrmode; + + at_exit(); + clear_eol_bot(); + at_enter(saved_attrmode); + } +} + + public void +at_enter(attr) + int attr; +{ + attr = apply_at_specials(attr); + +#if !MSDOS_COMPILER + /* The one with the most priority is last. */ + if (attr & AT_UNDERLINE) + putstr(sc_u_in); + // tputs(sc_u_in, 1, putchr); + if (attr & AT_BOLD) + putstr(sc_b_in); + // tputs(sc_b_in, 1, putchr); + if (attr & AT_BLINK) + putstr(sc_bl_in); + // tputs(sc_bl_in, 1, putchr); + if (attr & AT_STANDOUT) + putstr(sc_s_in); + // tputs(sc_s_in, 1, putchr); +#else + flush(); + /* The one with the most priority is first. */ + if (attr & AT_STANDOUT) + { + SETCOLORS(so_fg_color, so_bg_color); + } else if (attr & AT_BLINK) + { + SETCOLORS(bl_fg_color, bl_bg_color); + } + else if (attr & AT_BOLD) + { + SETCOLORS(bo_fg_color, bo_bg_color); + } + else if (attr & AT_UNDERLINE) + { + SETCOLORS(ul_fg_color, ul_bg_color); + } +#endif + + attrmode = attr; +} + + public void +at_exit() +{ +#if !MSDOS_COMPILER + /* Undo things in the reverse order we did them. */ + if (attrmode & AT_STANDOUT) + putstr(sc_s_out); + // tputs(sc_s_out, 1, putchr); + if (attrmode & AT_BLINK) + putstr(sc_bl_out); + // tputs(sc_bl_out, 1, putchr); + if (attrmode & AT_BOLD) + putstr(sc_b_out); + // tputs(sc_b_out, 1, putchr); + if (attrmode & AT_UNDERLINE) + putstr(sc_u_out); + // tputs(sc_u_out, 1, putchr); +#else + flush(); + SETCOLORS(nm_fg_color, nm_bg_color); +#endif + + attrmode = AT_NORMAL; +} + + public void +at_switch(attr) + int attr; +{ + int new_attrmode = apply_at_specials(attr); + int ignore_modes = AT_ANSI; + + if ((new_attrmode & ~ignore_modes) != (attrmode & ~ignore_modes)) + { + at_exit(); + at_enter(attr); + } +} + + public int +is_at_equiv(attr1, attr2) + int attr1; + int attr2; +{ + attr1 = apply_at_specials(attr1); + attr2 = apply_at_specials(attr2); + + return (attr1 == attr2); +} + + public int +apply_at_specials(attr) + int attr; +{ + if (attr & AT_BINARY) + attr |= binattr; + if (attr & AT_HILITE) + attr |= AT_STANDOUT; + attr &= ~(AT_BINARY|AT_HILITE); + + return attr; +} + +#if 0 /* No longer used */ +/* + * Erase the character to the left of the cursor + * and move the cursor left. + */ + public void +backspace() +{ +#if !MSDOS_COMPILER + /* + * Erase the previous character by overstriking with a space. + */ + putstr(sc_backspace); + // tputs(sc_backspace, 1, putchr); + putchr(' '); + putstr(sc_backspace); + // tputs(sc_backspace, 1, putchr); +#else +#if MSDOS_COMPILER==MSOFTC + struct rccoord tpos; + + flush(); + tpos = _gettextposition(); + if (tpos.col <= 1) + return; + _settextposition(tpos.row, tpos.col-1); + _outtext(" "); + _settextposition(tpos.row, tpos.col-1); +#else +#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC + cputs("\b"); +#else +#if MSDOS_COMPILER==WIN32C + COORD cpos; + DWORD cChars; + CONSOLE_SCREEN_BUFFER_INFO scr; + + flush(); + GetConsoleScreenBufferInfo(con_out, &scr); + cpos = scr.dwCursorPosition; + if (cpos.X <= 0) + return; + cpos.X--; + SetConsoleCursorPosition(con_out, cpos); + FillConsoleOutputCharacter(con_out, (TCHAR)' ', 1, cpos, &cChars); + SetConsoleCursorPosition(con_out, cpos); +#endif +#endif +#endif +#endif +} +#endif /* 0 */ + +/* + * Output a plain backspace, without erasing the previous char. + */ + public void +putbs() +{ +#if !MSDOS_COMPILER + putstr(sc_backspace); + // tputs(sc_backspace, 1, putchr); +#else + int row, col; + + flush(); + { +#if MSDOS_COMPILER==MSOFTC + struct rccoord tpos; + tpos = _gettextposition(); + row = tpos.row; + col = tpos.col; +#else +#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC + row = wherey(); + col = wherex(); +#else +#if MSDOS_COMPILER==WIN32C + CONSOLE_SCREEN_BUFFER_INFO scr; + GetConsoleScreenBufferInfo(con_out, &scr); + row = scr.dwCursorPosition.Y - scr.srWindow.Top + 1; + col = scr.dwCursorPosition.X - scr.srWindow.Left + 1; +#endif +#endif +#endif + } + if (col <= 1) + return; + _settextposition(row, col-1); +#endif /* MSDOS_COMPILER */ +} + +#if MSDOS_COMPILER==WIN32C +/* + * Determine whether an input character is waiting to be read. + */ + static int +win32_kbhit(tty) + HANDLE tty; +{ + INPUT_RECORD ip; + DWORD read; + + if (keyCount > 0) + return (TRUE); + + currentKey.ascii = 0; + currentKey.scan = 0; + + /* + * Wait for a real key-down event, but + * ignore SHIFT and CONTROL key events. + */ + do + { + PeekConsoleInput(tty, &ip, 1, &read); + if (read == 0) + return (FALSE); + ReadConsoleInput(tty, &ip, 1, &read); + } while (ip.EventType != KEY_EVENT || + ip.Event.KeyEvent.bKeyDown != TRUE || + ip.Event.KeyEvent.wVirtualScanCode == 0 || + ip.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT || + ip.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL || + ip.Event.KeyEvent.wVirtualKeyCode == VK_MENU); + + currentKey.ascii = ip.Event.KeyEvent.uChar.AsciiChar; + currentKey.scan = ip.Event.KeyEvent.wVirtualScanCode; + keyCount = ip.Event.KeyEvent.wRepeatCount; + + if (ip.Event.KeyEvent.dwControlKeyState & + (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) + { + switch (currentKey.scan) + { + case PCK_ALT_E: /* letter 'E' */ + currentKey.ascii = 0; + break; + } + } else if (ip.Event.KeyEvent.dwControlKeyState & + (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) + { + switch (currentKey.scan) + { + case PCK_RIGHT: /* right arrow */ + currentKey.scan = PCK_CTL_RIGHT; + break; + case PCK_LEFT: /* left arrow */ + currentKey.scan = PCK_CTL_LEFT; + break; + case PCK_DELETE: /* delete */ + currentKey.scan = PCK_CTL_DELETE; + break; + } + } else if (ip.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) + { + switch (currentKey.scan) + { + case PCK_SHIFT_TAB: /* tab */ + currentKey.ascii = 0; + break; + } + } + + return (TRUE); +} + +/* + * Read a character from the keyboard. + */ + public char +WIN32getch(tty) + int tty; +{ + int ascii; + + if (pending_scancode) + { + pending_scancode = 0; + return ((char)(currentKey.scan & 0x00FF)); + } + + while (win32_kbhit((HANDLE)tty) == FALSE) + { + Sleep(20); + if (ABORT_SIGS()) + return ('\003'); + continue; + } + keyCount --; + ascii = currentKey.ascii; + /* + * On PC's, the extended keys return a 2 byte sequence beginning + * with '00', so if the ascii code is 00, the next byte will be + * the lsb of the scan code. + */ + pending_scancode = (ascii == 0x00); + return ((char)ascii); +} +#endif + +#if MSDOS_COMPILER +/* + */ + public void +WIN32setcolors(fg, bg) + int fg; + int bg; +{ + SETCOLORS(fg, bg); +} + +/* + */ + public void +WIN32textout(text, len) + char *text; + int len; +{ +#if MSDOS_COMPILER==WIN32C + DWORD written; + WriteConsole(con_out, text, len, &written, NULL); +#else + char c = text[len]; + text[len] = '\0'; + cputs(text); + text[len] = c; +#endif +} +#endif diff --git a/files/Sources/files/less/search.c b/files/Sources/files/less/search.c new file mode 100644 index 00000000..705d9b29 --- /dev/null +++ b/files/Sources/files/less/search.c @@ -0,0 +1,1747 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Routines to search a file for a pattern. + */ + +#include "less.h" +#include "pattern.h" +#include "position.h" +#include "charset.h" + +#define MINPOS(a,b) (((a) < (b)) ? (a) : (b)) +#define MAXPOS(a,b) (((a) > (b)) ? (a) : (b)) + +extern int sigs; +extern int how_search; +extern int caseless; +extern int linenums; +extern int sc_height; +extern int jump_sline; +extern int bs_mode; +extern int less_is_more; +extern int ctldisp; +extern int status_col; +extern void * constant ml_search; +extern POSITION start_attnpos; +extern POSITION end_attnpos; +extern int utf_mode; +extern int screen_trashed; +#if HILITE_SEARCH +extern int hilite_search; +extern int size_linebuf; +extern int squished; +extern int can_goto_line; +static int hide_hilite; +static POSITION prep_startpos; +static POSITION prep_endpos; +static int is_caseless; +static int is_ucase_pattern; + +/* + * Structures for maintaining a set of ranges for hilites and filtered-out + * lines. Each range is stored as a node within a red-black tree, and we + * try to extend existing ranges (without creating overlaps) rather than + * create new nodes if possible. We remember the last node found by a + * search for constant-time lookup if the next search is near enough to + * the previous. To aid that, we overlay a secondary doubly-linked list + * on top of the red-black tree so we can find the preceding/succeeding + * nodes also in constant time. + * + * Each node is allocated from a series of pools, each pool double the size + * of the previous (for amortised constant time allocation). Since our only + * tree operations are clear and node insertion, not node removal, we don't + * need to maintain a usage bitmap or freelist and can just return nodes + * from the pool in-order until capacity is reached. + */ +struct hilite +{ + POSITION hl_startpos; + POSITION hl_endpos; +}; +struct hilite_node +{ + struct hilite_node *parent; + struct hilite_node *left; + struct hilite_node *right; + struct hilite_node *prev; + struct hilite_node *next; + int red; + struct hilite r; +}; +struct hilite_storage +{ + int capacity; + int used; + struct hilite_storage *next; + struct hilite_node *nodes; +}; +struct hilite_tree +{ + struct hilite_storage *first; + struct hilite_storage *current; + struct hilite_node *root; + struct hilite_node *lookaside; +}; +#define HILITE_INITIALIZER() { NULL, NULL, NULL, NULL } +#define HILITE_LOOKASIDE_STEPS 2 + +static struct hilite_tree hilite_anchor = HILITE_INITIALIZER(); +static struct hilite_tree filter_anchor = HILITE_INITIALIZER(); + +#endif + +/* + * These are the static variables that represent the "remembered" + * search pattern and filter pattern. + */ +struct pattern_info { + DEFINE_PATTERN(compiled); + char* text; + int search_type; +}; + +#if NO_REGEX +#define info_compiled(info) ((void*)0) +#else +#define info_compiled(info) ((info)->compiled) +#endif + +static struct pattern_info search_info; +static struct pattern_info filter_info; + +/* + * Are there any uppercase letters in this string? + */ + static int +is_ucase(str) + char *str; +{ + char *str_end = str + strlen(str); + LWCHAR ch; + + while (str < str_end) + { + ch = step_char(&str, +1, str_end); + if (IS_UPPER(ch)) + return (1); + } + return (0); +} + +/* + * Compile and save a search pattern. + */ + static int +set_pattern(info, pattern, search_type) + struct pattern_info *info; + char *pattern; + int search_type; +{ +#if !NO_REGEX + if (pattern == NULL) + CLEAR_PATTERN(info->compiled); + else if (compile_pattern(pattern, search_type, &info->compiled) < 0) + return -1; +#endif + /* Pattern compiled successfully; save the text too. */ + if (info->text != NULL) + free(info->text); + info->text = NULL; + if (pattern != NULL) + { + info->text = (char *) ecalloc(1, strlen(pattern)+1); + strcpy(info->text, pattern); + } + info->search_type = search_type; + + /* + * Ignore case if -I is set OR + * -i is set AND the pattern is all lowercase. + */ + is_ucase_pattern = is_ucase(pattern); + if (is_ucase_pattern && caseless != OPT_ONPLUS) + is_caseless = 0; + else + is_caseless = caseless; + return 0; +} + +/* + * Discard a saved pattern. + */ + static void +clear_pattern(info) + struct pattern_info *info; +{ + if (info->text != NULL) + free(info->text); + info->text = NULL; +#if !NO_REGEX + uncompile_pattern(&info->compiled); +#endif +} + +/* + * Initialize saved pattern to nothing. + */ + static void +init_pattern(info) + struct pattern_info *info; +{ + CLEAR_PATTERN(info->compiled); + info->text = NULL; + info->search_type = 0; +} + +/* + * Initialize search variables. + */ + public void +init_search() +{ + init_pattern(&search_info); + init_pattern(&filter_info); +} + +/* + * Determine which text conversions to perform before pattern matching. + */ + static int +get_cvt_ops() +{ + int ops = 0; + if (is_caseless || bs_mode == BS_SPECIAL) + { + if (is_caseless) + ops |= CVT_TO_LC; + if (bs_mode == BS_SPECIAL) + ops |= CVT_BS; + if (bs_mode != BS_CONTROL) + ops |= CVT_CRLF; + } else if (bs_mode != BS_CONTROL) + { + ops |= CVT_CRLF; + } + if (ctldisp == OPT_ONPLUS) + ops |= CVT_ANSI; + return (ops); +} + +/* + * Is there a previous (remembered) search pattern? + */ + static int +prev_pattern(info) + struct pattern_info *info; +{ +#if !NO_REGEX + if ((info->search_type & SRCH_NO_REGEX) == 0) + return (!is_null_pattern(info->compiled)); +#endif + return (info->text != NULL); +} + +#if HILITE_SEARCH +/* + * Repaint the hilites currently displayed on the screen. + * Repaint each line which contains highlighted text. + * If on==0, force all hilites off. + */ + public void +repaint_hilite(on) + int on; +{ + int slinenum; + POSITION pos; + int save_hide_hilite; + + if (squished) + repaint(); + + save_hide_hilite = hide_hilite; + if (!on) + { + if (hide_hilite) + return; + hide_hilite = 1; + } + + if (!can_goto_line) + { + repaint(); + hide_hilite = save_hide_hilite; + return; + } + + // iOS: need to remove that -1 if we want to repaint the entire screen. + for (slinenum = TOP; slinenum < TOP + sc_height /* -1*/; slinenum++) + { + pos = position(slinenum); + if (pos == NULL_POSITION) + continue; + (void) forw_line(pos); + goto_line(slinenum); + put_line(); + } + lower_left(); + hide_hilite = save_hide_hilite; +} + +/* + * Clear the attn hilite. + */ + public void +clear_attn() +{ + int slinenum; + POSITION old_start_attnpos; + POSITION old_end_attnpos; + POSITION pos; + POSITION epos; + int moved = 0; + + if (start_attnpos == NULL_POSITION) + return; + old_start_attnpos = start_attnpos; + old_end_attnpos = end_attnpos; + start_attnpos = end_attnpos = NULL_POSITION; + + if (!can_goto_line) + { + repaint(); + return; + } + if (squished) + repaint(); + for (slinenum = TOP; slinenum < TOP + sc_height -1; slinenum++) + { + pos = position(slinenum); + if (pos == NULL_POSITION) + continue; + epos = position(slinenum+1); + if (pos < old_end_attnpos && + (epos == NULL_POSITION || epos > old_start_attnpos)) + { + (void) forw_line(pos); + goto_line(slinenum); + put_line(); + moved = 1; + } + } + if (moved) + lower_left(); +} +#endif + +/* + * Hide search string highlighting. + */ + public void +undo_search() +{ + if (!prev_pattern(&search_info)) + { + error("No previous regular expression", NULL_PARG); + return; + } +#if HILITE_SEARCH + hide_hilite = !hide_hilite; + repaint_hilite(1); +#endif +} + +#if HILITE_SEARCH +/* + * Clear the hilite list. + */ + public void +clr_hlist(anchor) + struct hilite_tree *anchor; +{ + struct hilite_storage *hls; + struct hilite_storage *nexthls; + + for (hls = anchor->first; hls != NULL; hls = nexthls) + { + nexthls = hls->next; + free((void*)hls->nodes); + free((void*)hls); + } + anchor->first = NULL; + anchor->current = NULL; + anchor->root = NULL; + + anchor->lookaside = NULL; + + prep_startpos = prep_endpos = NULL_POSITION; +} + + public void +clr_hilite() +{ + clr_hlist(&hilite_anchor); +} + + public void +clr_filter() +{ + clr_hlist(&filter_anchor); +} + + struct hilite_node* +hlist_last(anchor) + struct hilite_tree *anchor; +{ + struct hilite_node *n = anchor->root; + while (n != NULL && n->right != NULL) + n = n->right; + return n; +} + + struct hilite_node* +hlist_next(n) + struct hilite_node *n; +{ + return n->next; +} + + struct hilite_node* +hlist_prev(n) + struct hilite_node *n; +{ + return n->prev; +} + +/* + * Find the node covering pos, or the node after it if no node covers it, + * or return NULL if pos is after the last range. Remember the found node, + * to speed up subsequent searches for the same or similar positions (if + * we return NULL, remember the last node.) + */ + struct hilite_node* +hlist_find(anchor, pos) + struct hilite_tree *anchor; + POSITION pos; +{ + struct hilite_node *n, *m; + + if (anchor->lookaside) + { + int steps = 0; + int hit = 0; + + n = anchor->lookaside; + + for (;;) + { + if (pos < n->r.hl_endpos) + { + if (n->prev == NULL || pos >= n->prev->r.hl_endpos) + { + hit = 1; + break; + } + } else if (n->next == NULL) + { + n = NULL; + hit = 1; + break; + } + + /* + * If we don't find the right node within a small + * distance, don't keep doing a linear search! + */ + if (steps >= HILITE_LOOKASIDE_STEPS) + break; + steps++; + + if (pos < n->r.hl_endpos) + anchor->lookaside = n = n->prev; + else + anchor->lookaside = n = n->next; + } + + if (hit) + return n; + } + + n = anchor->root; + m = NULL; + + while (n != NULL) + { + if (pos < n->r.hl_startpos) + { + if (n->left != NULL) + { + m = n; + n = n->left; + continue; + } + break; + } + if (pos >= n->r.hl_endpos) + { + if (n->right != NULL) + { + n = n->right; + continue; + } + if (m != NULL) + { + n = m; + } else + { + m = n; + n = NULL; + } + } + break; + } + + if (n != NULL) + anchor->lookaside = n; + else if (m != NULL) + anchor->lookaside = m; + + return n; +} + +/* + * Should any characters in a specified range be highlighted? + */ + static int +is_hilited_range(pos, epos) + POSITION pos; + POSITION epos; +{ + struct hilite_node *n = hlist_find(&hilite_anchor, pos); + return (n != NULL && (epos == NULL_POSITION || epos > n->r.hl_startpos)); +} + +/* + * Is a line "filtered" -- that is, should it be hidden? + */ + public int +is_filtered(pos) + POSITION pos; +{ + struct hilite_node *n; + + if (ch_getflags() & CH_HELPFILE) + return (0); + + n = hlist_find(&filter_anchor, pos); + return (n != NULL && pos >= n->r.hl_startpos); +} + +/* + * If pos is hidden, return the next position which isn't, otherwise + * just return pos. + */ + public POSITION +next_unfiltered(pos) + POSITION pos; +{ + struct hilite_node *n; + + if (ch_getflags() & CH_HELPFILE) + return (pos); + + n = hlist_find(&filter_anchor, pos); + while (n != NULL && pos >= n->r.hl_startpos) + { + pos = n->r.hl_endpos; + n = n->next; + } + return (pos); +} + +/* + * If pos is hidden, return the previous position which isn't or 0 if + * we're filtered right to the beginning, otherwise just return pos. + */ + public POSITION +prev_unfiltered(pos) + POSITION pos; +{ + struct hilite_node *n; + + if (ch_getflags() & CH_HELPFILE) + return (pos); + + n = hlist_find(&filter_anchor, pos); + while (n != NULL && pos >= n->r.hl_startpos) + { + pos = n->r.hl_startpos; + if (pos == 0) + break; + pos--; + n = n->prev; + } + return (pos); +} + + +/* + * Should any characters in a specified range be highlighted? + * If nohide is nonzero, don't consider hide_hilite. + */ + public int +is_hilited(pos, epos, nohide, p_matches) + POSITION pos; + POSITION epos; + int nohide; + int *p_matches; +{ + int match; + + if (p_matches != NULL) + *p_matches = 0; + + if (!status_col && + start_attnpos != NULL_POSITION && + pos < end_attnpos && + (epos == NULL_POSITION || epos > start_attnpos)) + /* + * The attn line overlaps this range. + */ + return (1); + + match = is_hilited_range(pos, epos); + if (!match) + return (0); + + if (p_matches != NULL) + /* + * Report matches, even if we're hiding highlights. + */ + *p_matches = 1; + + if (hilite_search == 0) + /* + * Not doing highlighting. + */ + return (0); + + if (!nohide && hide_hilite) + /* + * Highlighting is hidden. + */ + return (0); + + return (1); +} + +/* + * Tree node storage: get the current block of nodes if it has spare + * capacity, or create a new one if not. + */ + static struct hilite_storage* +hlist_getstorage(anchor) + struct hilite_tree *anchor; +{ + int capacity = 1; + struct hilite_storage *s; + + if (anchor->current) + { + if (anchor->current->used < anchor->current->capacity) + return anchor->current; + capacity = anchor->current->capacity * 2; + } + + s = (struct hilite_storage *) ecalloc(1, sizeof(struct hilite_storage)); + s->nodes = (struct hilite_node *) ecalloc(capacity, sizeof(struct hilite_node)); + s->capacity = capacity; + s->used = 0; + s->next = NULL; + if (anchor->current) + anchor->current->next = s; + else + anchor->first = s; + anchor->current = s; + return s; +} + +/* + * Tree node storage: retrieve a new empty node to be inserted into the + * tree. + */ + static struct hilite_node* +hlist_getnode(anchor) + struct hilite_tree *anchor; +{ + struct hilite_storage *s = hlist_getstorage(anchor); + return &s->nodes[s->used++]; +} + +/* + * Rotate the tree left around a pivot node. + */ + static void +hlist_rotate_left(anchor, n) + struct hilite_tree *anchor; + struct hilite_node *n; +{ + struct hilite_node *np = n->parent; + struct hilite_node *nr = n->right; + struct hilite_node *nrl = n->right->left; + + if (np != NULL) + { + if (n == np->left) + np->left = nr; + else + np->right = nr; + } else + { + anchor->root = nr; + } + nr->left = n; + n->right = nrl; + + nr->parent = np; + n->parent = nr; + if (nrl != NULL) + nrl->parent = n; +} + +/* + * Rotate the tree right around a pivot node. + */ + static void +hlist_rotate_right(anchor, n) + struct hilite_tree *anchor; + struct hilite_node *n; +{ + struct hilite_node *np = n->parent; + struct hilite_node *nl = n->left; + struct hilite_node *nlr = n->left->right; + + if (np != NULL) + { + if (n == np->right) + np->right = nl; + else + np->left = nl; + } else + { + anchor->root = nl; + } + nl->right = n; + n->left = nlr; + + nl->parent = np; + n->parent = nl; + if (nlr != NULL) + nlr->parent = n; +} + + +/* + * Add a new hilite to a hilite list. + */ + static void +add_hilite(anchor, hl) + struct hilite_tree *anchor; + struct hilite *hl; +{ + struct hilite_node *p, *n, *u; + + /* Ignore empty ranges. */ + if (hl->hl_startpos >= hl->hl_endpos) + return; + + p = anchor->root; + + /* Inserting the very first node is trivial. */ + if (p == NULL) + { + n = hlist_getnode(anchor); + n->r = *hl; + anchor->root = n; + anchor->lookaside = n; + return; + } + + /* + * Find our insertion point. If we come across any overlapping + * or adjoining existing ranges, shrink our range and discard + * if it become empty. + */ + for (;;) + { + if (hl->hl_startpos < p->r.hl_startpos) + { + if (hl->hl_endpos > p->r.hl_startpos) + hl->hl_endpos = p->r.hl_startpos; + if (p->left != NULL) + { + p = p->left; + continue; + } + break; + } + if (hl->hl_startpos < p->r.hl_endpos) { + hl->hl_startpos = p->r.hl_endpos; + if (hl->hl_startpos >= hl->hl_endpos) + return; + } + if (p->right != NULL) + { + p = p->right; + continue; + } + break; + } + + /* + * Now we're at the right leaf, again check for contiguous ranges + * and extend the existing node if possible to avoid the + * insertion. Otherwise insert a new node at the leaf. + */ + if (hl->hl_startpos < p->r.hl_startpos) { + if (hl->hl_endpos == p->r.hl_startpos) + { + p->r.hl_startpos = hl->hl_startpos; + return; + } + if (p->prev != NULL && p->prev->r.hl_endpos == hl->hl_startpos) + { + p->prev->r.hl_endpos = hl->hl_endpos; + return; + } + + p->left = n = hlist_getnode(anchor); + n->next = p; + if (p->prev != NULL) + { + n->prev = p->prev; + p->prev->next = n; + } + p->prev = n; + } else { + if (p->r.hl_endpos == hl->hl_startpos) + { + p->r.hl_endpos = hl->hl_endpos; + return; + } + if (p->next != NULL && hl->hl_endpos == p->next->r.hl_startpos) { + p->next->r.hl_startpos = hl->hl_startpos; + return; + } + + p->right = n = hlist_getnode(anchor); + n->prev = p; + if (p->next != NULL) + { + n->next = p->next; + p->next->prev = n; + } + p->next = n; + } + n->parent = p; + n->red = 1; + n->r = *hl; + + /* + * The tree is in the correct order and covers the right ranges + * now, but may have become unbalanced. Rebalance it using the + * standard red-black tree constraints and operations. + */ + for (;;) + { + /* case 1 - current is root, root is always black */ + if (n->parent == NULL) + { + n->red = 0; + break; + } + + /* case 2 - parent is black, we can always be red */ + if (!n->parent->red) + break; + + /* + * constraint: because the root must be black, if our + * parent is red it cannot be the root therefore we must + * have a grandparent + */ + + /* + * case 3 - parent and uncle are red, repaint them black, + * the grandparent red, and start again at the grandparent. + */ + u = n->parent->parent->left; + if (n->parent == u) + u = n->parent->parent->right; + if (u != NULL && u->red) + { + n->parent->red = 0; + u->red = 0; + n = n->parent->parent; + n->red = 1; + continue; + } + + /* + * case 4 - parent is red but uncle is black, parent and + * grandparent on opposite sides. We need to start + * changing the structure now. This and case 5 will shorten + * our branch and lengthen the sibling, between them + * restoring balance. + */ + if (n == n->parent->right && + n->parent == n->parent->parent->left) + { + hlist_rotate_left(anchor, n->parent); + n = n->left; + } else if (n == n->parent->left && + n->parent == n->parent->parent->right) + { + hlist_rotate_right(anchor, n->parent); + n = n->right; + } + + /* + * case 5 - parent is red but uncle is black, parent and + * grandparent on same side + */ + n->parent->red = 0; + n->parent->parent->red = 1; + if (n == n->parent->left) + hlist_rotate_right(anchor, n->parent->parent); + else + hlist_rotate_left(anchor, n->parent->parent); + break; + } +} + +/* + * Hilight every character in a range of displayed characters. + */ + static void +create_hilites(linepos, start_index, end_index, chpos) + POSITION linepos; + int start_index; + int end_index; + int *chpos; +{ + struct hilite hl; + int i; + + /* Start the first hilite. */ + hl.hl_startpos = linepos + chpos[start_index]; + + /* + * Step through the displayed chars. + * If the source position (before cvt) of the char is one more + * than the source pos of the previous char (the usual case), + * just increase the size of the current hilite by one. + * Otherwise (there are backspaces or something involved), + * finish the current hilite and start a new one. + */ + for (i = start_index+1; i <= end_index; i++) + { + if (chpos[i] != chpos[i-1] + 1 || i == end_index) + { + hl.hl_endpos = linepos + chpos[i-1] + 1; + add_hilite(&hilite_anchor, &hl); + /* Start new hilite unless this is the last char. */ + if (i < end_index) + { + hl.hl_startpos = linepos + chpos[i]; + } + } + } +} + +/* + * Make a hilite for each string in a physical line which matches + * the current pattern. + * sp,ep delimit the first match already found. + */ + static void +hilite_line(linepos, line, line_len, chpos, sp, ep, cvt_ops) + POSITION linepos; + char *line; + int line_len; + int *chpos; + char *sp; + char *ep; + int cvt_ops; +{ + char *searchp; + char *line_end = line + line_len; + + /* + * sp and ep delimit the first match in the line. + * Mark the corresponding file positions, then + * look for further matches and mark them. + * {{ This technique, of calling match_pattern on subsequent + * substrings of the line, may mark more than is correct + * if the pattern starts with "^". This bug is fixed + * for those regex functions that accept a notbol parameter + * (currently POSIX, PCRE and V8-with-regexec2). }} + */ + searchp = line; + do { + if (sp == NULL || ep == NULL) + return; + create_hilites(linepos, sp-line, ep-line, chpos); + /* + * If we matched more than zero characters, + * move to the first char after the string we matched. + * If we matched zero, just move to the next char. + */ + if (ep > searchp) + searchp = ep; + else if (searchp != line_end) + searchp++; + else /* end of line */ + break; + } while (match_pattern(info_compiled(&search_info), search_info.text, + searchp, line_end - searchp, &sp, &ep, 1, search_info.search_type)); +} +#endif + +#if HILITE_SEARCH +/* + * Find matching text which is currently on screen and highlight it. + */ + static void +hilite_screen() +{ + struct scrpos scrpos; + + get_scrpos(&scrpos); + if (scrpos.pos == NULL_POSITION) + return; + prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1); + repaint_hilite(1); +} + +/* + * Change highlighting parameters. + */ + public void +chg_hilite() +{ + /* + * Erase any highlights currently on screen. + */ + clr_hilite(); + hide_hilite = 0; + + if (hilite_search == OPT_ONPLUS) + /* + * Display highlights. + */ + hilite_screen(); +} +#endif + +/* + * Figure out where to start a search. + */ + static POSITION +search_pos(search_type) + int search_type; +{ + POSITION pos; + int linenum; + + if (empty_screen()) + { + /* + * Start at the beginning (or end) of the file. + * The empty_screen() case is mainly for + * command line initiated searches; + * for example, "+/xyz" on the command line. + * Also for multi-file (SRCH_PAST_EOF) searches. + */ + if (search_type & SRCH_FORW) + { + pos = ch_zero(); + } else + { + pos = ch_length(); + if (pos == NULL_POSITION) + { + (void) ch_end_seek(); + pos = ch_length(); + } + } + linenum = 0; + } else + { + int add_one = 0; + + if (how_search == OPT_ON) + { + /* + * Search does not include current screen. + */ + if (search_type & SRCH_FORW) + linenum = sc_height-1; /* BOTTOM_PLUS_ONE */ + else + linenum = 0; /* TOP */ + } else if (how_search == OPT_ONPLUS && !(search_type & SRCH_AFTER_TARGET)) + { + /* + * Search includes all of displayed screen. + */ + if (search_type & SRCH_FORW) + linenum = 0; /* TOP */ + else + linenum = sc_height-1; /* BOTTOM_PLUS_ONE */ + } else + { + /* + * Search includes the part of current screen beyond the jump target. + * It starts at the jump target (if searching backwards), + * or at the jump target plus one (if forwards). + */ + linenum = adjsline(jump_sline); + if (search_type & SRCH_FORW) + add_one = 1; + } + pos = position(linenum); + if (add_one) + pos = forw_raw_line(pos, (char **)NULL, (int *)NULL); + } + + /* + * If the line is empty, look around for a plausible starting place. + */ + if (search_type & SRCH_FORW) + { + while (pos == NULL_POSITION) + { + if (++linenum >= sc_height) + break; + pos = position(linenum); + } + } else + { + while (pos == NULL_POSITION) + { + if (--linenum < 0) + break; + pos = position(linenum); + } + } + return (pos); +} + +/* + * Search a subset of the file, specified by start/end position. + */ + static int +search_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos) + POSITION pos; + POSITION endpos; + int search_type; + int matches; + int maxlines; + POSITION *plinepos; + POSITION *pendpos; +{ + char *line; + char *cline; + int line_len; + LINENUM linenum; + char *sp, *ep; + int line_match; + int cvt_ops; + int cvt_len; + int *chpos; + POSITION linepos, oldpos; + + linenum = find_linenum(pos); + oldpos = pos; + for (;;) + { + /* + * Get lines until we find a matching one or until + * we hit end-of-file (or beginning-of-file if we're + * going backwards), or until we hit the end position. + */ + if (ABORT_SIGS()) + { + /* + * A signal aborts the search. + */ + return (-1); + } + + if ((endpos != NULL_POSITION && pos >= endpos) || maxlines == 0) + { + /* + * Reached end position without a match. + */ + if (pendpos != NULL) + *pendpos = pos; + return (matches); + } + if (maxlines > 0) + maxlines--; + + if (search_type & SRCH_FORW) + { + /* + * Read the next line, and save the + * starting position of that line in linepos. + */ + linepos = pos; + pos = forw_raw_line(pos, &line, &line_len); + if (linenum != 0) + linenum++; + } else + { + /* + * Read the previous line and save the + * starting position of that line in linepos. + */ + pos = back_raw_line(pos, &line, &line_len); + linepos = pos; + if (linenum != 0) + linenum--; + } + + if (pos == NULL_POSITION) + { + /* + * Reached EOF/BOF without a match. + */ + if (pendpos != NULL) + *pendpos = oldpos; + return (matches); + } + + /* + * If we're using line numbers, we might as well + * remember the information we have now (the position + * and line number of the current line). + * Don't do it for every line because it slows down + * the search. Remember the line number only if + * we're "far" from the last place we remembered it. + */ + if (linenums && abs((int)(pos - oldpos)) > 2048) + add_lnum(linenum, pos); + oldpos = pos; + + if (is_filtered(linepos)) + continue; + + /* + * If it's a caseless search, convert the line to lowercase. + * If we're doing backspace processing, delete backspaces. + */ + cvt_ops = get_cvt_ops(); + cvt_len = cvt_length(line_len, cvt_ops); + cline = (char *) ecalloc(1, cvt_len); + chpos = cvt_alloc_chpos(cvt_len); + cvt_text(cline, line, chpos, &line_len, cvt_ops); + +#if HILITE_SEARCH + /* + * Check to see if the line matches the filter pattern. + * If so, add an entry to the filter list. + */ + if (((search_type & SRCH_FIND_ALL) || + prep_startpos == NULL_POSITION || + linepos < prep_startpos || linepos >= prep_endpos) && + prev_pattern(&filter_info)) { + int line_filter = match_pattern(info_compiled(&filter_info), filter_info.text, + cline, line_len, &sp, &ep, 0, filter_info.search_type); + if (line_filter) + { + struct hilite hl; + hl.hl_startpos = linepos; + hl.hl_endpos = pos; + add_hilite(&filter_anchor, &hl); + continue; + } + } +#endif + + /* + * Test the next line to see if we have a match. + * We are successful if we either want a match and got one, + * or if we want a non-match and got one. + */ + if (prev_pattern(&search_info)) + { + line_match = match_pattern(info_compiled(&search_info), search_info.text, + cline, line_len, &sp, &ep, 0, search_type); + if (line_match) + { + /* + * Got a match. + */ + if (search_type & SRCH_FIND_ALL) + { +#if HILITE_SEARCH + /* + * We are supposed to find all matches in the range. + * Just add the matches in this line to the + * hilite list and keep searching. + */ + hilite_line(linepos, cline, line_len, chpos, sp, ep, cvt_ops); +#endif + } else if (--matches <= 0) + { + /* + * Found the one match we're looking for. + * Return it. + */ +#if HILITE_SEARCH + if (hilite_search == OPT_ON) + { + /* + * Clear the hilite list and add only + * the matches in this one line. + */ + clr_hilite(); + hilite_line(linepos, cline, line_len, chpos, sp, ep, cvt_ops); + } +#endif + free(cline); + free(chpos); + if (plinepos != NULL) + *plinepos = linepos; + return (0); + } + } + } + free(cline); + free(chpos); + } +} + +/* + * search for a pattern in history. If found, compile that pattern. + */ + static int +hist_pattern(search_type) + int search_type; +{ +#if CMD_HISTORY + char *pattern; + + set_mlist(ml_search, 0); + pattern = cmd_lastpattern(); + if (pattern == NULL) + return (0); + + if (set_pattern(&search_info, pattern, search_type) < 0) + return (0); + +#if HILITE_SEARCH + if (hilite_search == OPT_ONPLUS && !hide_hilite) + hilite_screen(); +#endif + + return (1); +#else /* CMD_HISTORY */ + return (0); +#endif /* CMD_HISTORY */ +} + +/* + * Change the caseless-ness of searches. + * Updates the internal search state to reflect a change in the -i flag. + */ + public void +chg_caseless() +{ + if (!is_ucase_pattern) + /* + * Pattern did not have uppercase. + * Just set the search caselessness to the global caselessness. + */ + is_caseless = caseless; + else + { + /* + * Pattern did have uppercase. + * Regenerate the pattern using the new state. + */ + clear_pattern(&search_info); + hist_pattern(search_info.search_type); + } +} + +/* + * Search for the n-th occurrence of a specified pattern, + * either forward or backward. + * Return the number of matches not yet found in this file + * (that is, n minus the number of matches found). + * Return -1 if the search should be aborted. + * Caller may continue the search in another file + * if less than n matches are found in this file. + */ + public int +search(search_type, pattern, n) + int search_type; + char *pattern; + int n; +{ + POSITION pos; + + if (pattern == NULL || *pattern == '\0') + { + /* + * A null pattern means use the previously compiled pattern. + */ + search_type |= SRCH_AFTER_TARGET; + if (!prev_pattern(&search_info) && !hist_pattern(search_type)) + { + error("No previous regular expression", NULL_PARG); + return (-1); + } + if ((search_type & SRCH_NO_REGEX) != + (search_info.search_type & SRCH_NO_REGEX)) + { + error("Please re-enter search pattern", NULL_PARG); + return -1; + } +#if HILITE_SEARCH + if (hilite_search == OPT_ON) + { + /* + * Erase the highlights currently on screen. + * If the search fails, we'll redisplay them later. + */ + repaint_hilite(0); + } + if (hilite_search == OPT_ONPLUS && hide_hilite) + { + /* + * Highlight any matches currently on screen, + * before we actually start the search. + */ + hide_hilite = 0; + hilite_screen(); + } + hide_hilite = 0; +#endif + } else + { + /* + * Compile the pattern. + */ + if (set_pattern(&search_info, pattern, search_type) < 0) + return (-1); +#if HILITE_SEARCH + if (hilite_search) + { + /* + * Erase the highlights currently on screen. + * Also permanently delete them from the hilite list. + */ + repaint_hilite(0); + hide_hilite = 0; + clr_hilite(); + } + if (hilite_search == OPT_ONPLUS) + { + /* + * Highlight any matches currently on screen, + * before we actually start the search. + */ + hilite_screen(); + } +#endif + } + + /* + * Figure out where to start the search. + */ + pos = search_pos(search_type); + if (pos == NULL_POSITION) + { + /* + * Can't find anyplace to start searching from. + */ + if (search_type & SRCH_PAST_EOF) + return (n); + /* repaint(); -- why was this here? */ + error("Nothing to search", NULL_PARG); + return (-1); + } + + n = search_range(pos, NULL_POSITION, search_type, n, -1, + &pos, (POSITION*)NULL); + if (n != 0) + { + /* + * Search was unsuccessful. + */ +#if HILITE_SEARCH + if (hilite_search == OPT_ON && n > 0) + /* + * Redisplay old hilites. + */ + repaint_hilite(1); +#endif + return (n); + } + + if (!(search_type & SRCH_NO_MOVE)) + { + /* + * Go to the matching line. + */ + jump_loc(pos, jump_sline); + } + +#if HILITE_SEARCH + if (hilite_search == OPT_ON) + /* + * Display new hilites in the matching line. + */ + repaint_hilite(1); +#endif + return (0); +} + + +#if HILITE_SEARCH +/* + * Prepare hilites in a given range of the file. + * + * The pair (prep_startpos,prep_endpos) delimits a contiguous region + * of the file that has been "prepared"; that is, scanned for matches for + * the current search pattern, and hilites have been created for such matches. + * If prep_startpos == NULL_POSITION, the prep region is empty. + * If prep_endpos == NULL_POSITION, the prep region extends to EOF. + * prep_hilite asks that the range (spos,epos) be covered by the prep region. + */ + public void +prep_hilite(spos, epos, maxlines) + POSITION spos; + POSITION epos; + int maxlines; +{ + POSITION nprep_startpos = prep_startpos; + POSITION nprep_endpos = prep_endpos; + POSITION new_epos; + POSITION max_epos; + int result; + int i; + +/* + * Search beyond where we're asked to search, so the prep region covers + * more than we need. Do one big search instead of a bunch of small ones. + */ +#define SEARCH_MORE (3*size_linebuf) + + if (!prev_pattern(&search_info) && !is_filtering()) + return; + + /* + * Make sure our prep region always starts at the beginning of + * a line. (search_range takes care of the end boundary below.) + */ + spos = back_raw_line(spos+1, (char **)NULL, (int *)NULL); + + /* + * If we're limited to a max number of lines, figure out the + * file position we should stop at. + */ + if (maxlines < 0) + max_epos = NULL_POSITION; + else + { + max_epos = spos; + for (i = 0; i < maxlines; i++) + max_epos = forw_raw_line(max_epos, (char **)NULL, (int *)NULL); + } + + /* + * Find two ranges: + * The range that we need to search (spos,epos); and the range that + * the "prep" region will then cover (nprep_startpos,nprep_endpos). + */ + + if (prep_startpos == NULL_POSITION || + (epos != NULL_POSITION && epos < prep_startpos) || + spos > prep_endpos) + { + /* + * New range is not contiguous with old prep region. + * Discard the old prep region and start a new one. + */ + clr_hilite(); + clr_filter(); + if (epos != NULL_POSITION) + epos += SEARCH_MORE; + nprep_startpos = spos; + } else + { + /* + * New range partially or completely overlaps old prep region. + */ + if (epos == NULL_POSITION) + { + /* + * New range goes to end of file. + */ + ; + } else if (epos > prep_endpos) + { + /* + * New range ends after old prep region. + * Extend prep region to end at end of new range. + */ + epos += SEARCH_MORE; + } else /* (epos <= prep_endpos) */ + { + /* + * New range ends within old prep region. + * Truncate search to end at start of old prep region. + */ + epos = prep_startpos; + } + + if (spos < prep_startpos) + { + /* + * New range starts before old prep region. + * Extend old prep region backwards to start at + * start of new range. + */ + if (spos < SEARCH_MORE) + spos = 0; + else + spos -= SEARCH_MORE; + nprep_startpos = spos; + } else /* (spos >= prep_startpos) */ + { + /* + * New range starts within or after old prep region. + * Trim search to start at end of old prep region. + */ + spos = prep_endpos; + } + } + + if (epos != NULL_POSITION && max_epos != NULL_POSITION && + epos > max_epos) + /* + * Don't go past the max position we're allowed. + */ + epos = max_epos; + + if (epos == NULL_POSITION || epos > spos) + { + int search_type = SRCH_FORW | SRCH_FIND_ALL; + search_type |= (search_info.search_type & SRCH_NO_REGEX); + for (;;) + { + result = search_range(spos, epos, search_type, 0, maxlines, (POSITION*)NULL, &new_epos); + if (result < 0) + return; + if (prep_endpos == NULL_POSITION || new_epos > prep_endpos) + nprep_endpos = new_epos; + + /* + * Check both ends of the resulting prep region to + * make sure they're not filtered. If they are, + * keep going at least one more line until we find + * something that isn't filtered, or hit the end. + */ + if (prep_endpos == NULL_POSITION || nprep_endpos > prep_endpos) + { + if (new_epos >= nprep_endpos && is_filtered(new_epos-1)) + { + spos = nprep_endpos; + epos = forw_raw_line(nprep_endpos, (char **)NULL, (int *)NULL); + if (epos == NULL_POSITION) + break; + maxlines = 1; + continue; + } + } + + if (prep_startpos == NULL_POSITION || nprep_startpos < prep_startpos) + { + if (nprep_startpos > 0 && is_filtered(nprep_startpos)) + { + epos = nprep_startpos; + spos = back_raw_line(nprep_startpos, (char **)NULL, (int *)NULL); + if (spos == NULL_POSITION) + break; + nprep_startpos = spos; + maxlines = 1; + continue; + } + } + break; + } + } + prep_startpos = nprep_startpos; + prep_endpos = nprep_endpos; +} + +/* + * Set the pattern to be used for line filtering. + */ + public void +set_filter_pattern(pattern, search_type) + char *pattern; + int search_type; +{ + clr_filter(); + if (pattern == NULL || *pattern == '\0') + clear_pattern(&filter_info); + else + set_pattern(&filter_info, pattern, search_type); + screen_trashed = 1; +} + +/* + * Is there a line filter in effect? + */ + public int +is_filtering() +{ + if (ch_getflags() & CH_HELPFILE) + return (0); + return prev_pattern(&filter_info); +} +#endif + +#if HAVE_V8_REGCOMP +/* + * This function is called by the V8 regcomp to report + * errors in regular expressions. + */ +public int reg_show_error = 1; + + void +regerror(s) + char *s; +{ + PARG parg; + + if (!reg_show_error) + return; + parg.p_string = s; + error("%s", &parg); +} +#endif + diff --git a/files/Sources/files/less/signal.c b/files/Sources/files/less/signal.c new file mode 100644 index 00000000..ff465198 --- /dev/null +++ b/files/Sources/files/less/signal.c @@ -0,0 +1,259 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Routines dealing with signals. + * + * A signal usually merely causes a bit to be set in the "signals" word. + * At some convenient time, the mainline code checks to see if any + * signals need processing by calling psignal(). + * If we happen to be reading from a file [in iread()] at the time + * the signal is received, we call intread to interrupt the iread. + */ + +#include "less.h" +#include + +/* + * "sigs" contains bits indicating signals which need to be processed. + */ +public int sigs; + +extern int sc_width, sc_height; +extern int screen_trashed; +extern int lnloop; +extern int linenums; +extern int wscroll; +extern int reading; +extern int quit_on_intr; +extern int less_is_more; +extern long jump_sline_fraction; + +/* + * Interrupt signal handler. + */ + /* ARGSUSED*/ + static RETSIGTYPE +u_interrupt(type) + int type; +{ + bell(); +#if OS2 + LSIGNAL(SIGINT, SIG_ACK); +#endif + LSIGNAL(SIGINT, u_interrupt); + sigs |= S_INTERRUPT; +#if MSDOS_COMPILER==DJGPPC + /* + * If a keyboard has been hit, it must be Ctrl-C + * (as opposed to Ctrl-Break), so consume it. + * (Otherwise, Less will beep when it sees Ctrl-C from keyboard.) + */ + if (kbhit()) + getkey(); +#endif + if (less_is_more) + quit(0); + if (reading) + intread(); /* May longjmp */ +} + +#ifdef SIGTSTP +/* + * "Stop" (^Z) signal handler. + */ + /* ARGSUSED*/ + static RETSIGTYPE +stop(type) + int type; +{ + LSIGNAL(SIGTSTP, stop); + sigs |= S_STOP; + if (reading) + intread(); +} +#endif + +#ifdef SIGWINCH +/* + * "Window" change handler + */ + /* ARGSUSED*/ + public RETSIGTYPE +winch(type) + int type; +{ + LSIGNAL(SIGWINCH, winch); + sigs |= S_WINCH; + if (reading) + intread(); +} +#else +#ifdef SIGWIND +/* + * "Window" change handler + */ + /* ARGSUSED*/ + public RETSIGTYPE +winch(type) + int type; +{ + LSIGNAL(SIGWIND, winch); + sigs |= S_WINCH; + if (reading) + intread(); +} +#endif +#endif + +#if MSDOS_COMPILER==WIN32C +/* + * Handle CTRL-C and CTRL-BREAK keys. + */ +#include "windows.h" + + static BOOL WINAPI +wbreak_handler(dwCtrlType) + DWORD dwCtrlType; +{ + switch (dwCtrlType) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + sigs |= S_INTERRUPT; + return (TRUE); + default: + break; + } + return (FALSE); +} +#endif + +/* + * Set up the signal handlers. + */ + public void +init_signals(on) + int on; +{ + if (on) + { + /* + * Set signal handlers. + */ + (void) LSIGNAL(SIGINT, u_interrupt); +#if MSDOS_COMPILER==WIN32C + SetConsoleCtrlHandler(wbreak_handler, TRUE); +#endif +#ifdef SIGTSTP + (void) LSIGNAL(SIGTSTP, stop); +#endif +#ifdef SIGWINCH + (void) LSIGNAL(SIGWINCH, winch); +#endif +#ifdef SIGWIND + (void) LSIGNAL(SIGWIND, winch); +#endif +#ifdef SIGQUIT + (void) LSIGNAL(SIGQUIT, SIG_IGN); +#endif + } else + { + /* + * Restore signals to defaults. + */ + (void) LSIGNAL(SIGINT, SIG_DFL); +#if MSDOS_COMPILER==WIN32C + SetConsoleCtrlHandler(wbreak_handler, FALSE); +#endif +#ifdef SIGTSTP + (void) LSIGNAL(SIGTSTP, SIG_DFL); +#endif +#ifdef SIGWINCH + (void) LSIGNAL(SIGWINCH, SIG_IGN); +#endif +#ifdef SIGWIND + (void) LSIGNAL(SIGWIND, SIG_IGN); +#endif +#ifdef SIGQUIT + (void) LSIGNAL(SIGQUIT, SIG_DFL); +#endif + } +} + +/* + * Process any signals we have received. + * A received signal cause a bit to be set in "sigs". + */ + public void +psignals() +{ + register int tsignals; + + if ((tsignals = sigs) == 0) + return; + sigs = 0; + +#ifdef SIGTSTP + if (tsignals & S_STOP) + { + /* + * Clean up the terminal. + */ +#ifdef SIGTTOU + LSIGNAL(SIGTTOU, SIG_IGN); +#endif + clear_bot(); + deinit(); + flush(); + raw_mode(0); +#ifdef SIGTTOU + LSIGNAL(SIGTTOU, SIG_DFL); +#endif + LSIGNAL(SIGTSTP, SIG_DFL); + kill(getpid(), SIGTSTP); + /* + * ... Bye bye. ... + * Hopefully we'll be back later and resume here... + * Reset the terminal and arrange to repaint the + * screen when we get back to the main command loop. + */ + LSIGNAL(SIGTSTP, stop); + raw_mode(1); + init(); + screen_trashed = 1; + tsignals |= S_WINCH; + } +#endif +#ifdef S_WINCH + if (tsignals & S_WINCH) + { + int old_width, old_height; + /* + * Re-execute scrsize() to read the new window size. + */ + old_width = sc_width; + old_height = sc_height; + get_term(); + if (sc_width != old_width || sc_height != old_height) + { + wscroll = (sc_height + 1) / 2; + calc_jump_sline(); + calc_shift_count(); + screen_trashed = 1; + } + } +#endif + if (tsignals & S_INTERRUPT) + { + if (quit_on_intr) + quit(QUIT_INTERRUPT); + } +} diff --git a/files/Sources/files/less/tableinit.h b/files/Sources/files/less/tableinit.h new file mode 100644 index 00000000..6e047d1c --- /dev/null +++ b/files/Sources/files/less/tableinit.h @@ -0,0 +1,177 @@ +static unsigned char cmdtable_init[] = +{ + '\r',0, A_F_LINE, + '\n',0, A_F_LINE, + 'e',0, A_F_LINE, + 'j',0, A_F_LINE, + SK(SK_DOWN_ARROW),0, A_F_LINE, + CONTROL('E'),0, A_F_LINE, + CONTROL('N'),0, A_F_LINE, + 'k',0, A_B_LINE, + 'y',0, A_B_LINE, + CONTROL('Y'),0, A_B_LINE, + SK(SK_CONTROL_K),0, A_B_LINE, + CONTROL('P'),0, A_B_LINE, + SK(SK_UP_ARROW),0, A_B_LINE, + 'J',0, A_FF_LINE, + 'K',0, A_BF_LINE, + 'Y',0, A_BF_LINE, + 'd',0, A_F_SCROLL, + CONTROL('D'),0, A_F_SCROLL, + 'u',0, A_B_SCROLL, + CONTROL('U'),0, A_B_SCROLL, + ' ',0, A_F_SCREEN, + 'f',0, A_F_SCREEN, + CONTROL('F'),0, A_F_SCREEN, + CONTROL('V'),0, A_F_SCREEN, + SK(SK_PAGE_DOWN),0, A_F_SCREEN, + 'b',0, A_B_SCREEN, + CONTROL('B'),0, A_B_SCREEN, + ESC,'v',0, A_B_SCREEN, + SK(SK_PAGE_UP),0, A_B_SCREEN, + 'z',0, A_F_WINDOW, + 'w',0, A_B_WINDOW, + ESC,' ',0, A_FF_SCREEN, + 'F',0, A_F_FOREVER, + ESC,'F',0, A_F_UNTIL_HILITE, + 'R',0, A_FREPAINT, + 'r',0, A_REPAINT, + CONTROL('R'),0, A_REPAINT, + CONTROL('L'),0, A_REPAINT, + ESC,'u',0, A_UNDO_SEARCH, + 'g',0, A_GOLINE, + SK(SK_HOME),0, A_GOLINE, + '<',0, A_GOLINE, + ESC,'<',0, A_GOLINE, + 'p',0, A_PERCENT, + '%',0, A_PERCENT, + ESC,'[',0, A_LSHIFT, + ESC,']',0, A_RSHIFT, + ESC,'(',0, A_LSHIFT, + ESC,')',0, A_RSHIFT, + ESC,'{',0, A_LLSHIFT, + ESC,'}',0, A_RRSHIFT, + SK(SK_RIGHT_ARROW),0, A_RSHIFT, + SK(SK_LEFT_ARROW),0, A_LSHIFT, + SK(SK_CTL_RIGHT_ARROW),0, A_RRSHIFT, + SK(SK_CTL_LEFT_ARROW),0, A_LLSHIFT, + '{',0, A_F_BRACKET|A_EXTRA, '{','}',0, + '}',0, A_B_BRACKET|A_EXTRA, '{','}',0, + '(',0, A_F_BRACKET|A_EXTRA, '(',')',0, + ')',0, A_B_BRACKET|A_EXTRA, '(',')',0, + '[',0, A_F_BRACKET|A_EXTRA, '[',']',0, + ']',0, A_B_BRACKET|A_EXTRA, '[',']',0, + ESC,CONTROL('F'),0, A_F_BRACKET, + ESC,CONTROL('B'),0, A_B_BRACKET, + 'G',0, A_GOEND, + ESC,'G',0, A_GOEND_BUF, + ESC,'>',0, A_GOEND, + '>',0, A_GOEND, + SK(SK_END),0, A_GOEND, + 'P',0, A_GOPOS, + + '0',0, A_DIGIT, + '1',0, A_DIGIT, + '2',0, A_DIGIT, + '3',0, A_DIGIT, + '4',0, A_DIGIT, + '5',0, A_DIGIT, + '6',0, A_DIGIT, + '7',0, A_DIGIT, + '8',0, A_DIGIT, + '9',0, A_DIGIT, + '.',0, A_DIGIT, + + '=',0, A_STAT, + CONTROL('G'),0, A_STAT, + ':','f',0, A_STAT, + '/',0, A_F_SEARCH, + '?',0, A_B_SEARCH, + ESC,'/',0, A_F_SEARCH|A_EXTRA, '*',0, + ESC,'?',0, A_B_SEARCH|A_EXTRA, '*',0, + 'n',0, A_AGAIN_SEARCH, + ESC,'n',0, A_T_AGAIN_SEARCH, + 'N',0, A_REVERSE_SEARCH, + ESC,'N',0, A_T_REVERSE_SEARCH, + '&',0, A_FILTER, + 'm',0, A_SETMARK, + '\'',0, A_GOMARK, + CONTROL('X'),CONTROL('X'),0, A_GOMARK, + 'E',0, A_EXAMINE, + ':','e',0, A_EXAMINE, + CONTROL('X'),CONTROL('V'),0, A_EXAMINE, + ':','n',0, A_NEXT_FILE, + ':','p',0, A_PREV_FILE, + 't',0, A_NEXT_TAG, + 'T',0, A_PREV_TAG, + ':','x',0, A_INDEX_FILE, + ':','d',0, A_REMOVE_FILE, + '-',0, A_OPT_TOGGLE, + ':','t',0, A_OPT_TOGGLE|A_EXTRA, 't',0, + 's',0, A_OPT_TOGGLE|A_EXTRA, 'o',0, + '_',0, A_DISP_OPTION, + '|',0, A_PIPE, + 'v',0, A_VISUAL, + '!',0, A_SHELL, + '+',0, A_FIRSTCMD, + + 'H',0, A_HELP, + 'h',0, A_HELP, + SK(SK_F1),0, A_HELP, + 'V',0, A_VERSION, + 'q',0, A_QUIT, + 'Q',0, A_QUIT, + ':','q',0, A_QUIT, + ':','Q',0, A_QUIT, + 'Z','Z',0, A_QUIT +}; + +/* + * Command table for UNIX 2003 compatibility: added first before builtin + * so that these commands override the normal LESS commands + */ + +static unsigned char UNIX03cmdtable_init[] = +{ + 's',0, A_F_LINE +}; + +static unsigned char edittable_init[] = +{ + '\t',0, EC_F_COMPLETE, /* TAB */ + '\17',0, EC_B_COMPLETE, /* BACKTAB */ + SK(SK_BACKTAB),0, EC_B_COMPLETE, /* BACKTAB */ + ESC,'\t',0, EC_B_COMPLETE, /* ESC TAB */ + CONTROL('L'),0, EC_EXPAND, /* CTRL-L */ + CONTROL('V'),0, EC_LITERAL, /* BACKSLASH */ + CONTROL('A'),0, EC_LITERAL, /* BACKSLASH */ + ESC,'l',0, EC_RIGHT, /* ESC l */ + SK(SK_RIGHT_ARROW),0, EC_RIGHT, /* RIGHTARROW */ + ESC,'h',0, EC_LEFT, /* ESC h */ + SK(SK_LEFT_ARROW),0, EC_LEFT, /* LEFTARROW */ + ESC,'b',0, EC_W_LEFT, /* ESC b */ + ESC,SK(SK_LEFT_ARROW),0, EC_W_LEFT, /* ESC LEFTARROW */ + SK(SK_CTL_LEFT_ARROW),0, EC_W_LEFT, /* CTRL-LEFTARROW */ + ESC,'w',0, EC_W_RIGHT, /* ESC w */ + ESC,SK(SK_RIGHT_ARROW),0, EC_W_RIGHT, /* ESC RIGHTARROW */ + SK(SK_CTL_RIGHT_ARROW),0, EC_W_RIGHT, /* CTRL-RIGHTARROW */ + ESC,'i',0, EC_INSERT, /* ESC i */ + SK(SK_INSERT),0, EC_INSERT, /* INSERT */ + ESC,'x',0, EC_DELETE, /* ESC x */ + SK(SK_DELETE),0, EC_DELETE, /* DELETE */ + ESC,'X',0, EC_W_DELETE, /* ESC X */ + ESC,SK(SK_DELETE),0, EC_W_DELETE, /* ESC DELETE */ + SK(SK_CTL_DELETE),0, EC_W_DELETE, /* CTRL-DELETE */ + SK(SK_CTL_BACKSPACE),0, EC_W_BACKSPACE, /* CTRL-BACKSPACE */ + ESC,'\b',0, EC_W_BACKSPACE, /* ESC BACKSPACE */ + ESC,'0',0, EC_HOME, /* ESC 0 */ + SK(SK_HOME),0, EC_HOME, /* HOME */ + ESC,'$',0, EC_END, /* ESC $ */ + SK(SK_END),0, EC_END, /* END */ + ESC,'k',0, EC_UP, /* ESC k */ + SK(SK_UP_ARROW),0, EC_UP, /* UPARROW */ + ESC,'j',0, EC_DOWN, /* ESC j */ + SK(SK_DOWN_ARROW),0, EC_DOWN, /* DOWNARROW */ + CONTROL('G'),0, EC_ABORT, /* CTRL-G */ +}; + diff --git a/files/Sources/files/less/tags.c b/files/Sources/files/less/tags.c new file mode 100644 index 00000000..636a291d --- /dev/null +++ b/files/Sources/files/less/tags.c @@ -0,0 +1,765 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +#include "less.h" + +#define WHITESP(c) ((c)==' ' || (c)=='\t') + +#if TAGS + +public char ztags[] = "tags"; +public char *tags = ztags; + +static int total; +static int curseq; + +extern int linenums; +extern int sigs; +extern int unix2003_compat; + +enum tag_result { + TAG_FOUND, + TAG_NOFILE, + TAG_NOTAG, + TAG_NOTYPE, + TAG_INTR +}; + +/* + * Tag type + */ +enum { + T_CTAGS, /* 'tags': standard and extended format (ctags) */ + T_CTAGS_X, /* stdin: cross reference format (ctags) */ + T_GTAGS, /* 'GTAGS': function defenition (global) */ + T_GRTAGS, /* 'GRTAGS': function reference (global) */ + T_GSYMS, /* 'GSYMS': other symbols (global) */ + T_GPATH /* 'GPATH': path name (global) */ +}; + +static enum tag_result findctag(); +static enum tag_result findgtag(); +static char *nextgtag(); +static char *prevgtag(); +static POSITION ctagsearch(); +static POSITION gtagsearch(); +static int getentry(); + +/* + * The list of tags generated by the last findgtag() call. + * + * Use either pattern or line number. + * findgtag() always uses line number, so pattern is always NULL. + * findctag() uses either pattern (in which case line number is 0), + * or line number (in which case pattern is NULL). + */ +struct taglist { + struct tag *tl_first; + struct tag *tl_last; +}; +#define TAG_END ((struct tag *) &taglist) +static struct taglist taglist = { TAG_END, TAG_END }; +struct tag { + struct tag *next, *prev; /* List links */ + char *tag_file; /* Source file containing the tag */ + LINENUM tag_linenum; /* Appropriate line number in source file */ + char *tag_pattern; /* Pattern used to find the tag */ + char tag_endline; /* True if the pattern includes '$' */ +}; +static struct tag *curtag; + +#define TAG_INS(tp) \ + (tp)->next = TAG_END; \ + (tp)->prev = taglist.tl_last; \ + taglist.tl_last->next = (tp); \ + taglist.tl_last = (tp); + +#define TAG_RM(tp) \ + (tp)->next->prev = (tp)->prev; \ + (tp)->prev->next = (tp)->next; + +/* + * Delete tag structures. + */ + public void +cleantags() +{ + register struct tag *tp; + + /* + * Delete any existing tag list. + * {{ Ideally, we wouldn't do this until after we know that we + * can load some other tag information. }} + */ + while ((tp = taglist.tl_first) != TAG_END) + { + TAG_RM(tp); + free(tp); + } + curtag = NULL; + total = curseq = 0; +} + +/* + * Create a new tag entry. + */ + static struct tag * +maketagent(name, file, linenum, pattern, endline) + char *name; + char *file; + LINENUM linenum; + char *pattern; + int endline; +{ + register struct tag *tp; + + tp = (struct tag *) ecalloc(sizeof(struct tag), 1); + tp->tag_file = (char *) ecalloc(strlen(file) + 1, sizeof(char)); + strcpy(tp->tag_file, file); + tp->tag_linenum = linenum; + tp->tag_endline = endline; + if (pattern == NULL) + tp->tag_pattern = NULL; + else + { + tp->tag_pattern = (char *) ecalloc(strlen(pattern) + 1, sizeof(char)); + strcpy(tp->tag_pattern, pattern); + } + return (tp); +} + +/* + * Get tag mode. + */ + public int +gettagtype() +{ + int f; + + if (strcmp(tags, "GTAGS") == 0) + return T_GTAGS; + if (strcmp(tags, "GRTAGS") == 0) + return T_GRTAGS; + if (strcmp(tags, "GSYMS") == 0) + return T_GSYMS; + if (strcmp(tags, "GPATH") == 0) + return T_GPATH; + if (strcmp(tags, "-") == 0) + return T_CTAGS_X; + f = open(tags, OPEN_READ); + if (f >= 0) + { + close(f); + return T_CTAGS; + } + return T_GTAGS; +} + +/* + * Find tags in tag file. + * Find a tag in the "tags" file. + * Sets "tag_file" to the name of the file containing the tag, + * and "tagpattern" to the search pattern which should be used + * to find the tag. + */ + public void +findtag(tag) + register char *tag; +{ + int type = gettagtype(); + enum tag_result result; + + if (type == T_CTAGS) + result = findctag(tag); + else + result = findgtag(tag, type); + switch (result) + { + case TAG_FOUND: + case TAG_INTR: + break; + case TAG_NOFILE: + error("No tags file", NULL_PARG); + break; + case TAG_NOTAG: + error("No such tag in tags file", NULL_PARG); + break; + case TAG_NOTYPE: + error("unknown tag type", NULL_PARG); + break; + } +} + +/* + * Search for a tag. + */ + public POSITION +tagsearch() +{ + if (curtag == NULL) + return (NULL_POSITION); /* No gtags loaded! */ + if (curtag->tag_linenum != 0) + return gtagsearch(); + else + return ctagsearch(); +} + +/* + * Go to the next tag. + */ + public char * +nexttag(n) + int n; +{ + char *tagfile = (char *) NULL; + + while (n-- > 0) + tagfile = nextgtag(); + return tagfile; +} + +/* + * Go to the previous tag. + */ + public char * +prevtag(n) + int n; +{ + char *tagfile = (char *) NULL; + + while (n-- > 0) + tagfile = prevgtag(); + return tagfile; +} + +/* + * Return the total number of tags. + */ + public int +ntags() +{ + return total; +} + +/* + * Return the sequence number of current tag. + */ + public int +curr_tag() +{ + return curseq; +} + +/***************************************************************************** + * ctags + */ + +/* + * Find tags in the "tags" file. + * Sets curtag to the first tag entry. + */ + static enum tag_result +findctag(tag) + register char *tag; +{ + char *p; + register FILE *f; + register int taglen; + LINENUM taglinenum; + char *tagfile; + char *tagpattern; + int tagendline; + int search_char; + int err; + char tline[TAGLINE_SIZE]; + struct tag *tp; + + p = shell_unquote(tags); + f = fopen(p, "r"); + free(p); + if (f == NULL) + return TAG_NOFILE; + + cleantags(); + total = 0; + taglen = (int) strlen(tag); + + /* + * Search the tags file for the desired tag. + */ + while (fgets(tline, sizeof(tline), f) != NULL) + { + if (tline[0] == '!') + /* Skip header of extended format. */ + continue; + if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen])) + continue; + + /* + * Found it. + * The line contains the tag, the filename and the + * location in the file, separated by white space. + * The location is either a decimal line number, + * or a search pattern surrounded by a pair of delimiters. + * Parse the line and extract these parts. + */ + tagpattern = NULL; + + /* + * Skip over the whitespace after the tag name. + */ + p = skipsp(tline+taglen); + if (*p == '\0') + /* File name is missing! */ + continue; + + /* + * Save the file name. + * Skip over the whitespace after the file name. + */ + tagfile = p; + while (!WHITESP(*p) && *p != '\0') + p++; + *p++ = '\0'; + p = skipsp(p); + if (*p == '\0') + /* Pattern is missing! */ + continue; + + /* + * First see if it is a line number. + */ + tagendline = 0; + taglinenum = getnum(&p, 0, &err); + if (err) + { + /* + * No, it must be a pattern. + * Delete the initial "^" (if present) and + * the final "$" from the pattern. + * Delete any backslash in the pattern. + */ + taglinenum = 0; + search_char = *p++; + if (*p == '^') + p++; + tagpattern = p; + while (*p != search_char && *p != '\0') + { + if (*p == '\\') + p++; + p++; + } + tagendline = (p[-1] == '$'); + if (tagendline) + p--; + *p = '\0'; + } + tp = maketagent(tag, tagfile, taglinenum, tagpattern, tagendline); + TAG_INS(tp); + total++; + } + fclose(f); + if (total == 0) + return TAG_NOTAG; + curtag = taglist.tl_first; + curseq = 1; + return TAG_FOUND; +} + +/* + * Edit current tagged file. + */ + public int +edit_tagfile() +{ + if (curtag == NULL) + return (1); + return (edit(curtag->tag_file)); +} + +/* + * Search for a tag. + * This is a stripped-down version of search(). + * We don't use search() for several reasons: + * - We don't want to blow away any search string we may have saved. + * - The various regular-expression functions (from different systems: + * regcmp vs. re_comp) behave differently in the presence of + * parentheses (which are almost always found in a tag). + */ + static POSITION +ctagsearch() +{ + POSITION pos, linepos; + LINENUM linenum; + int len; + char *line; + int found_string_match; + + pos = ch_zero(); + linenum = find_linenum(pos); + + for (;;) + { + /* + * Get lines until we find a matching one or + * until we hit end-of-file. + */ + if (ABORT_SIGS()) + return (NULL_POSITION); + + /* + * Read the next line, and save the + * starting position of that line in linepos. + */ + linepos = pos; + pos = forw_raw_line(pos, &line, (int *)NULL); + if (linenum != 0) + linenum++; + + if (pos == NULL_POSITION) + { + /* + * We hit EOF without a match. + */ + error("Tag not found", NULL_PARG); + return (NULL_POSITION); + } + + /* + * If we're using line numbers, we might as well + * remember the information we have now (the position + * and line number of the current line). + */ + if (linenums) + add_lnum(linenum, pos); + + /* + * Test the line to see if we have a match. + * Use strncmp because the pattern may be + * truncated (in the tags file) if it is too long. + * If tagendline is set, make sure we match all + * the way to end of line (no extra chars after the match). + */ + len = (int)strlen(curtag->tag_pattern); + if (unix2003_compat) { + /* this should probably be the else case too */ + found_string_match = strstr(line, curtag->tag_pattern) != NULL; + } else { + found_string_match = strncmp(curtag->tag_pattern, line, len) == 0; + } + if (found_string_match && + (!curtag->tag_endline || line[len] == '\0' || line[len] == '\r')) + { + curtag->tag_linenum = find_linenum(linepos); + break; + } + } + + return (linepos); +} + +/******************************************************************************* + * gtags + */ + +/* + * Find tags in the GLOBAL's tag file. + * The findgtag() will try and load information about the requested tag. + * It does this by calling "global -x tag" and storing the parsed output + * for future use by gtagsearch(). + * Sets curtag to the first tag entry. + */ + static enum tag_result +findgtag(tag, type) + char *tag; /* tag to load */ + int type; /* tags type */ +{ + char buf[256]; + FILE *fp; + struct tag *tp; + + if (type != T_CTAGS_X && tag == NULL) + return TAG_NOFILE; + + cleantags(); + total = 0; + + /* + * If type == T_CTAGS_X then read ctags's -x format from stdin + * else execute global(1) and read from it. + */ + if (type == T_CTAGS_X) + { + fp = thread_stdin; + /* Set tag default because we cannot read stdin again. */ + tags = ztags; + } else + { +#if !HAVE_POPEN + return TAG_NOFILE; +#else + char *command; + char *flag; + char *qtag; + char *cmd = lgetenv("LESSGLOBALTAGS"); + + if (cmd == NULL || *cmd == '\0') + return TAG_NOFILE; + /* Get suitable flag value for global(1). */ + switch (type) + { + case T_GTAGS: + flag = "" ; + break; + case T_GRTAGS: + flag = "r"; + break; + case T_GSYMS: + flag = "s"; + break; + case T_GPATH: + flag = "P"; + break; + default: + return TAG_NOTYPE; + } + + /* Get our data from global(1). */ + qtag = shell_quote(tag); + if (qtag == NULL) + qtag = tag; + command = (char *) ecalloc(strlen(cmd) + strlen(flag) + + strlen(qtag) + 5, sizeof(char)); + sprintf(command, "%s -x%s %s", cmd, flag, qtag); + if (qtag != tag) + free(qtag); + fp = popen(command, "r"); + free(command); +#endif + } + if (fp != NULL) + { + while (fgets(buf, sizeof(buf), fp)) + { + char *name, *file, *line; + int len; + + if (sigs) + { +#if HAVE_POPEN + if (fp != thread_stdin) + pclose(fp); +#endif + return TAG_INTR; + } + len = (int) strlen(buf); + if (len > 0 && buf[len-1] == '\n') + buf[len-1] = '\0'; + else + { + int c; + do { + c = fgetc(fp); + } while (c != '\n' && c != EOF); + } + + if (getentry(buf, &name, &file, &line)) + { + /* + * Couldn't parse this line for some reason. + * We'll just pretend it never happened. + */ + break; + } + + /* Make new entry and add to list. */ + tp = maketagent(name, file, (LINENUM) atoi(line), NULL, 0); + TAG_INS(tp); + total++; + } + if (fp != thread_stdin) + { + if (pclose(fp)) + { + curtag = NULL; + total = curseq = 0; + return TAG_NOFILE; + } + } + } + + /* Check to see if we found anything. */ + tp = taglist.tl_first; + if (tp == TAG_END) + return TAG_NOTAG; + curtag = tp; + curseq = 1; + return TAG_FOUND; +} + +static int circular = 0; /* 1: circular tag structure */ + +/* + * Return the filename required for the next gtag in the queue that was setup + * by findgtag(). The next call to gtagsearch() will try to position at the + * appropriate tag. + */ + static char * +nextgtag() +{ + struct tag *tp; + + if (curtag == NULL) + /* No tag loaded */ + return NULL; + + tp = curtag->next; + if (tp == TAG_END) + { + if (!circular) + return NULL; + /* Wrapped around to the head of the queue */ + curtag = taglist.tl_first; + curseq = 1; + } else + { + curtag = tp; + curseq++; + } + return (curtag->tag_file); +} + +/* + * Return the filename required for the previous gtag in the queue that was + * setup by findgtat(). The next call to gtagsearch() will try to position + * at the appropriate tag. + */ + static char * +prevgtag() +{ + struct tag *tp; + + if (curtag == NULL) + /* No tag loaded */ + return NULL; + + tp = curtag->prev; + if (tp == TAG_END) + { + if (!circular) + return NULL; + /* Wrapped around to the tail of the queue */ + curtag = taglist.tl_last; + curseq = total; + } else + { + curtag = tp; + curseq--; + } + return (curtag->tag_file); +} + +/* + * Position the current file at at what is hopefully the tag that was chosen + * using either findtag() or one of nextgtag() and prevgtag(). Returns -1 + * if it was unable to position at the tag, 0 if successful. + */ + static POSITION +gtagsearch() +{ + if (curtag == NULL) + return (NULL_POSITION); /* No gtags loaded! */ + return (find_pos(curtag->tag_linenum)); +} + +/* + * The getentry() parses both standard and extended ctags -x format. + * + * [standard format] + * + * +------------------------------------------------ + * |main 30 main.c main(argc, argv) + * |func 21 subr.c func(arg) + * + * The following commands write this format. + * o Traditinal Ctags with -x option + * o Global with -x option + * See + * + * [extended format] + * + * +---------------------------------------------------------- + * |main function 30 main.c main(argc, argv) + * |func function 21 subr.c func(arg) + * + * The following commands write this format. + * o Exuberant Ctags with -x option + * See + * + * Returns 0 on success, -1 on error. + * The tag, file, and line will each be NUL-terminated pointers + * into buf. + */ + static int +getentry(buf, tag, file, line) + char *buf; /* standard or extended ctags -x format data */ + char **tag; /* name of the tag we actually found */ + char **file; /* file in which to find this tag */ + char **line; /* line number of file where this tag is found */ +{ + char *p = buf; + + for (*tag = p; *p && !IS_SPACE(*p); p++) /* tag name */ + ; + if (*p == 0) + return (-1); + *p++ = 0; + for ( ; *p && IS_SPACE(*p); p++) /* (skip blanks) */ + ; + if (*p == 0) + return (-1); + /* + * If the second part begin with other than digit, + * it is assumed tag type. Skip it. + */ + if (!IS_DIGIT(*p)) + { + for ( ; *p && !IS_SPACE(*p); p++) /* (skip tag type) */ + ; + for (; *p && IS_SPACE(*p); p++) /* (skip blanks) */ + ; + } + if (!IS_DIGIT(*p)) + return (-1); + *line = p; /* line number */ + for (*line = p; *p && !IS_SPACE(*p); p++) + ; + if (*p == 0) + return (-1); + *p++ = 0; + for ( ; *p && IS_SPACE(*p); p++) /* (skip blanks) */ + ; + if (*p == 0) + return (-1); + *file = p; /* file name */ + for (*file = p; *p && !IS_SPACE(*p); p++) + ; + if (*p == 0) + return (-1); + *p = 0; + + /* value check */ + if (strlen(*tag) && strlen(*line) && strlen(*file) && atoi(*line) > 0) + return (0); + return (-1); +} + +#endif diff --git a/files/Sources/files/less/ttyin.c b/files/Sources/files/less/ttyin.c new file mode 100644 index 00000000..26956ce9 --- /dev/null +++ b/files/Sources/files/less/ttyin.c @@ -0,0 +1,196 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Routines dealing with getting input from the keyboard (i.e. from the user). + */ + +#include "less.h" +#if OS2 +#include "cmd.h" +#include "pckeys.h" +#endif +#if MSDOS_COMPILER==WIN32C +#include "windows.h" +extern char WIN32getch(); +static DWORD console_mode; +#endif + +public int tty; +extern int sigs; +extern int utf_mode; +extern char * active_dashp_command; +extern int add_newline; + +/* + * Open keyboard for input. + */ + public void +open_getchr() +{ +#if MSDOS_COMPILER==WIN32C + /* Need this to let child processes inherit our console handle */ + SECURITY_ATTRIBUTES sa; + memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES)); + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = TRUE; + tty = (int) CreateFile("CONIN$", GENERIC_READ, + FILE_SHARE_READ, &sa, + OPEN_EXISTING, 0L, NULL); + GetConsoleMode((HANDLE)tty, &console_mode); + /* Make sure we get Ctrl+C events. */ + SetConsoleMode((HANDLE)tty, ENABLE_PROCESSED_INPUT); +#else +#if MSDOS_COMPILER + extern int fd0; + /* + * Open a new handle to CON: in binary mode + * for unbuffered keyboard read. + */ + fd0 = dup(0); + close(0); + tty = open("CON", OPEN_READ); +#if MSDOS_COMPILER==DJGPPC + /* + * Setting stdin to binary causes Ctrl-C to not + * raise SIGINT. We must undo that side-effect. + */ + (void) __djgpp_set_ctrl_c(1); +#endif +#else + /* + * Try /dev/tty. + * If that doesn't work, use file descriptor 2, + * which in Unix is usually attached to the screen, + * but also usually lets you read from the keyboard. + */ +#if OS2 + /* The __open() system call translates "/dev/tty" to "con". */ + tty = __open("/dev/tty", OPEN_READ); +#else + tty = open("/dev/tty", OPEN_READ); +#endif + if (tty < 0) + tty = ios_gettty(); + // tty = 2; + // iOS, temporary: + // tty = fileno(thread_stdin); +#endif +#endif +} + +/* + * Close the keyboard. + */ + public void +close_getchr() +{ +#if MSDOS_COMPILER==WIN32C + SetConsoleMode((HANDLE)tty, console_mode); + CloseHandle((HANDLE)tty); +#endif +} + +/* + * Get a character from the keyboard. + */ + public int +getchr() +{ + char c; + int result; + + if (active_dashp_command) { + /* Use it until all gone */ + c = *active_dashp_command++; + if (c =='\0') { + active_dashp_command = NULL; + if (add_newline) { + c = '\n'; + add_newline = 0; + return (c & 0377); + } + } else + return (c & 0377); + } + + do + { +#if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC + /* + * In raw read, we don't see ^C so look here for it. + */ + flush(); +#if MSDOS_COMPILER==WIN32C + if (ABORT_SIGS()) + return (READ_INTR); + c = WIN32getch(tty); +#else + c = getch(); +#endif + result = 1; + if (c == '\003') + return (READ_INTR); +#else + result = iread(tty, &c, sizeof(char)); + if (result == READ_INTR) + return (READ_INTR); + if (result < 0) + { + /* + * Don't call error() here, + * because error calls getchr! + */ + quit(QUIT_ERROR); + } +#endif +#if 0 /* allow entering arbitrary hex chars for testing */ + /* ctrl-A followed by two hex chars makes a byte */ + { + static int hex_in = 0; + static int hex_value = 0; + if (c == CONTROL('A')) + { + hex_in = 2; + result = 0; + continue; + } + if (hex_in > 0) + { + int v; + if (c >= '0' && c <= '9') + v = c - '0'; + else if (c >= 'a' && c <= 'f') + v = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + v = c - 'A' + 10; + else + hex_in = 0; + hex_value = (hex_value << 4) | v; + if (--hex_in > 0) + { + result = 0; + continue; + } + c = hex_value; + } + } +#endif + /* + * Various parts of the program cannot handle + * an input character of '\0'. + * If a '\0' was actually typed, convert it to '\340' here. + */ + if (c == '\0') + c = '\340'; + } while (result != 1); + + return (c & 0xFF); +} diff --git a/files/Sources/files/less/ubin.uni b/files/Sources/files/less/ubin.uni new file mode 100644 index 00000000..17c45903 --- /dev/null +++ b/files/Sources/files/less/ubin.uni @@ -0,0 +1,33 @@ +/* Generated by "./mkutable -f2 Cc Cf Cs Co Zl Zp -- unicode/UnicodeData.txt" on Tue Sep 20 10:51:43 PDT 2016 */ + { 0x0000, 0x001f }, /* Cc */ + { 0x007f, 0x009f }, /* Cc */ + { 0x00ad, 0x00ad }, /* Cf */ + { 0x0600, 0x0605 }, /* Cf */ + { 0x061c, 0x061c }, /* Cf */ + { 0x06dd, 0x06dd }, /* Cf */ + { 0x070f, 0x070f }, /* Cf */ + { 0x08e2, 0x08e2 }, /* Cf */ + { 0x180e, 0x180e }, /* Cf */ + { 0x200b, 0x200f }, /* Cf */ + { 0x2028, 0x2028 }, /* Zl */ + { 0x2029, 0x2029 }, /* Zp */ + { 0x202a, 0x202e }, /* Cf */ + { 0x2060, 0x2064 }, /* Cf */ + { 0x2066, 0x206f }, /* Cf */ + { 0xd800, 0xd800 }, /* Cs */ + { 0xdb7f, 0xdb80 }, /* Cs */ + { 0xdbff, 0xdc00 }, /* Cs */ + { 0xdfff, 0xdfff }, /* Cs */ + { 0xe000, 0xe000 }, /* Co */ + { 0xf8ff, 0xf8ff }, /* Co */ + { 0xfeff, 0xfeff }, /* Cf */ + { 0xfff9, 0xfffb }, /* Cf */ + { 0x110bd, 0x110bd }, /* Cf */ + { 0x1bca0, 0x1bca3 }, /* Cf */ + { 0x1d173, 0x1d17a }, /* Cf */ + { 0xe0001, 0xe0001 }, /* Cf */ + { 0xe0020, 0xe007f }, /* Cf */ + { 0xf0000, 0xf0000 }, /* Co */ + { 0xffffd, 0xffffd }, /* Co */ + { 0x100000, 0x100000 }, /* Co */ + { 0x10fffd, 0x10fffd }, /* Co */ diff --git a/files/Sources/files/less/version.c b/files/Sources/files/less/version.c new file mode 100644 index 00000000..dde1af0d --- /dev/null +++ b/files/Sources/files/less/version.c @@ -0,0 +1,810 @@ +/* + * Copyright (C) 1984-2016 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* +----------------------- CHANGE HISTORY -------------------------- + + 1/29/84 Allowed use on standard input + 2/1/84 Added E, N, P commands + 4/17/84 Added '=' command, 'stop' signal handling + 4/20/84 Added line folding +v2 4/27/84 Fixed '=' command to use BOTTOM_PLUS_ONE, + instead of TOP, added 'p' & 'v' commands +v3 5/3/84 Added -m and -t options, '-' command +v4 5/3/84 Added LESS environment variable +v5 5/3/84 New comments, fixed '-' command slightly +v6 5/15/84 Added -Q, visual bell +v7 5/24/84 Fixed jump_back(n) bug: n should count real + lines, not folded lines. Also allow number on G command. +v8 5/30/84 Re-do -q and -Q commands +v9 9/25/84 Added "+" argument +v10 10/10/84 Fixed bug in -b argument processing +v11 10/18/84 Made error() ring bell if \n not entered. +----------------------------------------------------------------- +v12 2/13/85 Reorganized signal handling and made portable to 4.2bsd. +v13 2/16/85 Reword error message for '-' command. +v14 2/22/85 Added -bf and -bp variants of -b. +v15 2/25/85 Miscellaneous changes. +v16 3/13/85 Added -u flag for backspace processing. +v17 4/13/85 Added j and k commands, changed -t default. +v18 4/20/85 Rewrote signal handling code. +v19 5/2/85 Got rid of "verbose" eq_message(). + Made search() scroll in some cases. +v20 5/21/85 Fixed screen.c ioctls for System V. +v21 5/23/85 Fixed some first_cmd bugs. +v22 5/24/85 Added support for no RECOMP nor REGCMP. +v23 5/25/85 Miscellanous changes and prettying up. + Posted to USENET. +----------------------------------------------------------------- +v24 6/3/85 Added ti,te terminal init & de-init. + (Thanks to Mike Kersenbrock) +v25 6/8/85 Added -U flag, standout mode underlining. +v26 6/9/85 Added -M flag. + Use underline termcap (us) if it exists. +v27 6/15/85 Renamed some variables to make unique in + 6 chars. Minor fix to -m. +v28 6/28/85 Fixed right margin bug. +v29 6/28/85 Incorporated M.Rose's changes to signal.c +v30 6/29/85 Fixed stupid bug in argument processing. +v31 7/15/85 Added -p flag, changed repaint algorithm. + Added kludge for magic cookie terminals. +v32 7/16/85 Added cat_file if output not a tty. +v33 7/23/85 Added -e flag and EDITOR. +v34 7/26/85 Added -s flag. +v35 7/27/85 Rewrote option handling; added option.c. +v36 7/29/85 Fixed -e flag to work if not last file. +v37 8/10/85 Added -x flag. +v38 8/19/85 Changed prompting; created prompt.c. +v39 8/24/85 (Not -p) does not initially clear screen. +v40 8/26/85 Added "skipping" indicator in forw(). + Posted to USENET. +----------------------------------------------------------------- +v41 9/17/85 ONLY_RETURN, control char commands, + faster search, other minor fixes. +v42 9/25/85 Added ++ command line syntax; + ch_fsize for pipes. +v43 10/15/85 Added -h flag, changed prim.c algorithms. +v44 10/16/85 Made END print in all cases of eof; + ignore SIGTTOU after receiv ing SIGTSTP. +v45 10/16/85 Never print backspaces unless -u. +v46 10/24/85 Backwards scroll in jump_loc. +v47 10/30/85 Fixed bug in edit(): *first_cmd==0 +v48 11/16/85 Use TIOCSETN instead of TIOCSETP. + Added marks (m and ' commands). + Posted to USENET. +----------------------------------------------------------------- +v49 1/9/86 Fixed bug: signal didn't clear mcc. +v50 1/15/86 Added ' (quote) to gomark. +v51 1/16/86 Added + cmd, fixed problem if first_cmd + fails, made g cmd sort of "work" on pipes + ev en if bof is no longer buffered. +v52 1/17/86 Made short files work better. +v53 1/20/86 Added -P option. +v54 1/20/86 Changed help to use HELPFILE. +v55 1/23/86 Messages work better if not tty output. +v56 1/24/86 Added -l option. +v57 1/31/86 Fixed -l to get confirmation before + ov erwriting an existing file. +v58 8/28/86 Added filename globbing. +v59 9/15/86 Fixed some bugs with very long filenames. +v60 9/26/86 Incorporated changes from Leith (Casey) + Leedom for boldface and -z option. +v61 9/26/86 Got rid of annoying repaints after ! cmd. + Posted to USENET. +----------------------------------------------------------------- +v62 12/23/86 Added is_directory(); change -z default to + -1 instead of 24; cat-and-exit if -e and + file is less than a screenful. +v63 1/8/87 Fixed bug in cat-and-exit if > 1 file. +v64 1/12/87 Changed puts/putstr, putc/putchr, + getc/getchr to av oid name conflict with + stdio functions. +v65 1/26/87 Allowed '-' command to change NUMBER + v alued options (thanks to Gary Puckering) +v66 2/13/87 Fixed bug: prepaint should use force=1. +v67 2/24/87 Added !! and % expansion to ! command. +v68 2/25/87 Added SIGWINCH and TIOCGWINSZ support; + changed is_directory to bad_file. + (thanks to J. Robert Ward) +v69 2/25/87 Added SIGWIND and WIOCGETD (for Unix PC). +v70 3/13/87 Changed help cmd from 'h' to 'H'; better + error msgs in bad_file, errno_message. +v71 5/11/87 Changed -p to -c, made triple -c/-C + for clear-eol like more's -c. +v72 6/26/87 Added -E, -L, use $SHELL in lsystem(). + (thanks to Stev e Spearman) +v73 6/26/87 Allow Examine "#" for previous file. + Posted to USENET 8/25/87. +----------------------------------------------------------------- +v74 9/18/87 Fix conflict in EOF symbol with stdio.h, + Make os.c more portable to BSD. +v75 9/23/87 Fix problems in get_term (thanks to + Paul Eggert); new backwards scrolling in + jump_loc (thanks to Marion Hakanson). +v76 9/23/87 Added -i flag; allow single "!" to + inv oke a shell (thanks to Franco Barber). +v77 9/24/87 Added -n flag and line number support. +v78 9/25/87 Fixed problem with prompts longer than + the screen width. +v79 9/29/87 Added the _ command. +v80 10/6/87 Allow signal to break out of linenum scan. +v81 10/6/87 Allow -b to be changed from within less. +v82 10/7/87 Add cmd_decode to use a table for key + binding (thanks to Dav id Nason). +v83 10/9/87 Allow .less file for user-defined keys. +v84 10/11/87 Fix -e/-E problems (thanks to Felix Lee). +v85 10/15/87 Search now keeps track of line numbers. +v86 10/20/87 Added -B option and autobuf; fixed + "pipe error" bug. +v87 3/1/88 Fix bug re BSD signals while reading file. +v88 3/12/88 Use new format for -P option (thanks to + der Mouse), allow "+-c" without message, + fix bug re BSD hangup. +v89 3/18/88 Turn off line numbers if linenum scan + is interrupted. +v90 3/30/88 Allow -P from within less. +v91 3/30/88 Added tags file support (new -t option) + (thanks to Brian Campbell). +v92 4/4/88 Added -+option syntax. +v93 4/11/88 Add support for slow input (thanks to + Joe Orost & apologies for taking almost + 3 years to get this in!) +v94 4/11/88 Redo reading/signal stuff. +v95 4/20/88 Repaint screen better after signal. +v96 4/21/88 Add /! and ?! commands. +v97 5/17/88 Allow -l/-L from within less. + Eliminate some static arrays (use calloc). + Posted to USENET. +----------------------------------------------------------------- +v98 10/14/88 Fix incorrect calloc call; uninitialized + var in exec_mca; core dump on unknown TERM. + Make v cmd work if past last line of file. + Fix some signal bugs. +v99 10/29/88 Allow space between -X and string, + when X is a string-valued option. +v100 1/5/89 Fix globbing bug when $SHELL not set; + allow spaces after -t command. +v101 1/6/89 Fix problem with long (truncated) lines + in tags file (thanks to Neil Dixon). +v102 1/6/89 Fix bug with E# when no prev file; + allow spaces after -l command. +v103 3/14/89 Add -N, -f and -? options. Add z and w + commands. Add %L for prompt strings. +v104 3/16/89 Added EDITPROTO. +v105 3/20/89 Fix bug in find_linenum which cached + incorrectly on long lines. +v106 3/31/89 Added -k option and multiple lesskey + files. +v107 4/27/89 Add 8-bit char support and -g option. + Split option code into 3 files. +v108 5/5/89 Allocate position table dynamically + (thanks to Paul Eggert); change % command + from "percent" to vi-style brace finder. +v109 5/10/89 Added ESC-% command, split prim.c. +v110 5/24/89 Fixed bug in + option; fixed repaint bug + under Sun windows (thanks to Paul Eggert). +v111 5/25/89 Generalized # and % expansion; use + calloc for some error messages. +v112 5/30/89 Get rid of ESC-%, add {}()[] commands. +v113 5/31/89 Optimize lseeks (thanks to Paul Eggert). +v114 7/25/89 Added ESC-/ and ESC-/! commands. +v115 7/26/89 Added ESC-n command. +v116 7/31/89 Added find_pos to optimize g command. +v117 8/1/89 Change -f option to -r. +v118 8/2/89 Save positions for all previous files, + not just the immediately previous one. +v119 8/7/89 Save marks across file boundaries. + Add file handle stuff. +v120 8/11/89 Add :ta command. +v121 8/16/89 Add -f option. +v122 8/30/89 Fix performance with many buffers. +v123 8/31/89 Verbose prompts for string options. + Posted beta to USENET. +----------------------------------------------------------------- +v124 9/18/89 Reorganize search commands, + N = rev, ESC-n = span, add ESC-N. +v125 9/18/89 Fix tab bug (thanks to Alex Liu). + Fix EOF bug when both -w and -c. +v126 10/25/89 Add -j option. +v127 10/27/89 Fix problems with blank lines before BOF. +v128 10/27/89 Add %bj, etc. to prompt strings. +v129 11/3/89 Add -+,-- commands; add set-option and + unset-option to lesskey. +v130 11/6/89 Generalize A_EXTRA to string, remove + set-option, unset-option from lesskey. +v131 11/7/89 Changed name of EDITPROTO to LESSEDIT. +v132 11/8/89 Allow editing of command prefix. +v133 11/16/89 Add -y option (thanks to Jeff Sullivan). +v134 12/1/89 Glob filenames in the -l command. +v135 12/5/89 Combined {}()[] commands into one, and + added ESC-^F and ESC-^B commands. +v136 1/20/90 Added -S, -R flags. Added | command. + Added warning for binary files. (thanks + to Richard Brittain and J. Sullivan). +v137 1/21/90 Rewrote horrible pappend code. + Added * notation for hi-bit chars. +v138 1/24/90 Fix magic cookie terminal handling. + Get rid of "cleanup" loop in ch_get. +v139 1/27/90 Added MSDOS support. (many thanks + to Richard Brittain). +v140 2/7/90 Editing a new file adds it to the + command line list. +v141 2/8/90 Add edit_list for editing >1 file. +v142 2/10/90 Add :x command. +v143 2/11/90 Add * and @ modifies to search cmds. + Change ESC-/ cmd from /@* to / *. +v144 3/1/90 Messed around with ch_zero; + no real change. +v145 3/2/90 Added -R and -v/-V for MSDOS; + renamed FILENAME to avoid conflict. +v146 3/5/90 Pull cmdbuf functions out of command.c +v147 3/7/90 Implement ?@; fix multi-file edit bugs. +v148 3/29/90 Fixed bug in :e then :e#. +v149 4/3/90 Change error,ierror,query to use PARG. +v150 4/6/90 Add LESS_CHARSET, LESS_CHARDEF. +v151 4/13/90 Remove -g option; clean up ispipe. +v152 4/14/90 lsystem() closes input file, for + editors which require exclusive open. +v153 4/18/90 Fix bug if SHELL unset; + fix bug in overstrike control char. +v154 4/25/90 Output to fd 2 via buffer. +v155 4/30/90 Ignore -i if uppercase in pattern + (thanks to Michael Rendell.) +v156 5/3/90 Remove scroll limits in forw() & back(); + causes problems with -c. +v157 5/4/90 Forward search starts at next real line + (not screen line) after jump target. +v158 6/14/90 Added F command. +v159 7/29/90 Fix bug in exiting: output not flushed. +v160 7/29/90 Clear screen before initial output w/ -c. +v161 7/29/90 Add -T flag. +v162 8/14/90 Fix bug with +F on command line. +v163 8/21/90 Added LESSBINFMT variable. +v164 9/5/90 Added -p, LINES, COLUMNS and + unset mark ' == BOF, for 1003.2 D5. +v165 9/6/90 At EOF with -c set, don't display empty + screen when try to page forward. +v166 9/6/90 Fix G when final line in file wraps. +v167 9/11/90 Translate CR/LF -> LF for 1003.2. +v168 9/13/90 Return to curr file if "tag not found". +v169 12/12/90 G goes to EOF even if file has grown. +v170 1/17/91 Add optimization for BSD _setjmp; + fix #include ioctl.h TERMIO problem. + (thanks to Paul Eggert) + Posted to USENET. +----------------------------------------------------------------- +v171 3/6/91 Fix -? bug in get_filename. +v172 3/15/91 Fix G bug in empty file. + Fix bug with ?\n and -i and uppercase + pattern at EOF! + (thanks to Paul Eggert) +v173 3/17/91 Change N cmd to not permanently change + direction. (thanks to Brian Matthews) +v174 3/18/91 Fix bug with namelogfile not getting + cleared when change files. +v175 3/18/91 Fix bug with ++cmd on command line. + (thanks to Jim Meyering) +v176 4/2/91 Change | to not force current screen, + include marked line, start/end from + top of screen. Improve search speed. + (thanks to Don Mears) +v177 4/2/91 Add LESSHELP variable. + Fix bug with F command with -e. + Try /dev/tty for input before using fd 2. + Patches posted to USENET 4/2/91. +----------------------------------------------------------------- +v178 4/8/91 Fixed bug in globbing logfile name. + (thanks to Jim Meyering) +v179 4/9/91 Allow negative -z for screen-relative. +v180 4/9/91 Clear to eos rather than eol if "db"; + don't use "sr" if "da". + (thanks to Tor Lillqvist) +v181 4/18/91 Fixed bug with "negative" chars 80 - FF. + (thanks to Benny Sander Hofmann) +v182 5/16/91 Fixed bug with attribute at EOL. + (thanks to Brian Matthews) +v183 6/1/91 Rewrite linstall to do smart config. +v184 7/11/91 Process \b in searches based on -u + rather than -i. +v185 7/11/91 -Pxxx sets short prompt; assume SIGWINCH + after a SIGSTOP. (thanks to Ken Laprade) +----------------------------------------------------------------- +v186 4/20/92 Port to MS-DOS (Microsoft C). +v187 4/23/92 Added -D option & TAB_COMPLETE_FILENAME. +v188 4/28/92 Added command line editing features. +v189 12/8/92 Fix mem overrun in anscreen.c:init; + fix edit_list to recover from bin file. +v190 2/13/93 Make TAB enter one filename at a time; + create ^L with old TAB functionality. +v191 3/10/93 Defer creating "flash" page for MS-DOS. +v192 9/6/93 Add BACK-TAB. +v193 9/17/93 Simplify binary_file handling. +v194 1/4/94 Add rudiments of alt_filename handling. +v195 1/11/94 Port back to Unix; support keypad. +----------------------------------------------------------------- +v196 6/7/94 Fix bug with bad filename; fix IFILE + type problem. (thanks to David MacKenzie) +v197 6/7/94 Fix bug with .less tables inserted wrong. +v198 6/23/94 Use autoconf installation technology. + (thanks to David MacKenzie) +v199 6/29/94 Fix MS-DOS build (thanks to Tim Wiegman). +v200 7/25/94 Clean up copyright, minor fixes. + Posted to prep.ai.mit.edu +----------------------------------------------------------------- +v201 7/27/94 Check for no memcpy; add casts to calloc; + look for regcmp in libgen.a. + (thanks to Kaveh Ghazi). +v202 7/28/94 Fix bug in edit_next/edit_prev with + non-existent files. +v203 8/2/94 Fix a variety of configuration bugs on + various systems. (thanks to Sakai + Kiyotaka, Harald Koenig, Bjorn Brox, + Teemu Rantanen, and Thorsten Lockert) +v204 8/3/94 Use strerror if available. + (thanks to J.T. Conklin) +v205 8/5/94 Fix bug in finding "me" termcap entry. + (thanks to Andreas Stolcke) +8/10/94 v205+: Change BUFSIZ to LBUFSIZE to avoid name + conflict with stdio.h. + Posted to prep.ai.mit.edu +----------------------------------------------------------------- +v206 8/10/94 Use initial_scrpos for -t to avoid + displaying first page before init(). + (thanks to Dominique Petitpierre) +v207 8/12/94 Fix bug if stdout is not tty. +v208 8/16/94 Fix bug in close_altfile if goto err1 + in edit_ifile. (Thanks to M.J. Hewitt) +v209 8/16/94 Change scroll to wscroll to avoid + conflict with library function. +v210 8/16/94 Fix bug with bold on 8 bit chars. + (thanks to Vitor Duarte) +v211 8/16/94 Don't quit on EOI in jump_loc / forw. +v212 8/18/94 Use time_t if available. +v213 8/20/94 Allow ospeed to be defined in termcap.h. +v214 8/20/94 Added HILITE_SEARCH, -F, ESC-u cmd. + (thanks to Paul Lew and Bob Byrnes) +v215 8/23/94 Fix -i toggle behavior. +v216 8/23/94 Process BS in all searches, not only -u. +v217 8/24/94 Added -X flag. +v218 8/24/94 Reimplement undo_search. +v219 8/24/94 Find tags marked with line number + instead of pattern. +v220 8/24/94 Stay at same position after SIG_WINCH. +v221 8/24/94 Fix bug in file percentage in big file. +v222 8/25/94 Do better if can't reopen current file. +v223 8/27/94 Support setlocale. + (thanks to Robert Joop) +v224 8/29/94 Revert v216: process BS in search + only if -u. +v225 9/6/94 Rewrite undo_search again: toggle. +v226 9/15/94 Configuration fixes. + (thanks to David MacKenzie) +v227 9/19/94 Fixed strerror config problem. + Posted to prep.ai.mit.edu +----------------------------------------------------------------- +v228 9/21/94 Fix bug in signals: repeated calls to + get_editkeys overflowed st_edittable. +v229 9/21/94 Fix "Nothing to search" error if -a + and SRCH_PAST_EOF. +v230 9/21/94 Don't print extra error msg in search + after regerror(). +v231 9/22/94 Fix hilite bug if search matches 0 chars. + (thanks to John Polstra) +v232 9/23/94 Deal with weird systems that have + termios.h but not tcgetattr(). + Posted to prep.ai.mit.edu +----------------------------------------------------------------- +v233 9/26/94 Use get_term() instead of pos_init() in + psignals to re-get lower_left termcap. + (Thanks to John Malecki) +v234 9/26/94 Make MIDDLE closer to middle of screen. +v235 9/27/94 Use local strchr if system doesn't have. +v236 9/28/94 Don't use libucb; use libterm if + libtermcap & libcurses doesn't work. + (Fix for Solaris; thanks to Frank Kaefer) +v237 9/30/94 Use system isupper() etc if provided. + Posted to prep.ai.mit.edu +----------------------------------------------------------------- +v238 10/6/94 Make binary non-blinking if LESSBINFMT + is set to a string without a *. +v239 10/7/94 Don't let delimit_word run back past + beginning of cmdbuf. +v240 10/10/94 Don't write into termcap buffer. + (Thanks to Benoit Speckel) +v241 10/13/94 New lesskey file format. + Don't expand filenames in search command. +v242 10/14/94 Allow lesskey specification of "literal". +v243 10/14/94 Add #stop command to lesskey. +v244 10/16/94 Add -f flag to lesskey. +v245 10/25/94 Allow TAB_COMPLETE_FILENAME to be undefd. +v246 10/27/94 Move help file to /usr/local/share. +v247 10/27/94 Add -V option. +v248 11/5/94 Add -V option to lesskey. +v249 11/5/94 Remove -f flag from lesskey; default + input file is ~/.lesskey.in, not stdin. +v250 11/7/94 Lesskey input file "-" means stdin. +v251 11/9/94 Convert cfgetospeed result to ospeed. + (Thanks to Andrew Chernov) +v252 11/16/94 Change default lesskey input file from + .lesskey.in to .lesskey. + Posted to prep.ai.mit.edu +----------------------------------------------------------------- +v253 11/21/94 Fix bug when tags file has a backslash. +v254 12/6/94 Fix -k option. +v255 12/8/94 Add #define EXAMINE to disable :e etc. +v256 12/10/94 Change highlighting: only highlite search + results (but now it is reliable). +v257 12/10/94 Add goto_line and repaint_highlight + to optimize highlight repaints. +v258 12/12/94 Fixup in hilite_line if BS_SPECIAL. +v259 12/12/94 Convert to autoconf 2.0. +v260 12/13/94 Add SECURE define. +v261 12/14/94 Use system WERASE char as EC_W_BACKSPACE. +v262 12/16/94 Add -g/-G flag and screen_hilite. +v263 12/20/94 Reimplement/optimize -G flag behavior. +v264 12/23/94 Allow EXTRA string after line-edit cmd + in lesskey file. +v265 12/24/94 Add LESSOPEN=|cmd syntax. +v266 12/26/94 Add -I flag. +v267 12/28/94 Formalize the four-byte header emitted + by a LESSOPEN pipe. +v268 12/28/94 Get rid of four-byte header. +v269 1/2/95 Close alt file before open new one. + Avoids multiple popen(). +v270 1/3/95 Use VISUAL; use S_ISDIR/S_ISREG; fix + config problem with Solaris POSIX regcomp. +v271 1/4/95 Don't quit on read error. +v272 1/5/95 Get rid of -L. +v273 1/6/95 Fix ch_ungetchar bug; don't call + LESSOPEN on a pipe. +v274 1/6/95 Ported to OS/2 (thanks to Kai Uwe Rommel) +v275 1/18/95 Fix bug if toggle -G at EOF. +v276 1/30/95 Fix OS/2 version. +v277 1/31/95 Add "next" charset; don't display ^X + for X > 128. +v278 2/14/95 Change default for -G. + Posted to prep.ai.mit.edu +----------------------------------------------------------------- +v279 2/22/95 Add GNU options --help, --version. + Minor config fixes. +v280 2/24/95 Clean up calls to glob(); don't set # + if we can't open the new file. +v281 2/24/95 Repeat search should turn on hilites. +v282 3/2/95 Minor fixes. +v283 3/2/95 Fix homefile; make OS2 look in $HOME. +v284 3/2/95 Error if "v" on LESSOPENed file; + "%" figures out file size on pipe. +v285 3/7/95 Don't set # in lsystem; + lesskey try $HOME first. +v286 3/7/95 Reformat change history (too much free time?). +v287 3/8/95 Fix hilite bug if overstrike multiple chars. +v288 3/8/95 Allow lesskey to override get_editkey keys. +v289 3/9/95 Fix adj_hilite bug when line gets processed by + hilite_line more than once. +v290 3/9/95 Make configure automatically. Fix Sequent problem + with incompatible sigsetmask(). + Posted to prep.ai.mit.edu +----------------------------------------------------------------- +v291 3/21/95 Add #env to lesskey. Fix MS-DOS build. + Posted to simtel. +----------------------------------------------------------------- +v292 4/24/95 Add MS-DOS support for Borland C. + Fix arrow keys in MS-DOS versions. +v293 4/28/95 Add auto-versioning stuff to make dist. +v294 5/12/95 Fix Borland build. +v295 1/20/96 Fix search on squished file; add /@@. +v296 1/23/96 Allow cmdbuf larger than screen width. +v297 1/24/96 Don't call termcap if tgetent fails; + add #defines for buffers. +v298 1/24/96 Change @@ to ^K. + Add alternate search modifiers ^N, ^F, ^E. +v299 1/25/96 Fix percent overflow in jump_percent (thanks to Brent Wiese); + don't send "ti" after shell command till RETURN pressed. +v300 1/25/96 Change -U to print tabs as ^I. +v301 1/30/96 Make hilites work in cmd F output. +v302 1/31/96 Fix cmd F to notice window-change signals. +v303 1/31/96 Add ESC-SPACE command. +v304 2/1/96 Add ^R search modifier; add LESSSECURE. +v305 2/2/96 Workaround Linux /proc kernel bug; add LESSKEY. +v306 3/16/96 Minor fixes. +v307 3/25/96 Allow cmd line arg "--"; fix DOS & OS/2 defines.h. +v308 4/4/96 Port to OS-9 (thanks to Boisy Pitre); fix -d. +v309 4/9/96 Fix OS-9 version; fix tags bug with "$". +v310 4/10/96 Get rid of HELPFILE. +v311 4/22/96 Add Windows32 support; merge doscreen.c into screen.c. +v312 4/24/96 Don't quit after "cannot reopen" error. +v313 4/25/96 Added horizontal scrolling. +v314 4/26/96 Modified -e to quit on reaching end of a squished file. +v315 4/26/96 Fix "!;TAB" bug. +v316 5/2/96 Make "|a" when (a < curr screen) go to end of curr screen. +v317 5/14/96 Various fixes for the MS-DOS and OS/2 builds. + Added ## and %% handling for filenames +v318 5/29/96 Port to OS-9 Microware compiler; minor fixes + (thanks to Martin Gregorie). +v319 7/8/96 Fix Windows port (thanks to Jeff Paquette). +v320 7/11/96 Final fixes for Windows port. +v321 7/18/96 Minor fixes. + Posted to Web page. +----------------------------------------------------------------- +v322 8/13/96 Fix bug in shell escape from help file; add support for + Microsoft Visual C under Windows; numerous small fixes. +v323 8/19/96 Fixes for Windows version (thanks to Simon Munton); + fix for Linux library weirdness (thanks to Jim Diamond); + port to DJGPP (thanks to Eli Zaretskii). +v324 8/21/96 Add support for spaces in filenames (thanks to Simon Munton). +v325 8/21/96 Add lessecho, for spaces in filenames under Unix. +v326 8/27/96 Fix DJGPP version. +v327 9/1/96 Reorganize lglob, make spaces in filenames work better in Unix. +v328 10/7/96 Append / to directory name in filename completion. + Fix MS-DOS and OS-9 versions. +v329 10/11/96 Fix more MS-DOS bugs; add LESSSEPARATOR; add -" option. + Add LESSMETACHARS, LESSMETAESCAPE. +v330 10/21/96 Minor fixes. + Posted to Web page. +----------------------------------------------------------------- +v331 4/22/97 Various Windows fixes (thanks to Gurusamy Sarathy). +v332 4/22/97 Enter filenames from cmd line into edit history. + Posted to Web page. +----------------------------------------------------------------- +v333 3/4/99 Changed -w to highlite new line after forward movement. +v334 3/9/99 Avoid overflowing prompt buffer; add %d and %D. +v335 3/20/99 Add EBCDIC support (thanks to Thomas Dorner). + Use HOMEDRIVE/HOMEPATH on Windows (thanks to Preston Bannister). + Posted to Web page. +----------------------------------------------------------------- +v336 4/8/99 Fix installation bugs. +v337 4/9/99 Fix another installation bug. + Posted to Web page. +----------------------------------------------------------------- +v338 4/13/99 Add support for long option names. +v339 4/18/99 Add \k, long option names to lesskey. Add -^P. Add :d. +v340 4/21/99 Add regexec2. Fix Windows build. + Posted to Web page. +----------------------------------------------------------------- +v341 5/6/99 Add -F option; %c & ?c prompt escapes. + (Thanks to Michele Maltoni) +v342 7/22/99 Add system-wide lesskey file; allow GPL or Less License. +v343 9/23/99 Support UTF-8 (Thanks to Robert Brady). + Add %P and ?P in prompts. +v344 10/27/99 -w highlights target line of g and p commands. +v345 10/29/99 Make -R pass thru ESC but not other control chars. + Posted to Web page. +----------------------------------------------------------------- +v346 11/4/99 Fix bugs in long option processing; R cmd should clear hilites. + Posted to Web page. +----------------------------------------------------------------- +v347 12/13/99 Fixes for DJGPP version (thanks to Eli Zaretskii). +v348 12/28/99 Fix deleting file with marks (thanks to Dimitar Jekov). + Fix color problem in DJGPP version (thanks to Eli Zaretskii). +v349 1/24/00 Fix minor DJGPP bugs; check environment vars for UTF-8; + add --with-editor (thanks to Eli, Markus Kuhn, Thomas Schoepf). +v350 3/1/00 Fix clear-while-standout bug. +v351 3/5/00 Change -M and = prompts to show top & bottom line number. + Posted to Web page. +----------------------------------------------------------------- +v352 3/8/00 Fix scan_option NULL dereference. +----------------------------------------------------------------- +v353 3/20/00 Fix SECURE compile bug, allow space after numeric option. +v354 3/23/00 Add support for PCRE; add --with-regex configure option. +----------------------------------------------------------------- +v355 6/28/00 Add -# option (thanks to Andy Levinson). +v356 7/5/00 Add -J option. +v357 7/6/00 Support sigprocmask. +----------------------------------------------------------------- +v358 7/8/00 Fix problems with #stop in lesskey file. + Posted to Web page. +----------------------------------------------------------------- +v359 9/10/00 Fixes for Win32 display problems (thanks to Maurizio Vairani). +v360 1/17/01 Move sysless to etc. +v361 12/4/01 Add IBM-1047 charset & EBCDIC fixes (thanks to Thomas Dorner). + Fix 32 bit dependencies (thanks to Paul Eggert). + Fix UTF-8 overstriking (thanks to Robert Brady). +v362 12/4/01 Make status column show search targets. +v363 12/6/01 Add --no-keypad option. + Add variable width tabstops (thanks to Peter Samuelson). +v364 12/10/01 Better handling of very long lines in input; + Fix horizontal shifting of colored text. +v365 12/11/01 Fix overstriking of tabs; + Add support for global(1) and multiple tag matches + (thanks to Shigio Yamaguchi and Tim Vanderhoek). +v366 12/11/01 Fixes for OS/2 (thanks to Kyosuke Tokoro). +v367 12/13/01 Allow -D and -x options to terminate without dollar sign; + Right/left arrow when entering N are shift cmds, not line edit. +v368 12/18/01 Update lesskey commands. +v370 12/23/01 Fix tags error messages. + Posted to Web page. +----------------------------------------------------------------- +v371 12/26/01 Fix new_file bug; use popen in Windows version; + fix some compiler warnings. +v372 12/29/01 Make -b be in units of 1K. +v373 1/14/02 Improve handling of filenames containing shell metachars. +v374 2/7/02 Fix memory leak; fix bug in -x argument parsing. +v375 4/7/02 Fix searching for SGR sequences; fix SECURE build; + add SGR support to DJGPP version (thanks to Eli Zaretskii). +v376 6/10/02 Fix bug in overstriking mulitbyte UTF-8 characters + (thanks to Jungshik Shin). + Posted to Web page. +----------------------------------------------------------------- +v377 9/10/02 Fix bug in Windows version when file contains CR; + fix bug in search highlights with -R; + make initial buffer limit really be 64K not unlimited. +v378 9/30/02 Misc bug fixes and compiler warning cleanup. + Posted to Web page. +----------------------------------------------------------------- +v379 11/23/02 Add -L option; fix bug with ctrl-K in lesskey files; + improve UTF-8 overstriking and underscore overstriking; + fix minor man page problems; change to autoconf 2.54. +v380 11/24/02 Make LINENUM same as POSITION. +v381 11/28/02 Make -N use 7 columns for line number if possible. +----------------------------------------------------------------- +v382 2/3/04 Remove copyrighted code. +----------------------------------------------------------------- +v383 2/16/04 Add history file; add -K option; improve UTF-8 handling; + fix some signed char bugs (thanks to Christian Biere); + fix some upper/lower case bugs (thanks to Bjoern Jacke); + add erase2 char (thanks to David Lawrence); + add windows charset (thanks to Dimitar Zhekov). +v384 2/20/04 Improvements in UTF-8 handling. +v385 2/23/04 Fix UTF-8 output bug. +----------------------------------------------------------------- +v386 9/13/05 Improvements to UTF-8 shift & color (thanks to Charles Levert); + protect against invalid LESSOPEN and LESSCLOSE values. +v387 9/14/05 Update Charles Levert's UTF-8 patch. +v388 9/14/05 Change history behavior; change most sprintf calls to snprintf. +v389 9/14/05 Fix copy & paste with long lines; improve performance of + expand_linebuf; fix crash in init_mlist; +v390 9/15/05 Show search matches in status column even if -G is set. +----------------------------------------------------------------- +v391 9/17/05 Fix bugs. +v392 10/14/05 Fix line wrapping bug. +v393 10/19/05 Allow multiple attributes per char; fix bold+underline bug + (thanks again to Charles Levert). +v394 11/8/05 Fix prompt bug; fix compile problem in Windows build. +----------------------------------------------------------------- +v395 1/12/07 Update Unicode tables (thanks to Charles Levert); + don't chmod if LESSHISTFILE = /dev/null; + make -f work for directories; support DESTDIR in Makefile; + fix sigset_t detection in configure; + make "t" cmd traverse tags in correct order +v396 1/13/07 Add compatibility with POSIX more. +v397 3/21/07 Allow decimal point in number for % command; + Allow decimal point in number for -j option; + Allow n command to fetch last search pattern from history + (thanks to arno). +v398 3/22/07 Don't rewrite history file if not necessary; + fix bug when filenames contain "$". +v399 3/22/07 Don't move to bottom of screen at startup; + don't output extraneous newlines. +v400 3/23/07 Allow search to find pattern after null byte (PCRE and no-regex) + (thanks to Michael Constant). +----------------------------------------------------------------- +v401 3/24/07 Minor documentation fixes. +v402 3/30/07 Fix autoconf bug when memcpy etc are inline; + fix bug in terminating number following -j option. +v403 5/25/07 Fix Windows build. +v404 6/5/07 Fix display bug with F command and long lines. +v405 6/17/07 Fix display bug when using -w option. +v406 6/17/07 Fix secure build. +v407 8/16/07 Fix bugs; support CSI chars. +v408 10/1/07 Fix bug in -i with non-ASCII chars. +v409 10/12/07 Fix crash when viewing text with invalid UTF-8 sequences. +v411 11/6/07 Fix case-insensitive searching with non-ASCII text. +v412 11/6/07 Use symbolic SEEK constants. +v413 11/6/07 Fix search highlight bug with non-ASCII text. +v414 11/6/07 Fix display bug with no-wrap terminals. +v415 11/14/07 Add --follow-name option. +v416 11/22/07 Fix crash when searching text with invalid UTF-8 sequences. +v417 12/31/07 Don't support single-char CSI in UTF-8 mode; + fix bug with -R and invalid CSI sequences; + fix bug searching text with SGR sequences with -r; + emulate SGR sequences in WIN32 build. +v418 12/31/07 Clean up. +----------------------------------------------------------------- +v419 1/16/08 Make CSI char 0x9B work in UTF-8 mode (thanks to Colin Watson). +v420 2/24/08 Add & command; fix -F option; fix '' after G. +v421 2/24/08 Ignore filtered lines when searching. +v422 3/2/08 Output CR at startup. +v423 5/27/08 Clean up. +v424 6/16/08 Fix compile bug with pcre; don't filter help file. +v425 7/14/08 Fix non-ANSI code in list handling in ch.c. +v426 10/27/08 Fix ignaw terminal handling (thanks to Per Hedeland); + fix binary file detection in UTF-8 mode. +v427 3/16/09 A few Win32 fixes (thanks to Jason Hood). +v428 3/30/09 Add "|-" syntax to LESSOPEN. +v429 4/10/09 Fix search highlighting bug with underlined text. +----------------------------------------------------------------- +v430 4/22/09 Don't pass "-" to non-pipe LESSOPEN unless it starts with "-". +v431 4/29/09 Fix highlight bug when match is at end of line. +v432 6/27/09 Better fix for highlight bugs; + fix new problems with ignaw terminals. +v433 6/28/09 Cleanup search code. +v434 6/29/09 More cleanup. +v435 7/04/09 Fix bugs with non-regex filtering. +v436 7/05/09 Fix memory leak. +----------------------------------------------------------------- +v437 7/14/09 Fix bug in handling some long option names; + make percentage calculation more accurate. +v438 12/29/10 Fix bugs with -i/-I and & filtering; + exit with status 2 on ctrl-C with -K. +v439 12/31/10 Add -A option. +v440 1/5/11 Fix bug displaying prompt after = command. +v441 1/21/11 Fix semi-infinite loop if no newlines in file; + make new -A behavior the default. +----------------------------------------------------------------- +v442 3/2/11 Fix search bug. + Add ctrl-G line edit command. +v443 4/9/11 Fix Windows build. +v444 6/8/11 Fix ungetc bug; remove vestiges of obsolete -l option. +----------------------------------------------------------------- +v445 10/19/11 Fix hilite bug in backwards scroll with -J. + Fix hilite bug with backspaces. + Fix bugs handling SGR sequences in Win32 (thanks to Eric Lee). + Add support for GNU regex (thanks to Reuben Thomas). +v446 5/15/12 Up/down arrows in cmd editing search for matching cmd. +v447 5/21/12 Add ESC-F command, two-pipe LESSOPEN syntax. +v448 6/15/12 Print name of regex library in version message. +v449 6/23/12 Allow config option --with-regex=none. +v450 7/4/12 Fix EOF bug with ESC-F. +v451 7/20/12 Fix typo. +----------------------------------------------------------------- +v452 10/19/12 Fix --with-regex=none, fix "stty 0", fix Win32. + Don't quit if errors in cmd line options. +v453 10/27/12 Increase buffer sizes. +v454 11/5/12 Fix typo. +v455 11/5/12 Fix typo. +v456 11/8/12 Fix option string incompatibility. +v457 12/8/12 Use new option string syntax only after --use-backslash. +v458 4/4/13 Fix display bug in using up/down in cmd buffer. +----------------------------------------------------------------- +v459 5/6/13 Fix ++ bug. +v460 6/19/13 Automate construction of Unicode tables. +v461 6/21/13 Collapse multiple CRs before LF. +v462 11/26/13 Don't overwrite history file, just append to it. +v463 7/13/14 Misc. fixes. +v464 7/19/14 Fix bugs & improve performance in & filtering + (thanks to John Sullivan). +v465 8/9/14 More fixes from John Sullivan. +v466 8/23/14 Add colon to LESSANSIMIDCHARS. +v467 9/18/14 Misc. fixes. +v468 9/18/14 Fix typo +v469 10/2/14 Allow extra string in command to append to a multichar + cmd without executing it; fix bug using GNU regex. +v470 10/5/14 Fix some compiler warnings. +v471 12/14/14 Fix unget issues with prompt. Allow disabling history + when compiled value of LESSHISTFILE = "-". +v473 12/19/14 Fix prompt bug with stdin and -^P in lesskey extra string. +v474 1/30/15 Fix bug in backwards search with match on bottom line. + Make follow mode reopen file if file shrinks. +v475 3/2/15 Fix possible buffer overrun with invalid UTF-8; + fix bug when compiled with no regex; fix non-match search. +v476 5/3/15 Update man pages. +v477 5/19/15 Fix off-by-one in jump_forw_buffered; + don't add FAKE_* files to cmd history. +v478 5/21/15 Fix nonportable pointer usage in hilite tree. +v479 7/6/15 Allow %% escapes in LESSOPEN variable. +v480 7/24/15 Fix bug in no-regex searches; support MSVC v1900. +v481 8/20/15 Fix broken -g option. +----------------------------------------------------------------- +v482 2/25/16 Update Unicode database to "2015-06-16, 20:24:00 GMT [KW]". +v483 2/27/16 Regenerate hilite when change search caselessness. + (Thanks to Jason Hood) + Fix bug when terminal has no "cm". (Thanks to Noel Cragg) +v484 9/20/16 Update to Unicode 9.0.0 database. +v485 10/21/16 Fix "nothing to search" bug when top/bottom line is empty; + Display line numbers in bold. (thanks to Jason Hood); + Fix incorrect display when entering double-width chars in + search string. +v486 10/22/16 New commands ESC-{ and ESC-} to shift to start/end of + displayed lines; new option -Da in Windows version to + enable SGR mode (thanks to Jason Hood). +v487 10/23/16 configure --help formatting. +*/ + +char version[] = "487"; diff --git a/files/Sources/files/less/wide.uni b/files/Sources/files/less/wide.uni new file mode 100644 index 00000000..17c38591 --- /dev/null +++ b/files/Sources/files/less/wide.uni @@ -0,0 +1,108 @@ +/* Generated by "./mkutable -f1 W F -- unicode/EastAsianWidth.txt" on Tue Sep 20 10:51:43 PDT 2016 */ + { 0x1100, 0x115f }, /* W */ + { 0x231a, 0x231b }, /* W */ + { 0x2329, 0x232a }, /* W */ + { 0x23e9, 0x23ec }, /* W */ + { 0x23f0, 0x23f0 }, /* W */ + { 0x23f3, 0x23f3 }, /* W */ + { 0x25fd, 0x25fe }, /* W */ + { 0x2614, 0x2615 }, /* W */ + { 0x2648, 0x2653 }, /* W */ + { 0x267f, 0x267f }, /* W */ + { 0x2693, 0x2693 }, /* W */ + { 0x26a1, 0x26a1 }, /* W */ + { 0x26aa, 0x26ab }, /* W */ + { 0x26bd, 0x26be }, /* W */ + { 0x26c4, 0x26c5 }, /* W */ + { 0x26ce, 0x26ce }, /* W */ + { 0x26d4, 0x26d4 }, /* W */ + { 0x26ea, 0x26ea }, /* W */ + { 0x26f2, 0x26f3 }, /* W */ + { 0x26f5, 0x26f5 }, /* W */ + { 0x26fa, 0x26fa }, /* W */ + { 0x26fd, 0x26fd }, /* W */ + { 0x2705, 0x2705 }, /* W */ + { 0x270a, 0x270b }, /* W */ + { 0x2728, 0x2728 }, /* W */ + { 0x274c, 0x274c }, /* W */ + { 0x274e, 0x274e }, /* W */ + { 0x2753, 0x2755 }, /* W */ + { 0x2757, 0x2757 }, /* W */ + { 0x2795, 0x2797 }, /* W */ + { 0x27b0, 0x27b0 }, /* W */ + { 0x27bf, 0x27bf }, /* W */ + { 0x2b1b, 0x2b1c }, /* W */ + { 0x2b50, 0x2b50 }, /* W */ + { 0x2b55, 0x2b55 }, /* W */ + { 0x2e80, 0x2e99 }, /* W */ + { 0x2e9b, 0x2ef3 }, /* W */ + { 0x2f00, 0x2fd5 }, /* W */ + { 0x2ff0, 0x2ffb }, /* W */ + { 0x3000, 0x3000 }, /* F */ + { 0x3001, 0x303e }, /* W */ + { 0x3041, 0x3096 }, /* W */ + { 0x3099, 0x30ff }, /* W */ + { 0x3105, 0x312d }, /* W */ + { 0x3131, 0x318e }, /* W */ + { 0x3190, 0x31ba }, /* W */ + { 0x31c0, 0x31e3 }, /* W */ + { 0x31f0, 0x321e }, /* W */ + { 0x3220, 0x3247 }, /* W */ + { 0x3250, 0x32fe }, /* W */ + { 0x3300, 0x4dbf }, /* W */ + { 0x4e00, 0xa48c }, /* W */ + { 0xa490, 0xa4c6 }, /* W */ + { 0xa960, 0xa97c }, /* W */ + { 0xac00, 0xd7a3 }, /* W */ + { 0xf900, 0xfaff }, /* W */ + { 0xfe10, 0xfe19 }, /* W */ + { 0xfe30, 0xfe52 }, /* W */ + { 0xfe54, 0xfe66 }, /* W */ + { 0xfe68, 0xfe6b }, /* W */ + { 0xff01, 0xff60 }, /* F */ + { 0xffe0, 0xffe6 }, /* F */ + { 0x16fe0, 0x16fe0 }, /* W */ + { 0x17000, 0x187ec }, /* W */ + { 0x18800, 0x18af2 }, /* W */ + { 0x1b000, 0x1b001 }, /* W */ + { 0x1f004, 0x1f004 }, /* W */ + { 0x1f0cf, 0x1f0cf }, /* W */ + { 0x1f18e, 0x1f18e }, /* W */ + { 0x1f191, 0x1f19a }, /* W */ + { 0x1f200, 0x1f202 }, /* W */ + { 0x1f210, 0x1f23b }, /* W */ + { 0x1f240, 0x1f248 }, /* W */ + { 0x1f250, 0x1f251 }, /* W */ + { 0x1f300, 0x1f320 }, /* W */ + { 0x1f32d, 0x1f335 }, /* W */ + { 0x1f337, 0x1f37c }, /* W */ + { 0x1f37e, 0x1f393 }, /* W */ + { 0x1f3a0, 0x1f3ca }, /* W */ + { 0x1f3cf, 0x1f3d3 }, /* W */ + { 0x1f3e0, 0x1f3f0 }, /* W */ + { 0x1f3f4, 0x1f3f4 }, /* W */ + { 0x1f3f8, 0x1f43e }, /* W */ + { 0x1f440, 0x1f440 }, /* W */ + { 0x1f442, 0x1f4fc }, /* W */ + { 0x1f4ff, 0x1f53d }, /* W */ + { 0x1f54b, 0x1f54e }, /* W */ + { 0x1f550, 0x1f567 }, /* W */ + { 0x1f57a, 0x1f57a }, /* W */ + { 0x1f595, 0x1f596 }, /* W */ + { 0x1f5a4, 0x1f5a4 }, /* W */ + { 0x1f5fb, 0x1f64f }, /* W */ + { 0x1f680, 0x1f6c5 }, /* W */ + { 0x1f6cc, 0x1f6cc }, /* W */ + { 0x1f6d0, 0x1f6d2 }, /* W */ + { 0x1f6eb, 0x1f6ec }, /* W */ + { 0x1f6f4, 0x1f6f6 }, /* W */ + { 0x1f910, 0x1f91e }, /* W */ + { 0x1f920, 0x1f927 }, /* W */ + { 0x1f930, 0x1f930 }, /* W */ + { 0x1f933, 0x1f93e }, /* W */ + { 0x1f940, 0x1f94b }, /* W */ + { 0x1f950, 0x1f95e }, /* W */ + { 0x1f980, 0x1f991 }, /* W */ + { 0x1f9c0, 0x1f9c0 }, /* W */ + { 0x20000, 0x2fffd }, /* W */ + { 0x30000, 0x3fffd }, /* W */ diff --git a/files/Sources/files/libutil.h b/files/Sources/files/libutil.h new file mode 100644 index 00000000..4b5ed047 --- /dev/null +++ b/files/Sources/files/libutil.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 1996 Peter Wemm . + * All rights reserved. + * Copyright (c) 2002 Networks Associates Technology, Inc. + * All rights reserved. + * + * Portions of this software were developed for the FreeBSD Project by + * ThinkSec AS and NAI Labs, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 + * ("CBOSS"), as part of the DARPA CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, is 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. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * 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. + * + * $FreeBSD: src/lib/libutil/libutil.h,v 1.42 2006/02/18 11:25:28 des Exp $ + */ + +#ifndef _LIBUTIL_H_ +#define _LIBUTIL_H_ + +#include +#include + +#ifdef _SYS_PARAM_H_ +/* for pidfile.c */ +struct pidfh { + int pf_fd; + char pf_path[MAXPATHLEN + 1]; + dev_t pf_dev; + ino_t pf_ino; +}; +#endif + +struct in_addr; +struct sockaddr; + +__BEGIN_DECLS +int humanize_number(char *_buf, size_t _len, int64_t _number, + const char *_suffix, int _scale, int _flags); + +int realhostname(char *host, size_t hsize, const struct in_addr *ip); +int realhostname_sa(char *host, size_t hsize, struct sockaddr *addr, + int addrlen); + +#ifdef _SYS_PARAM_H_ +struct pidfh *pidfile_open(const char *path, mode_t mode, pid_t *pidptr); +int pidfile_write(struct pidfh *pfh); +int pidfile_close(struct pidfh *pfh); +int pidfile_remove(struct pidfh *pfh); +#endif + +int reexec_to_match_kernel(void); +int reexec_to_match_lp64ness(bool isLP64); + +__END_DECLS + +/* return values from realhostname() */ +#define HOSTNAME_FOUND (0) +#define HOSTNAME_INCORRECTNAME (1) +#define HOSTNAME_INVALIDADDR (2) +#define HOSTNAME_INVALIDNAME (3) + +/* Values for humanize_number(3)'s flags parameter. */ +#define HN_DECIMAL 0x01 +#define HN_NOSPACE 0x02 +#define HN_B 0x04 +#define HN_DIVISOR_1000 0x08 +#define HN_IEC_PREFIXES 0x10 + +/* Values for humanize_number(3)'s scale parameter. */ +#define HN_GETSCALE 0x10 +#define HN_AUTOSCALE 0x20 + +#endif /* !_LIBUTIL_H_ */ diff --git a/files/Sources/files/ln/link.1 b/files/Sources/files/ln/link.1 new file mode 100644 index 00000000..bfb5b6e2 --- /dev/null +++ b/files/Sources/files/ln/link.1 @@ -0,0 +1 @@ +.so man1/ln.1 diff --git a/files/Sources/files/ln/ln.1 b/files/Sources/files/ln/ln.1 new file mode 100644 index 00000000..37100d37 --- /dev/null +++ b/files/Sources/files/ln/ln.1 @@ -0,0 +1,233 @@ +.\"- +.\" Copyright (c) 1980, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" 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. +.\" +.\" @(#)ln.1 8.2 (Berkeley) 12/30/93 +.\" $FreeBSD: src/bin/ln/ln.1,v 1.31 2006/02/14 11:08:05 glebius Exp $ +.\" +.Dd February 14, 2006 +.Dt LN 1 +.Os +.Sh NAME +.Nm link , +.Nm ln +.Nd make links +.Sh SYNOPSIS +.Nm ln +.Op Fl Ffhinsv +.Ar source_file +.Op Ar target_file +.Nm ln +.Op Fl Ffhinsv +.Ar source_file ... +.Ar target_dir +.Nm link +.Ar source_file Ar target_file +.Sh DESCRIPTION +The +.Nm ln +utility creates a new directory entry (linked file) which has the +same modes as the original file. +It is useful for maintaining multiple copies of a file in many places +at once without using up storage for the +.Dq copies ; +instead, a link +.Dq points +to the original copy. +There are two types of links; hard links and symbolic links. +How a link +.Dq points +to a file is one of the differences between a hard and symbolic link. +.Pp +The options are as follows: +.Bl -tag -width flag +.\" ========== +.It Fl F +If the target file already exists and is a directory, then remove it +so that the link may occur. +The +.Fl F +option should be used with either +.Fl f +or +.Fl i +options. +If none is specified, +.Fl f +is implied. +The +.Fl F +option is a no-op unless +.Fl s +option is specified. +.It Fl h +If the +.Ar target_file +or +.Ar target_dir +is a symbolic link, do not follow it. +This is most useful with the +.Fl f +option, to replace a symlink which may point to a directory. +.\" ========== +.It Fl f +If the target file already exists, +then unlink it so that the link may occur. +(The +.Fl f +option overrides any previous +.Fl i +options.) +.\" ========== +.It Fl i +Cause +.Nm ln +to write a prompt to standard error if the target file exists. +If the response from the standard input begins with the character +.Sq Li y +or +.Sq Li Y , +then unlink the target file so that the link may occur. +Otherwise, do not attempt the link. +(The +.Fl i +option overrides any previous +.Fl f +options.) +.\" ========== +.It Fl n +Same as +.Fl h , +for compatibility with other +.Nm ln +implementations. +.\" ========== +.It Fl s +Create a symbolic link. +.\" ========== +.It Fl v +Cause +.Nm ln +to be verbose, showing files as they are processed. +.El +.Pp +By default, +.Nm ln +makes +.Em hard +links. +A hard link to a file is indistinguishable from the original directory entry; +any changes to a file are effectively independent of the name used to reference +the file. +Hard links may not normally refer to directories and may not span file systems. +.Pp +A symbolic link contains the name of the file to +which it is linked. +The referenced file is used when an +.Xr open 2 +operation is performed on the link. +A +.Xr stat 2 +on a symbolic link will return the linked-to file; an +.Xr lstat 2 +must be done to obtain information about the link. +The +.Xr readlink 2 +call may be used to read the contents of a symbolic link. +Symbolic links may span file systems and may refer to directories. +.Pp +Given one or two arguments, +.Nm ln +creates a link to an existing file +.Ar source_file . +If +.Ar target_file +is given, the link has that name; +.Ar target_file +may also be a directory in which to place the link; +otherwise it is placed in the current directory. +If only the directory is specified, the link will be made +to the last component of +.Ar source_file . +.Pp +Given more than two arguments, +.Nm ln +makes links in +.Ar target_dir +to all the named source files. +The links made will have the same name as the files being linked to. +.Pp +When the utility is called as +.Nm link , +exactly two arguments must be supplied, +neither of which may specify a directory. +No options may be supplied in this simple mode of operation, +which performs a +.Xr link 2 +operation using the two passed arguments. +.Sh COMPATIBILITY +The +.Fl h , +.Fl i , +.Fl n +and +.Fl v +options are non-standard and their use in scripts is not recommended. +They are provided solely for compatibility with other +.Nm ln +implementations. +.Pp +The +.Fl F +option is +.Fx +extention and should not be used in portable scripts. +.Sh SEE ALSO +.Xr link 2 , +.Xr lstat 2 , +.Xr readlink 2 , +.Xr stat 2 , +.Xr symlink 2 , +.Xr symlink 7 +.Sh STANDARDS +The +.Nm ln +utility conforms to +.St -p1003.2-92 . +.Pp +The simplified +.Nm link +command conforms to +.St -susv2 . +.Sh HISTORY +An +.Nm ln +command appeared in +.At v1 . diff --git a/files/Sources/files/ln/ln.c b/files/Sources/files/ln/ln.c new file mode 100644 index 00000000..21c2445a --- /dev/null +++ b/files/Sources/files/ln/ln.c @@ -0,0 +1,269 @@ +/*- + * Copyright (c) 1987, 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. + */ + +#if 0 +#ifndef lint +static char const copyright[] = +"@(#) Copyright (c) 1987, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)ln.c 8.2 (Berkeley) 3/31/94"; +#endif /* not lint */ +#endif +#include +__FBSDID("$FreeBSD: src/bin/ln/ln.c,v 1.34 2006/02/14 11:08:05 glebius Exp $"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ios_error.h" + +static int fflag; /* Unlink existing files. */ +static int Fflag; /* Remove empty directories also. */ +static int hflag; /* Check new name for symlink first. */ +static int iflag; /* Interactive mode. */ +static int sflag; /* Symbolic, not hard, link. */ +static int vflag; /* Verbose output. */ + /* System link call. */ +static int (*linkf)(const char *, const char *); +static char linkch; + +static int linkit(const char *, const char *, int); +static void usage(void); + +int +ln_main(int argc, char *argv[]) +{ + struct stat sb; + char *p, *sourcedir; + int ch, exitval; + + fflag = Fflag = hflag = iflag = sflag = vflag = 0; + optind = 1; opterr = 1; optreset = 1; + if (argc < 1) + usage(); + /* + * Test for the special case where the utility is called as + * "link", for which the functionality provided is greatly + * simplified. + */ + if ((p = rindex(argv[0], '/')) == NULL) + p = argv[0]; + else + ++p; + if (strcmp(p, "link") == 0) { + while (getopt(argc, argv, "") != -1) + usage(); + argc -= optind; + argv += optind; + if (argc != 2) + usage(); + linkf = link; + return(linkit(argv[0], argv[1], 0)); + exit(linkit(argv[0], argv[1], 0)); + } + + while ((ch = getopt(argc, argv, "Ffhinsv")) != -1) + switch (ch) { + case 'F': + Fflag = 1; + break; + case 'f': + fflag = 1; + iflag = 0; + break; + case 'h': + case 'n': + hflag = 1; + break; + case 'i': + iflag = 1; + fflag = 0; + break; + case 's': + sflag = 1; + break; + case 'v': + vflag = 1; + break; + case '?': + default: + usage(); + } + + argv += optind; + argc -= optind; + + linkf = sflag ? symlink : link; + linkch = sflag ? '-' : '='; + if (sflag == 0) + Fflag = 0; + if (Fflag == 1 && iflag == 0) + fflag = 1; + + switch(argc) { + case 0: + usage(); + /* NOTREACHED */ + case 1: /* ln target */ + exit(linkit(argv[0], ".", 1)); + case 2: /* ln target source */ + exit(linkit(argv[0], argv[1], 0)); + default: + ; + } + /* ln target1 target2 directory */ + sourcedir = argv[argc - 1]; + if (hflag && lstat(sourcedir, &sb) == 0 && S_ISLNK(sb.st_mode)) { + /* + * We were asked not to follow symlinks, but found one at + * the target--simulate "not a directory" error + */ + errno = ENOTDIR; + err(1, "%s", sourcedir); + } + if (stat(sourcedir, &sb)) { + err(1, "%s", sourcedir); + } + if (!S_ISDIR(sb.st_mode)) + usage(); + for (exitval = 0; *argv != sourcedir; ++argv) + exitval |= linkit(*argv, sourcedir, 1); + exit(exitval); +} + +static int +linkit(const char *target, const char *source, int isdir) +{ + struct stat sb; + const char *p; + int ch, exists, first; + char path[PATH_MAX]; + char bbuf[PATH_MAX]; + + if (!sflag) { + /* If target doesn't exist, quit now. */ + if (stat(target, &sb)) { + warn("%s", target); + return (1); + } + /* Only symbolic links to directories. */ + if (S_ISDIR(sb.st_mode)) { + errno = EISDIR; + warn("%s", target); + return (1); + } + } + + /* + * If the source is a directory (and not a symlink if hflag), + * append the target's name. + */ + if (isdir || + (lstat(source, &sb) == 0 && S_ISDIR(sb.st_mode)) || + (!hflag && stat(source, &sb) == 0 && S_ISDIR(sb.st_mode))) { + if (strlcpy(bbuf, target, sizeof(bbuf)) >= sizeof(bbuf) || + (p = basename(bbuf)) == NULL || + snprintf(path, sizeof(path), "%s/%s", source, p) >= + (ssize_t)sizeof(path)) { + errno = ENAMETOOLONG; + warn("%s", target); + return (1); + } + source = path; + } + + exists = !lstat(source, &sb); + /* + * If the file exists, then unlink it forcibly if -f was specified + * and interactively if -i was specified. + */ + if (fflag && exists) { + if (Fflag && S_ISDIR(sb.st_mode)) { + if (rmdir(source)) { + warn("%s", source); + return (1); + } + } else if (unlink(source)) { + warn("%s", source); + return (1); + } + } else if (iflag && exists) { + fflush(thread_stdout); + fprintf(thread_stderr, "replace %s? ", source); + fflush(thread_stderr); + + first = ch = getchar(); + while(ch != '\n' && ch != EOF) + ch = getchar(); + if (first != 'y' && first != 'Y') { + fprintf(thread_stderr, "not replaced\n"); + return (1); + } + + if (Fflag && S_ISDIR(sb.st_mode)) { + if (rmdir(source)) { + warn("%s", source); + return (1); + } + } else if (unlink(source)) { + warn("%s", source); + return (1); + } + } + + /* Attempt the link. */ + if ((*linkf)(target, source)) { + warn("%s", source); + return (1); + } + if (vflag) + (void)fprintf(thread_stdout, "%s %c> %s\n", source, linkch, target); + return (0); +} + +static void +usage(void) +{ + (void)fprintf(thread_stderr, "%s\n%s\n%s\n", + "usage: ln [-Ffhinsv] source_file [target_file]", + " ln [-Ffhinsv] source_file ... target_dir", + " link source_file target_file"); + exit(1); +} diff --git a/files/Sources/files/ln/symlink.7 b/files/Sources/files/ln/symlink.7 new file mode 100644 index 00000000..7c6c7576 --- /dev/null +++ b/files/Sources/files/ln/symlink.7 @@ -0,0 +1,457 @@ +.\"- +.\" Copyright (c) 1992, 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. +.\" +.\" @(#)symlink.7 8.3 (Berkeley) 3/31/94 +.\" $FreeBSD: src/bin/ln/symlink.7,v 1.30 2005/02/13 22:25:09 ru Exp $ +.\" +.Dd March 31, 1994 +.Dt SYMLINK 7 +.Os +.Sh NAME +.Nm symlink +.Nd symbolic link handling +.Sh SYMBOLIC LINK HANDLING +Symbolic links are files that act as pointers to other files. +To understand their behavior, you must first understand how hard links +work. +A hard link to a file is indistinguishable from the original file because +it is a reference to the object underlying the original file name. +Changes to a file are independent of the name used to reference the +file. +Hard links may not refer to directories and may not reference files +on different file systems. +A symbolic link contains the name of the file to which it is linked, +i.e., it is a pointer to another name, and not to an underlying object. +For this reason, symbolic links may reference directories and may span +file systems. +.Pp +Because a symbolic link and its referenced object coexist in the file system +name space, confusion can arise in distinguishing between the link itself +and the referenced object. +Historically, commands and system calls have adopted their own link +following conventions in a somewhat ad-hoc fashion. +Rules for more a uniform approach, as they are implemented in this system, +are outlined here. +It is important that local applications conform to these rules, too, +so that the user interface can be as consistent as possible. +.Pp +Symbolic links are handled either by operating on the link itself, +or by operating on the object referenced by the link. +In the latter case, +an application or system call is said to +.Dq follow +the link. +Symbolic links may reference other symbolic links, +in which case the links are dereferenced until an object that is +not a symbolic link is found, +a symbolic link which references a file which does not exist is found, +or a loop is detected. +(Loop detection is done by placing an upper limit on the number of +links that may be followed, and an error results if this limit is +exceeded.) +.Pp +There are three separate areas that need to be discussed. +They are as follows: +.Pp +.Bl -enum -compact -offset indent +.It +Symbolic links used as file name arguments for system calls. +.It +Symbolic links specified as command line arguments to utilities that +are not traversing a file tree. +.It +Symbolic links encountered by utilities that are traversing a file tree +(either specified on the command line or encountered as part of the +file hierarchy walk). +.El +.Ss System calls. +The first area is symbolic links used as file name arguments for +system calls. +.Pp +Except as noted below, all system calls follow symbolic links. +For example, if there were a symbolic link +.Dq Li slink +which pointed to a file named +.Dq Li afile , +the system call +.Dq Li open("slink" ...\&) +would return a file descriptor to the file +.Dq afile . +.Pp +There are nine system calls that do not follow links, and which operate +on the symbolic link itself. +They are: +.Xr lchflags 2 , +.Xr lchmod 2 , +.Xr lchown 2 , +.Xr lstat 2 , +.Xr lutimes 3 , +.Xr readlink 2 , +.Xr rename 2 , +.Xr rmdir 2 , +and +.Xr unlink 2 . +Because +.Xr remove 3 +is an alias for +.Xr unlink 2 , +it also does not follow symbolic links. +When +.Xr rmdir 2 +is applied to a symbolic link, it fails with the error +.Er ENOTDIR . +.Pp +The owner and group of an existing symbolic link can be changed by +means of the +.Xr lchown 2 +system call. +The flags, access permissions, owner/group and modification time of +an existing symbolic link can be changed by means of the +.Xr lchflags 2 , +.Xr lchmod 2 , +.Xr lchown 2 , +and +.Xr lutimes 3 +system calls, respectively. +Of these, only the flags are used by the system; +the access permissions and ownership are ignored. +.Pp +The +.Bx 4.4 +system differs from historical +.Bx 4 +systems in that the system call +.Xr chown 2 +has been changed to follow symbolic links. +The +.Xr lchown 2 +system call was added later when the limitations of the new +.Xr chown 2 +became apparent. +.Ss Commands not traversing a file tree. +The second area is symbolic links, specified as command line file +name arguments, to commands which are not traversing a file tree. +.Pp +Except as noted below, commands follow symbolic links named as command +line arguments. +For example, if there were a symbolic link +.Dq Li slink +which pointed to a file named +.Dq Li afile , +the command +.Dq Li cat slink +would display the contents of the file +.Dq Li afile . +.Pp +It is important to realize that this rule includes commands which may +optionally traverse file trees, e.g.\& the command +.Dq Li "chown file" +is included in this rule, while the command +.Dq Li "chown -R file" +is not. +(The latter is described in the third area, below.) +.Pp +If it is explicitly intended that the command operate on the symbolic +link instead of following the symbolic link, e.g., it is desired that +.Dq Li "chown slink" +change the ownership of the file that +.Dq Li slink +is, whether it is a symbolic link or not, the +.Fl h +option should be used. +In the above example, +.Dq Li "chown root slink" +would change the ownership of the file referenced by +.Dq Li slink , +while +.Dq Li "chown -h root slink" +would change the ownership of +.Dq Li slink +itself. +.Pp +There are four exceptions to this rule. +The +.Xr mv 1 +and +.Xr rm 1 +commands do not follow symbolic links named as arguments, +but respectively attempt to rename and delete them. +(Note, if the symbolic link references a file via a relative path, +moving it to another directory may very well cause it to stop working, +since the path may no longer be correct.) +.Pp +The +.Xr ls 1 +command is also an exception to this rule. +For compatibility with historic systems (when +.Nm ls +is not doing a tree walk, i.e., the +.Fl R +option is not specified), +the +.Nm ls +command follows symbolic links named as arguments if the +.Fl H +or +.Fl L +option is specified, +or if the +.Fl F , +.Fl d +or +.Fl l +options are not specified. +(The +.Nm ls +command is the only command where the +.Fl H +and +.Fl L +options affect its behavior even though it is not doing a walk of +a file tree.) +.Pp +The +.Xr file 1 +command is also an exception to this rule. +The +.Xr file 1 +command does not follow symbolic links named as argument by default. +The +.Xr file 1 +command does follow symbolic links named as argument if +.Fl L +option is specified. +.Pp +The +.Bx 4.4 +system differs from historical +.Bx 4 +systems in that the +.Nm chown +and +.Nm chgrp +commands follow symbolic links specified on the command line. +.Ss Commands traversing a file tree. +The following commands either optionally or always traverse file trees: +.Xr chflags 1 , +.Xr chgrp 1 , +.Xr chmod 1 , +.Xr cp 1 , +.Xr du 1 , +.Xr find 1 , +.Xr ls 1 , +.Xr pax 1 , +.Xr rm 1 , +.Xr tar 1 +and +.Xr chown 8 . +.Pp +It is important to realize that the following rules apply equally to +symbolic links encountered during the file tree traversal and symbolic +links listed as command line arguments. +.Pp +The first rule applies to symbolic links that reference files that are +not of type directory. +Operations that apply to symbolic links are performed on the links +themselves, but otherwise the links are ignored. +.Pp +The command +.Dq Li "rm -r slink directory" +will remove +.Dq Li slink , +as well as any symbolic links encountered in the tree traversal of +.Dq Li directory , +because symbolic links may be removed. +In no case will +.Nm rm +affect the file which +.Dq Li slink +references in any way. +.Pp +The second rule applies to symbolic links that reference files of type +directory. +Symbolic links which reference files of type directory are never +.Dq followed +by default. +This is often referred to as a +.Dq physical +walk, as opposed to a +.Dq logical +walk (where symbolic links referencing directories are followed). +.Pp +As consistently as possible, you can make commands doing a file tree +walk follow any symbolic links named on the command line, regardless +of the type of file they reference, by specifying the +.Fl H +(for +.Dq half\-logical ) +flag. +This flag is intended to make the command line name space look +like the logical name space. +(Note, for commands that do not always do file tree traversals, the +.Fl H +flag will be ignored if the +.Fl R +flag is not also specified.) +.Pp +For example, the command +.Dq Li "chown -HR user slink" +will traverse the file hierarchy rooted in the file pointed to by +.Dq Li slink . +Note, the +.Fl H +is not the same as the previously discussed +.Fl h +flag. +The +.Fl H +flag causes symbolic links specified on the command line to be +dereferenced both for the purposes of the action to be performed +and the tree walk, and it is as if the user had specified the +name of the file to which the symbolic link pointed. +.Pp +As consistently as possible, you can make commands doing a file tree +walk follow any symbolic links named on the command line, as well as +any symbolic links encountered during the traversal, regardless of +the type of file they reference, by specifying the +.Fl L +(for +.Dq logical ) +flag. +This flag is intended to make the entire name space look like +the logical name space. +(Note, for commands that do not always do file tree traversals, the +.Fl L +flag will be ignored if the +.Fl R +flag is not also specified.) +.Pp +For example, the command +.Dq Li "chown -LR user slink" +will change the owner of the file referenced by +.Dq Li slink . +If +.Dq Li slink +references a directory, +.Nm chown +will traverse the file hierarchy rooted in the directory that it +references. +In addition, if any symbolic links are encountered in any file tree that +.Nm chown +traverses, they will be treated in the same fashion as +.Dq Li slink . +.Pp +As consistently as possible, you can specify the default behavior by +specifying the +.Fl P +(for +.Dq physical ) +flag. +This flag is intended to make the entire name space look like the +physical name space. +.Pp +For commands that do not by default do file tree traversals, the +.Fl H , +.Fl L +and +.Fl P +flags are ignored if the +.Fl R +flag is not also specified. +In addition, you may specify the +.Fl H , +.Fl L +and +.Fl P +options more than once; the last one specified determines the +command's behavior. +This is intended to permit you to alias commands to behave one way +or the other, and then override that behavior on the command line. +.Pp +The +.Xr ls 1 +and +.Xr rm 1 +commands have exceptions to these rules. +The +.Nm rm +command operates on the symbolic link, and not the file it references, +and therefore never follows a symbolic link. +The +.Nm rm +command does not support the +.Fl H , +.Fl L +or +.Fl P +options. +.Pp +To maintain compatibility with historic systems, +the +.Nm ls +command acts a little differently. +If you do not specify the +.Fl F , +.Fl d +or +.Fl l +options, +.Nm ls +will follow symbolic links specified on the command line. +If the +.Fl L +flag is specified, +.Nm ls +follows all symbolic links, +regardless of their type, +whether specified on the command line or encountered in the tree walk. +.Sh SEE ALSO +.Xr chflags 1 , +.Xr chgrp 1 , +.Xr chmod 1 , +.Xr cp 1 , +.Xr du 1 , +.Xr find 1 , +.Xr ln 1 , +.Xr ls 1 , +.Xr mv 1 , +.Xr pax 1 , +.Xr rm 1 , +.Xr tar 1 , +.Xr lchflags 2 , +.Xr lchmod 2 , +.Xr lchown 2 , +.Xr lstat 2 , +.Xr lutimes 3 , +.Xr readlink 2 , +.Xr rename 2 , +.Xr symlink 2 , +.Xr unlink 2 , +.Xr fts 3 , +.Xr remove 3 , +.Xr chown 8 diff --git a/files/Sources/files/ls/cmp.c b/files/Sources/files/ls/cmp.c new file mode 100644 index 00000000..2ad5edd9 --- /dev/null +++ b/files/Sources/files/ls/cmp.c @@ -0,0 +1,226 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)cmp.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ +#endif +#include +__RCSID("$FreeBSD: src/bin/ls/cmp.c,v 1.12 2002/06/30 05:13:54 obrien Exp $"); + + +#include +#include + +#include +#include + +#include "ls.h" +#include "extern.h" + +#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) || \ + defined(_XOPEN_SOURCE) || defined(__NetBSD__) +#define ATIMENSEC_CMP(x, op, y) ((x)->st_atimensec op (y)->st_atimensec) +#define CTIMENSEC_CMP(x, op, y) ((x)->st_ctimensec op (y)->st_ctimensec) +#define MTIMENSEC_CMP(x, op, y) ((x)->st_mtimensec op (y)->st_mtimensec) +#define BTIMENSEC_CMP(x, op, y) ((x)->st_birthtimensec op (y)->st_birthtimensec) +#else +#define ATIMENSEC_CMP(x, op, y) \ + ((x)->st_atimespec.tv_nsec op (y)->st_atimespec.tv_nsec) +#define CTIMENSEC_CMP(x, op, y) \ + ((x)->st_ctimespec.tv_nsec op (y)->st_ctimespec.tv_nsec) +#define MTIMENSEC_CMP(x, op, y) \ + ((x)->st_mtimespec.tv_nsec op (y)->st_mtimespec.tv_nsec) +#define BTIMENSEC_CMP(x, op, y) \ + ((x)->st_birthtimespec.tv_nsec op (y)->st_birthtimespec.tv_nsec) +#endif + +int +namecmp(const FTSENT *a, const FTSENT *b) +{ + return (strcoll(a->fts_name, b->fts_name)); +} + +int +revnamecmp(const FTSENT *a, const FTSENT *b) +{ + return (strcoll(b->fts_name, a->fts_name)); +} + +int +modcmp(const FTSENT *a, const FTSENT *b) +{ + if (b->fts_statp->st_mtime > a->fts_statp->st_mtime) + return (1); + else if (b->fts_statp->st_mtime < a->fts_statp->st_mtime) + return (-1); + else if (MTIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (1); + else if (MTIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (-1); + else + return (namecmp(a, b)); +} + +int +revmodcmp(const FTSENT *a, const FTSENT *b) +{ + if (b->fts_statp->st_mtime > a->fts_statp->st_mtime) + return (-1); + else if (b->fts_statp->st_mtime < a->fts_statp->st_mtime) + return (1); + else if (MTIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (-1); + else if (MTIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (1); + else + return (revnamecmp(a, b)); +} + +int +acccmp(const FTSENT *a, const FTSENT *b) +{ + if (b->fts_statp->st_atime > a->fts_statp->st_atime) + return (1); + else if (b->fts_statp->st_atime < a->fts_statp->st_atime) + return (-1); + else if (ATIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (1); + else if (ATIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (-1); + else + return (namecmp(a, b)); +} + +int +revacccmp(const FTSENT *a, const FTSENT *b) +{ + if (b->fts_statp->st_atime > a->fts_statp->st_atime) + return (-1); + else if (b->fts_statp->st_atime < a->fts_statp->st_atime) + return (1); + else if (ATIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (-1); + else if (ATIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (1); + else + return (revnamecmp(a, b)); +} + +int +statcmp(const FTSENT *a, const FTSENT *b) +{ + if (b->fts_statp->st_ctime > a->fts_statp->st_ctime) + return (1); + else if (b->fts_statp->st_ctime < a->fts_statp->st_ctime) + return (-1); + else if (CTIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (1); + else if (CTIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (-1); + else + return (namecmp(a, b)); +} + +int +revstatcmp(const FTSENT *a, const FTSENT *b) +{ + if (b->fts_statp->st_ctime > a->fts_statp->st_ctime) + return (-1); + else if (b->fts_statp->st_ctime < a->fts_statp->st_ctime) + return (1); + else if (CTIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (-1); + else if (CTIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (1); + else + return (revnamecmp(a, b)); +} + +int +sizecmp(a, b) + const FTSENT *a, *b; +{ + if (b->fts_statp->st_size > a->fts_statp->st_size) + return (1); + if (b->fts_statp->st_size < a->fts_statp->st_size) + return (-1); + else + return (namecmp(a, b)); +} + +int +revsizecmp(a, b) + const FTSENT *a, *b; +{ + if (b->fts_statp->st_size > a->fts_statp->st_size) + return (-1); + if (b->fts_statp->st_size < a->fts_statp->st_size) + return (1); + else + return (revnamecmp(a, b)); +} + +int +birthcmp(const FTSENT *a, const FTSENT *b) +{ + if (b->fts_statp->st_birthtime > a->fts_statp->st_birthtime) + return (1); + else if (b->fts_statp->st_birthtime < a->fts_statp->st_birthtime) + return (-1); + else if (BTIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (1); + else if (BTIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (-1); + else + return (namecmp(a, b)); +} + +int +revbirthcmp(const FTSENT *a, const FTSENT *b) +{ + if (b->fts_statp->st_birthtime > a->fts_statp->st_birthtime) + return (-1); + else if (b->fts_statp->st_birthtime < a->fts_statp->st_birthtime) + return (1); + else if (BTIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (-1); + else if (BTIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (1); + else + return (revnamecmp(a, b)); +} diff --git a/files/Sources/files/ls/extern.h b/files/Sources/files/ls/extern.h new file mode 100644 index 00000000..93d4691d --- /dev/null +++ b/files/Sources/files/ls/extern.h @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 1991, 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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. + * + * from: @(#)extern.h 8.1 (Berkeley) 5/31/93 + * $FreeBSD: src/bin/ls/extern.h,v 1.19 2002/05/19 02:51:36 tjr Exp $ + */ + +int acccmp(const FTSENT *, const FTSENT *); +int revacccmp(const FTSENT *, const FTSENT *); +int modcmp(const FTSENT *, const FTSENT *); +int revmodcmp(const FTSENT *, const FTSENT *); +int namecmp(const FTSENT *, const FTSENT *); +int revnamecmp(const FTSENT *, const FTSENT *); +int statcmp(const FTSENT *, const FTSENT *); +int revstatcmp(const FTSENT *, const FTSENT *); +int sizecmp (const FTSENT *, const FTSENT *); +int revsizecmp (const FTSENT *, const FTSENT *); +int birthcmp(const FTSENT *, const FTSENT *); +int revbirthcmp(const FTSENT *, const FTSENT *); + +void printcol(DISPLAY *); +void printlong(DISPLAY *); +void printscol(DISPLAY *); +void printstream(DISPLAY *); +void ls_usage(void); +int prn_normal(const char *); +size_t len_octal(const char *, int); +int prn_octal(const char *); +int prn_printable(const char *); +#ifdef COLORLS +void parsecolors(const char *cs); +void colorquit(int); + +extern char *ansi_fgcol; +extern char *ansi_bgcol; +extern char *ansi_coloff; +extern char *attrs_off; +extern char *enter_bold; +#endif diff --git a/files/Sources/files/ls/ls.1 b/files/Sources/files/ls/ls.1 new file mode 100644 index 00000000..12e8af53 --- /dev/null +++ b/files/Sources/files/ls/ls.1 @@ -0,0 +1,706 @@ +.\" Copyright (c) 1980, 1990, 1991, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgment: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 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. +.\" +.\" @(#)ls.1 8.7 (Berkeley) 7/29/94 +.\" $FreeBSD: src/bin/ls/ls.1,v 1.69 2002/08/21 17:32:34 trhodes Exp $ +.\" +.Dd May 19, 2002 +.Dt LS 1 +.Os +.Sh NAME +.Nm ls +.Nd list directory contents +.Sh SYNOPSIS +.Nm ls +.Op Fl ABCFGHLOPRSTUW@abcdefghiklmnopqrstuwx1 +.Op Ar +.Sh DESCRIPTION +For each operand that names a +.Ar file +of a type other than +directory, +.Nm ls +displays its name as well as any requested, +associated information. +For each operand that names a +.Ar file +of type directory, +.Nm ls +displays the names of files contained +within that directory, as well as any requested, associated +information. +.Pp +If no operands are given, the contents of the current +directory are displayed. +If more than one operand is given, +non-directory operands are displayed first; directory +and non-directory operands are sorted separately and in +lexicographical order. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl @ +Display extended attribute keys and sizes in long +.Pq Fl l +output. +.It Fl 1 +(The numeric digit +.Dq one . ) +Force output to be +one entry per line. +This is the default when +output is not to a terminal. +.It Fl A +List all entries except for +.Pa \&. +and +.Pa .. . +Always set for the super-user. +.It Fl a +Include directory entries whose names begin with a +dot +.Pq Pa \&. . +.It Fl B +Force printing of non-printable characters (as defined by +.Xr ctype 3 +and current locale settings) in file names as +.Li \e Ns Va xxx , +where +.Va xxx +is the numeric value of the character in octal. +.It Fl b +As +.Fl B , +but use +.Tn C +escape codes whenever possible. +.It Fl C +Force multi-column output; this is the default when output is to a terminal. +.It Fl c +Use time when file status was last changed for sorting +.Pq Fl t +or long printing +.Pq Fl l . +.It Fl d +Directories are listed as plain files (not searched recursively). +.It Fl e +Print the Access Control List (ACL) associated with the file, if present, in long +.Pq Fl l +output. +.It Fl F +Display a slash +.Pq Ql / +immediately after each pathname that is a directory, +an asterisk +.Pq Ql * +after each that is executable, +an at sign +.Pq Ql @ +after each symbolic link, +an equals sign +.Pq Ql = +after each socket, +a percent sign +.Pq Ql % +after each whiteout, +and a vertical bar +.Pq Ql \&| +after each that is a +.Tn FIFO . +.It Fl f +Output is not sorted. +This option turns on the +.Fl a +option. +.It Fl G +Enable colorized output. +This option is equivalent to defining +.Ev CLICOLOR +in the environment. +(See below.) +.It Fl g +This option is only available for compatibility with POSIX; +it is used to display the group name in the long +.Pq Fl l +format output (the owner name is suppressed). +.It Fl H +Symbolic links on the command line are followed. +This option is assumed if +none of the +.Fl F , d , +or +.Fl l +options are specified. +.It Fl h +When used with the +.Fl l +option, use unit suffixes: Byte, Kilobyte, Megabyte, Gigabyte, Terabyte +and Petabyte in order to reduce the number of digits to three or less +using base 2 for sizes. +.It Fl i +For each file, print the file's file serial number (inode number). +.It Fl k +If the +.Fl s +option is specified, print the file size allocation in kilobytes, +not blocks. +This option overrides the environment variable +.Ev BLOCKSIZE . +.It Fl L +Follow all symbolic links to final target and list the file or directory the link references +rather than the link itself. +This option cancels the +.Fl P +option. +.It Fl l +(The lowercase letter +.Dq ell . ) +List in long format. +(See below.) +If the output is to a terminal, a total sum for all the file +sizes is output on a line before the long listing. +.It Fl m +Stream output format; list files across the page, separated by commas. +.It Fl n +Display user and group IDs numerically, +rather than converting to a user or group name in a long +.Pq Fl l +output. +This option turns on the +.Fl l +option. +.It Fl O +Include the file flags in a long +.Pq Fl l +output. +.It Fl o +List in long format, but omit the group id. +.It Fl P +If argument is a symbolic link, list the link itself rather than the +object the link references. +This option cancels the +.Fl H +and +.Fl L +options. +.It Fl p +Write a slash +.Pq Ql / +after each filename if that file is a directory. +.It Fl q +Force printing of non-graphic characters in file names as +the character +.Ql \&? ; +this is the default when output is to a terminal. +.It Fl R +Recursively list subdirectories encountered. +.It Fl r +Reverse the order of the sort to get reverse +lexicographical order or the oldest entries first (or largest files +last, if combined with sort by size +.It Fl S +Sort files by size +.It Fl s +Display the number of file system blocks actually used by each file, in units +of 512 bytes, where partial units are rounded up to the next integer value. +If the output is to a terminal, a total sum for all the file +sizes is output on a line before the listing. +The environment variable +.Ev BLOCKSIZE +overrides the unit size of 512 bytes. +.It Fl T +When used with the +.Fl l +(lowercase letter +.Dq ell ) +option, display complete time information for the file, including +month, day, hour, minute, second, and year. +.It Fl t +Sort by time modified (most recently modified +first) before sorting the operands by lexicographical +order. +.It Fl u +Use time of last access, +instead of last modification +of the file for sorting +.Pq Fl t +or long printing +.Pq Fl l . +.It Fl U +Use time of file creation, instead of last modification for sorting +.Pq Fl t +or long output +.Pq Fl l . +.It Fl v +Force unedited printing of non-graphic characters; this is the default when +output is not to a terminal. +.It Fl W +Display whiteouts when scanning directories. +.Pq Fl S +flag). +.It Fl w +Force raw printing of non-printable characters. +This is the default +when output is not to a terminal. +.It Fl x +The same as +.Fl C , +except that the multi-column output is produced with entries sorted +across, rather than down, the columns. +.El +.Pp +The +.Fl 1 , C , x , +and +.Fl l +options all override each other; +the last one specified determines the format used. +.Pp +The +.Fl c +and +.Fl u +options override each other; the last one specified determines +the file time used. +.Pp +The +.Fl B , b , w , +and +.Fl q +options all override each other; +the last one specified determines the format used +for non-printable characters. +.Pp +The +.Fl H , L +and +.Fl P +options all override each other (either partially or fully); they +are applied in the order specified. +.Pp +By default, +.Nm ls +lists one entry per line to standard +output; the exceptions are to terminals or when the +.Fl C +or +.Fl x +options are specified. +.Pp +File information is displayed with one or more +.Ao blank Ac Ns s +separating the information associated with the +.Fl i , s , +and +.Fl l +options. +.Ss The Long Format +If the +.Fl l +option is given, the following information +is displayed for each file: +file mode, +number of links, owner name, group name, +number of bytes in the file, abbreviated +month, day-of-month file was last modified, +hour file last modified, minute file last +modified, and the pathname. +In addition, for each directory whose contents are displayed, +the total number of 512-byte blocks used by the files in the directory +is displayed on a line by itself, +immediately before the information for the files in the directory. +If the file or directory has extended attributes, +the permissions field printed by the +.Fl l +option is followed by a '@' character. +Otherwise, if the file or directory has extended security information +(such as an access control list), +the permissions field printed by the +.Fl l +option is followed by a '+' character. +.Pp +If the modification time of the file +is more than 6 months in the past or future, +then the year of the last modification +is displayed in place of the hour and minute fields. +.Pp +If the owner or group names are not a known user or group name, +or the +.Fl n +option is given, +the numeric ID's are displayed. +.Pp +If the file is a character special or block special file, +the major and minor device numbers for the file are displayed +in the size field. +If the file is a symbolic link, +the pathname of the linked-to file is preceded by +.Dq Li -> . +.Pp +The file mode printed under the +.Fl l +option consists of the +entry type, owner permissions, and group permissions. +The entry type character describes the type of file, +as follows: +.Pp +.Bl -tag -width 4n -offset indent -compact +.It Sy b +Block special file. +.It Sy c +Character special file. +.It Sy d +Directory. +.It Sy l +Symbolic link. +.It Sy s +Socket link. +.It Sy p +.Tn FIFO . +.It Sy \- +Regular file. +.El +.Pp +The next three fields +are three characters each: +owner permissions, +group permissions, and +other permissions. +Each field has three character positions: +.Bl -enum -offset indent +.It +If +.Sy r , +the file is readable; if +.Sy \- , +it is not readable. +.It +If +.Sy w , +the file is writable; if +.Sy \- , +it is not writable. +.It +The first of the following that applies: +.Bl -tag -width 4n -offset indent +.It Sy S +If in the owner permissions, the file is not executable and +set-user-ID mode is set. +If in the group permissions, the file is not executable +and set-group-ID mode is set. +.It Sy s +If in the owner permissions, the file is executable +and set-user-ID mode is set. +If in the group permissions, the file is executable +and setgroup-ID mode is set. +.It Sy x +The file is executable or the directory is +searchable. +.It Sy \- +The file is neither readable, writable, executable, +nor set-user-ID nor set-group-ID mode, nor sticky. +(See below.) +.El +.Pp +These next two apply only to the third character in the last group +(other permissions). +.Bl -tag -width 4n -offset indent +.It Sy T +The sticky bit is set +(mode +.Li 1000 ) , +but not execute or search permission. +(See +.Xr chmod 1 +or +.Xr sticky 8 . ) +.It Sy t +The sticky bit is set (mode +.Li 1000 ) , +and is searchable or executable. +(See +.Xr chmod 1 +or +.Xr sticky 8 . ) +.El +.El +.Sh EXAMPLES +The following is how to do an +.Nm ls +listing sorted by increasing size +.Pp +.Dl "ls -lrS" +.Sh DIAGNOSTICS +.Ex -std +.Sh ENVIRONMENT +The following environment variables affect the execution of +.Nm ls : +.Bl -tag -width ".Ev CLICOLOR_FORCE" +.It Ev BLOCKSIZE +If the environment variable +.Ev BLOCKSIZE +is set, the block counts +(see +.Fl s ) +will be displayed in units of that size block. +.It Ev CLICOLOR +Use +\*[Ai] +color sequences to distinguish file types. +See +.Ev LSCOLORS +below. +In addition to the file types mentioned in the +.Fl F +option some extra attributes (setuid bit set, etc.) are also displayed. +The colorization is dependent on a terminal type with the proper +.Xr termcap 5 +capabilities. +The default +.Dq Li cons25 +console has the proper capabilities, +but to display the colors in an +.Xr xterm 1 , +for example, +the +.Ev TERM +variable must be set to +.Dq Li xterm-color . +Other terminal types may require similar adjustments. +Colorization +is silently disabled if the output isn't directed to a terminal +unless the +.Ev CLICOLOR_FORCE +variable is defined. +.It Ev CLICOLOR_FORCE +Color sequences are normally disabled if the output isn't directed to +a terminal. +This can be overridden by setting this flag. +The +.Ev TERM +variable still needs to reference a color capable terminal however +otherwise it is not possible to determine which color sequences to +use. +.It Ev COLUMNS +If this variable contains a string representing a +decimal integer, it is used as the +column position width for displaying +multiple-text-column output. +The +.Nm ls +utility calculates how +many pathname text columns to display +based on the width provided. +(See +.Fl C +and +.Fl x . ) +.It Ev LANG +The locale to use when determining the order of day and month in the long +.Fl l +format output. +See +.Xr environ 7 +for more information. +.It Ev LSCOLORS +The value of this variable describes what color to use for which +attribute when colors are enabled with +.Ev CLICOLOR . +This string is a concatenation of pairs of the format +.Ar f Ns Ar b , +where +.Ar f +is the foreground color and +.Ar b +is the background color. +.Pp +The color designators are as follows: +.Pp +.Bl -tag -width 4n -offset indent -compact +.It Sy a +black +.It Sy b +red +.It Sy c +green +.It Sy d +brown +.It Sy e +blue +.It Sy f +magenta +.It Sy g +cyan +.It Sy h +light grey +.It Sy A +bold black, usually shows up as dark grey +.It Sy B +bold red +.It Sy C +bold green +.It Sy D +bold brown, usually shows up as yellow +.It Sy E +bold blue +.It Sy F +bold magenta +.It Sy G +bold cyan +.It Sy H +bold light grey; looks like bright white +.It Sy x +default foreground or background +.El +.Pp +Note that the above are standard +\*[Ai] +colors. +The actual display may differ +depending on the color capabilities of the terminal in use. +.Pp +The order of the attributes are as follows: +.Pp +.Bl -enum -offset indent -compact +.It +directory +.It +symbolic link +.It +socket +.It +pipe +.It +executable +.It +block special +.It +character special +.It +executable with setuid bit set +.It +executable with setgid bit set +.It +directory writable to others, with sticky bit +.It +directory writable to others, without sticky bit +.El +.Pp +The default is +.Qq "exfxcxdxbxegedabagacad" , +i.e. blue foreground and +default background for regular directories, black foreground and red +background for setuid executables, etc. +.It Ev LS_COLWIDTHS +If this variable is set, it is considered to be a +colon-delimited list of minimum column widths. +Unreasonable +and insufficient widths are ignored (thus zero signifies +a dynamically sized column). +Not all columns have changeable widths. +The fields are, +in order: inode, block count, number of links, user name, +group name, flags, file size, file name. +.It Ev TERM +The +.Ev CLICOLOR +functionality depends on a terminal type with color capabilities. +.It Ev TZ +The timezone to use when displaying dates. +See +.Xr environ 7 +for more information. +.El +.Sh COMPATIBILITY +The group field is now automatically included in the long listing for +files in order to be compatible with the +.St -p1003.2 +specification. +.Sh LEGACY DESCRIPTION +In legacy mode, the +.Fl f +option does not turn on the +.Fl a +option and the +.Fl g , +.Fl n , +and +.Fl o +options do not turn on the +.Fl l +option. +.Pp +Also, the +.Fl o +option causes the file flags to be included in a long (-l) output; +there is no +.Fl O +option. +.Pp +When +.Fl H +is specified (and not overridden by +.Fl L +or +.Fl P ) +and a file argument is a symlink +that resolves to a non-directory file, +the output will reflect the nature of the link, +rather than that of the file. +In legacy operation, the output will describe the file. +.Pp +For more information about legacy mode, see +.Xr compat 5 . +.Sh SEE ALSO +.Xr chflags 1 , +.Xr chmod 1 , +.Xr sort 1 , +.Xr xterm 1 , +.Xr compat 5 , +.Xr termcap 5 , +.Xr symlink 7 , +.Xr sticky 8 +.Sh STANDARDS +The +.Nm ls +utility conforms to +.St -p1003.1-2001 . +.Sh HISTORY +An +.Nm ls +command appeared in +.At v1 . +.Sh BUGS +To maintain backward compatibility, the relationships between the many +options are quite complex. diff --git a/files/Sources/files/ls/ls.c b/files/Sources/files/ls/ls.c new file mode 100644 index 00000000..a4745b29 --- /dev/null +++ b/files/Sources/files/ls/ls.c @@ -0,0 +1,1021 @@ +/* + * 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 + * Michael Fischbein. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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 +#ifndef lint +__used static const char copyright[] = +"@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)ls.c 8.5 (Berkeley) 4/2/94"; +#endif /* not lint */ +#endif +#include +__RCSID("$FreeBSD: src/bin/ls/ls.c,v 1.66 2002/09/21 01:28:36 wollman Exp $"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef COLORLS +#include "termcap.h" +#include +#endif +#ifdef __APPLE__ +#include +#include +#include +// #include +// #else +#define COMPAT_MODE(a,b) (1) +#endif /* __APPLE__ */ +#include "ls.h" +#include "extern.h" +#include "ios_error.h" + +/* + * Upward approximation of the maximum number of characters needed to + * represent a value of integral type t as a string, excluding the + * NUL terminator, with provision for a sign. + */ +#define STRBUF_SIZEOF(t) (1 + CHAR_BIT * sizeof(t) / 3 + 1) + +static void display(FTSENT *, FTSENT *); +static u_quad_t makenines(u_quad_t); +static int mastercmp(const FTSENT **, const FTSENT **); +static void traverse(int, char **, int); + +static void (*printfcn)(DISPLAY *); +static int (*sortfcn)(const FTSENT *, const FTSENT *); + +__thread long blocksize; /* block size units */ +__thread int termwidth = 80; /* default terminal width */ +static int output; /* If anything output. */ + +/* flags */ +__thread int f_accesstime; /* use time of last access */ +__thread int f_birthtime; /* use time of file birth */ +__thread int f_flags; /* show flags associated with a file */ +__thread int f_humanval; /* show human-readable file sizes */ +__thread int f_inode; /* print inode */ +static int f_kblocks; /* print size in kilobytes */ +static int f_listdir; /* list actual directory, not contents */ +static int f_listdot; /* list files beginning with . */ +__thread int f_longform; /* long listing format */ +__thread int f_nonprint; /* show unprintables as ? */ +static int f_nosort; /* don't sort output */ +__thread int f_notabs; /* don't use tab-separated multi-col output */ +__thread int f_numericonly; /* don't convert uid/gid to name */ +__thread int f_octal; /* show unprintables as \xxx */ +__thread int f_octal_escape; /* like f_octal but use C escapes if possible */ +static int f_recursive; /* ls subdirectories also */ +static int f_reversesort; /* reverse whatever sort is used */ +__thread int f_sectime; /* print the real time for all files */ +static int f_singlecol; /* use single column output */ +__thread int f_size; /* list size in short listing */ +__thread int f_slash; /* similar to f_type, but only for dirs */ +__thread int f_sortacross; /* sort across rows, not down columns */ +__thread int f_statustime; /* use time of last mode change */ +__thread int f_stream; /* stream the output, separate with commas */ +static int f_timesort; /* sort by time vice name */ +static int f_sizesort; /* sort by size */ +__thread int f_type; /* add type character for non-regular files */ +static int f_whiteout; /* show whiteout entries */ +__thread int f_acl; /* show ACLs in long listing */ +__thread int f_xattr; /* show extended attributes in long listing */ +__thread int f_group; /* show group */ +__thread int f_owner; /* show owner */ +#ifdef COLORLS +__thread int f_color; /* add type in color for non-regular files */ + +char *ansi_bgcol; /* ANSI sequence to set background colour */ +char *ansi_fgcol; /* ANSI sequence to set foreground colour */ +char *ansi_coloff; /* ANSI sequence to reset colours */ +char *attrs_off; /* ANSI sequence to turn off attributes */ +char *enter_bold; /* ANSI sequence to set color to bold mode */ +#endif + +static void initializeAllFlags() +{ + termwidth = 80; + f_accesstime = 0; /* use time of last access */ + f_birthtime = 0; /* use time of file birth */ + f_flags = 0; /* show flags associated with a file */ + f_humanval = 0; /* show human-readable file sizes */ + f_inode = 0; /* print inode */ + f_kblocks = 0; /* print size in kilobytes */ + f_listdir = 0; /* list actual directory, not contents */ + f_listdot = 0; /* list files beginning with . */ + f_longform = 0; /* long listing format */ + f_nonprint = 0; /* show unprintables as ? */ + f_nosort = 0; /* don't sort output */ + f_notabs = 0; /* don't use tab-separated multi-col output */ + f_numericonly = 0; /* don't convert uid/gid to name */ + f_octal = 0; /* show unprintables as \xxx */ + f_octal_escape = 0; /* like f_octal but use C escapes if possible */ + f_recursive = 0; /* ls subdirectories also */ + f_reversesort = 0; /* reverse whatever sort is used */ + f_sectime = 0; /* print the real time for all files */ + f_singlecol = 0; /* use single column output */ + f_size = 0; /* list size in short listing */ + f_slash = 0; /* similar to f_type, but only for dirs */ + f_sortacross = 0; /* sort across rows, not down columns */ + f_statustime = 0; /* use time of last mode change */ + f_stream = 0; /* stream the output, separate with commas */ + f_timesort = 0; /* sort by time vice name */ + f_sizesort = 0; /* sort by size */ + f_type = 0; /* add type character for non-regular files */ + f_whiteout = 0; /* show whiteout entries */ + f_acl = 0; /* show ACLs in long listing */ + f_xattr = 0; /* show extended attributes in long listing */ + f_group = 0; /* show group */ + f_owner = 0; /* show owner */ +#ifdef COLORLS + f_color = 0; /* add type in color for non-regular files */ +#endif + optind = 1; + opterr = 1; + optreset = 1; + output = 0; +} + + +static int rval; + +int +ls_main(int argc, char *argv[]) +{ + static char dot[] = ".", *dotav[] = {dot, NULL}; + struct winsize win; + int ch, fts_options, notused; + char *p; +#ifdef COLORLS + char termcapbuf[1024]; /* termcap definition buffer */ + char tcapbuf[512]; /* capability buffer */ + char *bp = tcapbuf; +#endif + + if (argc < 1) + ls_usage(); + // re-initialize all flags: + initializeAllFlags(); + rval = 0; + (void)setlocale(LC_ALL, ""); + + /* Terminal defaults to -Cq, non-terminal defaults to -1. */ + // iOS: we *are* a terminal, but file(thread_stdout) doesn't tell it + if (ios_isatty(STDOUT_FILENO)) { + termwidth = 80; + if ((p = getenv("COLUMNS")) != NULL && *p != '\0') + termwidth = atoi(p); + else if (ioctl(fileno(thread_stdout), TIOCGWINSZ, &win) != -1 && + win.ws_col > 0) + termwidth = win.ws_col; + f_nonprint = 1; + } else { + f_singlecol = 1; + /* retrieve environment variable, in case of explicit -C */ + p = getenv("COLUMNS"); + if (p) + termwidth = atoi(p); + } + + /* Root is -A automatically. */ + if (!getuid()) + f_listdot = 1; + + fts_options = FTS_PHYSICAL; + while ((ch = getopt(argc, argv, "1@ABCFGHLOPRSTUWabcdefghiklmnopqrstuvwx")) + != -1) { + switch (ch) { + /* + * The -1, -C, -x and -l options all override each other so + * shell aliasing works right. + */ + case '1': + f_singlecol = 1; + f_longform = 0; + f_stream = 0; + break; + case 'B': + f_nonprint = 0; + f_octal = 1; + f_octal_escape = 0; + break; + case 'C': + f_sortacross = f_longform = f_singlecol = 0; + break; + case 'l': + f_longform = 1; + f_singlecol = 0; + f_stream = 0; + break; + case 'x': + f_sortacross = 1; + f_longform = 0; + f_singlecol = 0; + break; + /* The -c and -u options override each other. */ + case 'c': + f_statustime = 1; + f_accesstime = f_birthtime = 0; + break; + case 'u': + f_accesstime = 1; + f_statustime = f_birthtime = 0; + break; + case 'U': + f_birthtime = 1; + f_statustime = f_accesstime = 0; + break; + case 'F': + f_type = 1; + f_slash = 0; + break; + case 'H': + if (COMPAT_MODE("bin/ls", "Unix2003")) { + fts_options &= ~FTS_LOGICAL; + fts_options |= FTS_PHYSICAL; + fts_options |= FTS_COMFOLLOWDIR; + } else + fts_options |= FTS_COMFOLLOW; + break; + case 'G': + setenv("CLICOLOR", "", 1); + break; + case 'L': + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + if (COMPAT_MODE("bin/ls", "Unix2003")) { + fts_options &= ~(FTS_COMFOLLOW|FTS_COMFOLLOWDIR); + } + break; + case 'P': + fts_options &= ~(FTS_COMFOLLOW|FTS_COMFOLLOWDIR); + fts_options &= ~FTS_LOGICAL; + fts_options |= FTS_PHYSICAL; + break; + case 'R': + f_recursive = 1; + break; + case 'a': + fts_options |= FTS_SEEDOT; + /* FALLTHROUGH */ + case 'A': + f_listdot = 1; + break; + /* The -d option turns off the -R option. */ + case 'd': + f_listdir = 1; + f_recursive = 0; + break; + case 'f': + f_nosort = 1; + if (COMPAT_MODE("bin/ls", "Unix2003")) { + fts_options |= FTS_SEEDOT; + f_listdot = 1; + } + break; + case 'g': /* Compatibility with Unix03 */ + if (COMPAT_MODE("bin/ls", "Unix2003")) { + f_group = 1; + f_longform = 1; + f_singlecol = 0; + f_stream = 0; + } + break; + case 'h': + f_humanval = 1; + break; + case 'i': + f_inode = 1; + break; + case 'k': + f_kblocks = 1; + break; + case 'm': + f_stream = 1; + f_singlecol = 0; + f_longform = 0; + break; + case 'n': + f_numericonly = 1; + if (COMPAT_MODE("bin/ls", "Unix2003")) { + f_longform = 1; + f_singlecol = 0; + f_stream = 0; + } + break; + case 'o': + if (COMPAT_MODE("bin/ls", "Unix2003")) { + f_owner = 1; + f_longform = 1; + f_singlecol = 0; + f_stream = 0; + } else { + f_flags = 1; + } + break; + case 'p': + f_slash = 1; + f_type = 1; + break; + case 'q': + f_nonprint = 1; + f_octal = 0; + f_octal_escape = 0; + break; + case 'r': + f_reversesort = 1; + break; + case 'S': + /* Darwin 1.4.1 compatibility */ + f_sizesort = 1; + break; + case 's': + f_size = 1; + break; + case 'T': + f_sectime = 1; + break; + case 't': + f_timesort = 1; + break; + case 'W': + f_whiteout = 1; + break; + case 'v': + /* Darwin 1.4.1 compatibility */ + f_nonprint = 0; + break; + case 'b': + f_nonprint = 0; + f_octal = 0; + f_octal_escape = 1; + break; + case 'w': + f_nonprint = 0; + f_octal = 0; + f_octal_escape = 0; + break; + case 'e': + f_acl = 1; + break; + case '@': + f_xattr = 1; + break; + case 'O': + f_flags = 1; + break; + default: + case '?': + ls_usage(); + } + } + argc -= optind; + argv += optind; + + /* Enabling of colours is conditional on the environment. */ + if (getenv("CLICOLOR") && + ((ios_isatty(STDOUT_FILENO)) || getenv("CLICOLOR_FORCE"))) { +#ifdef COLORLS +#ifndef TARGET_OS_IPHONE + if (tgetent(termcapbuf, getenv("TERM")) == 1) { + ansi_fgcol = tgetstr("AF", &bp); + ansi_bgcol = tgetstr("AB", &bp); + attrs_off = tgetstr("me", &bp); + enter_bold = tgetstr("md", &bp); + + /* To switch colours off use 'op' if + * available, otherwise use 'oc', or + * don't do colours at all. */ + ansi_coloff = tgetstr("op", &bp); + if (!ansi_coloff) + ansi_coloff = tgetstr("oc", &bp); + if (ansi_fgcol && ansi_bgcol && ansi_coloff) + f_color = 1; + } +#else /* TARGET_OS_IPHONE */ + ansi_fgcol = "\033[3" ; // tgetstr("AF", &bp); + ansi_bgcol = "\033[4" ; // tgetstr("AB", &bp); + attrs_off = "\033[0m"; // tgetstr("me", &bp); + enter_bold = "\033[1m"; // tgetstr("md", &bp); + ansi_coloff = "\033[39;49m"; // tgetstr("op", &bp); + if (ansi_fgcol && ansi_bgcol && ansi_coloff) + f_color = 1; +#endif + } +#else + (void)fprintf(thread_stderr, "Color support not compiled in.\n"); +#endif /*COLORLS*/ + +#ifdef COLORLS + if (f_color) { + /* + * We can't put tabs and color sequences together: + * column number will be incremented incorrectly + * for "stty oxtabs" mode. + */ +#ifndef TARGET_OS_IPHONE + f_notabs = 1; +#endif + // (void)signal(SIGINT, colorquit); + // (void)signal(SIGQUIT, colorquit); + parsecolors(getenv("LSCOLORS")); + } +#endif + + /* + * If not -F, -i, -l, -s or -t options, don't require stat + * information, unless in color mode in which case we do + * need this to determine which colors to display. + */ + if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type && !f_sizesort +#ifdef COLORLS + && !f_color +#endif + ) + fts_options |= FTS_NOSTAT; + + /* + * If not -F, -d or -l options, follow any symbolic links listed on + * the command line. + */ + if (!f_longform && !f_listdir && !f_type && !f_inode) + fts_options |= FTS_COMFOLLOW; + + /* + * If -W, show whiteout entries + */ +#ifdef FTS_WHITEOUT + if (f_whiteout) + fts_options |= FTS_WHITEOUT; +#endif + + /* If -l or -s, figure out block size. */ + if (f_longform || f_size) { + if (f_kblocks) + blocksize = 2; + else { + (void)getbsize(¬used, &blocksize); + blocksize /= 512; + } + } + /* Select a sort function. */ + if (f_reversesort) { + if (f_sizesort) + sortfcn = revsizecmp; + else if (!f_timesort) + sortfcn = revnamecmp; + else if (f_accesstime) + sortfcn = revacccmp; + else if (f_statustime) + sortfcn = revstatcmp; + else if (f_birthtime) + sortfcn = revbirthcmp; + else /* Use modification time. */ + sortfcn = revmodcmp; + } else { + if (f_sizesort) + sortfcn = sizecmp; + else if (!f_timesort) + sortfcn = namecmp; + else if (f_accesstime) + sortfcn = acccmp; + else if (f_statustime) + sortfcn = statcmp; + else if (f_birthtime) + sortfcn = birthcmp; + else /* Use modification time. */ + sortfcn = modcmp; + } + + /* Select a print function. */ + if (f_singlecol) + printfcn = printscol; + else if (f_longform) + printfcn = printlong; + else if (f_stream) + printfcn = printstream; + else + printfcn = printcol; + + if (argc) + traverse(argc, argv, fts_options); + else + traverse(1, dotav, fts_options); + // reset signal: + (void)signal(SIGINT, SIG_DFL); + (void)signal(SIGQUIT, SIG_DFL); + exit(rval); +} + + +/* + * Traverse() walks the logical directory structure specified by the argv list + * in the order specified by the mastercmp() comparison function. During the + * traversal it passes linked lists of structures to display() which represent + * a superset (may be exact set) of the files to be displayed. + */ +static void +traverse(int argc, char *argv[], int options) +{ + FTS *ftsp; + FTSENT *p, *chp; + int ch_options, error; + + if ((ftsp = + fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) { + err(1, "fts_open"); + } + + display(NULL, fts_children(ftsp, 0)); + if (f_listdir) { + fts_close(ftsp); + return; + } + + /* + * If not recursing down this tree and don't need stat info, just get + * the names. + */ + ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0; + + while ((p = fts_read(ftsp)) != NULL) + switch (p->fts_info) { + case FTS_DC: + warnx("%s: directory causes a cycle", p->fts_name); + if (COMPAT_MODE("bin/ls", "Unix2003")) { + rval = 1; + } + break; + case FTS_DNR: + case FTS_ERR: + warnx("%s: %s", p->fts_name, strerror(p->fts_errno)); + rval = 1; + break; + case FTS_D: + if (p->fts_level != FTS_ROOTLEVEL && + p->fts_name[0] == '.' && !f_listdot) { + fts_set(ftsp, p, FTS_SKIP); + break; + } + + /* + * If already output something, put out a newline as + * a separator. If multiple arguments, precede each + * directory with its name. + */ + if (output) + (void)fprintf(thread_stdout, "\n%s:\n", p->fts_path); + else if (argc > 1) { + (void)fprintf(thread_stdout, "%s:\n", p->fts_path); + output = 1; + } + chp = fts_children(ftsp, ch_options); + if (COMPAT_MODE("bin/ls", "Unix2003") && ((options & FTS_LOGICAL)!=0)) { + FTSENT *curr; + for (curr = chp; curr; curr = curr->fts_link) { + if (curr->fts_info == FTS_SLNONE) + curr->fts_number = NO_PRINT; + } + } + display(p, chp); + + if (!f_recursive && chp != NULL) + (void)fts_set(ftsp, p, FTS_SKIP); + break; + case FTS_SLNONE: /* Same as default unless Unix conformance */ + if (COMPAT_MODE("bin/ls", "Unix2003")) { + if ((options & FTS_LOGICAL)!=0) { /* -L was specified */ + warnx("%s: %s", p->fts_name, strerror(p->fts_errno ?: ENOENT)); + rval = 1; + } + } + break; + default: + break; + } + error = errno; + fts_close(ftsp); + errno = error; + + if (errno) { + err(1, "fts_read"); + } +} + +/* + * Display() takes a linked list of FTSENT structures and passes the list + * along with any other necessary information to the print function. P + * points to the parent directory of the display list. + */ +static void +display(FTSENT *p, FTSENT *list) +{ + struct stat *sp; + DISPLAY d; + FTSENT *cur; + NAMES *np; + off_t maxsize; + u_int64_t btotal, maxblock; + u_long lattrlen, maxlen, maxnlink, maxlattr; + ino_t maxinode; + int bcfile, maxflags; + gid_t maxgroup; + uid_t maxuser; + size_t flen, ulen, glen; + char *initmax; + int entries, needstats; + const char *user, *group; + char *flags, *lattr = NULL; + char buf[STRBUF_SIZEOF(u_quad_t) + 1]; + char ngroup[STRBUF_SIZEOF(uid_t) + 1]; + char nuser[STRBUF_SIZEOF(gid_t) + 1]; +#ifdef __APPLE__ + acl_entry_t dummy; + ssize_t xattr_size; + char *filename; + char path[MAXPATHLEN+1]; +#endif // __APPLE__ + /* + * If list is NULL there are two possibilities: that the parent + * directory p has no children, or that fts_children() returned an + * error. We ignore the error case since it will be replicated + * on the next call to fts_read() on the post-order visit to the + * directory p, and will be signaled in traverse(). + */ + if (list == NULL) + return; + + needstats = f_inode || f_longform || f_size; + btotal = 0; + initmax = getenv("LS_COLWIDTHS"); + /* Fields match -lios order. New ones should be added at the end. */ + maxlattr = maxblock = maxinode = maxlen = maxnlink = + maxuser = maxgroup = maxflags = maxsize = 0; + if (initmax != NULL && *initmax != '\0') { + char *initmax2, *jinitmax; + int ninitmax; + + /* Fill-in "::" as "0:0:0" for the sake of scanf. */ + jinitmax = initmax2 = malloc(strlen(initmax) * 2 + 2); + if (jinitmax == NULL) { + err(1, "malloc"); + } + if (*initmax == ':') + strcpy(initmax2, "0:"), initmax2 += 2; + else + *initmax2++ = *initmax, *initmax2 = '\0'; + for (initmax++; *initmax != '\0'; initmax++) { + if (initmax[-1] == ':' && initmax[0] == ':') { + *initmax2++ = '0'; + *initmax2++ = initmax[0]; + initmax2[1] = '\0'; + } else { + *initmax2++ = initmax[0]; + initmax2[1] = '\0'; + } + } + if (initmax2[-1] == ':') + strcpy(initmax2, "0"); + + ninitmax = sscanf(jinitmax, +#if _DARWIN_FEATURE_64_BIT_INODE + " %llu : %qu : %lu : %i : %i : %i : %qu : %lu : %lu ", +#else + " %lu : %qu : %lu : %i : %i : %i : %qu : %lu : %lu ", +#endif + &maxinode, &maxblock, &maxnlink, &maxuser, + &maxgroup, &maxflags, &maxsize, &maxlen, &maxlattr); + f_notabs = 1; + switch (ninitmax) { + case 0: + maxinode = 0; + /* FALLTHROUGH */ + case 1: + maxblock = 0; + /* FALLTHROUGH */ + case 2: + maxnlink = 0; + /* FALLTHROUGH */ + case 3: + maxuser = 0; + /* FALLTHROUGH */ + case 4: + maxgroup = 0; + /* FALLTHROUGH */ + case 5: + maxflags = 0; + /* FALLTHROUGH */ + case 6: + maxsize = 0; + /* FALLTHROUGH */ + case 7: + maxlen = 0; + /* FALLTHROUGH */ + case 8: + maxlattr = 0; + /* FALLTHROUGH */ +#ifdef COLORLS + if (!f_color) +#endif + f_notabs = 0; + /* FALLTHROUGH */ + default: + break; + } + maxinode = makenines(maxinode); + maxblock = makenines(maxblock); + maxnlink = makenines(maxnlink); + maxsize = makenines(maxsize); + } + bcfile = 0; + flags = NULL; + for (cur = list, entries = 0; cur; cur = cur->fts_link) { + if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { + warnx("%s: %s", cur->fts_name, strerror(cur->fts_errno)); + cur->fts_number = NO_PRINT; + rval = 1; + continue; + } + /* + * P is NULL if list is the argv list, to which different rules + * apply. + */ + if (p == NULL) { + /* Directories will be displayed later. */ + if (cur->fts_info == FTS_D && !f_listdir) { + cur->fts_number = NO_PRINT; + continue; + } + } else { + /* Only display dot file if -a/-A set. */ + if (cur->fts_name[0] == '.' && !f_listdot) { + cur->fts_number = NO_PRINT; + continue; + } + } + if (cur->fts_namelen > maxlen) + maxlen = cur->fts_namelen; + if (f_octal || f_octal_escape) { + u_long t = len_octal(cur->fts_name, cur->fts_namelen); + + if (t > maxlen) + maxlen = t; + } + if (needstats) { + sp = cur->fts_statp; + if (sp->st_blocks > maxblock) + maxblock = sp->st_blocks; + if (sp->st_ino > maxinode) + maxinode = sp->st_ino; + if (sp->st_nlink > maxnlink) + maxnlink = sp->st_nlink; + if (sp->st_size > maxsize) + maxsize = sp->st_size; + + btotal += sp->st_blocks; + if (f_longform) { + if (f_numericonly) { + (void)snprintf(nuser, sizeof(nuser), + "%u", sp->st_uid); + (void)snprintf(ngroup, sizeof(ngroup), + "%u", sp->st_gid); + user = nuser; + group = ngroup; + } else { + user = user_from_uid(sp->st_uid, 0); + group = group_from_gid(sp->st_gid, 0); + } + if ((ulen = strlen(user)) > maxuser) + maxuser = ulen; + if ((glen = strlen(group)) > maxgroup) + maxgroup = glen; + if (f_flags) { + flags = fflagstostr(sp->st_flags); + if (flags != NULL && *flags == '\0') { + free(flags); + flags = strdup("-"); + } + if (flags == NULL) { + err(1, "fflagstostr"); + } + flen = strlen(flags); + if (flen > (size_t)maxflags) + maxflags = flen; + } else + flen = 0; + lattr = NULL; + lattrlen = 0; + + if ((np = calloc(1, sizeof(NAMES) + lattrlen + + ulen + glen + flen + 4)) == NULL) + { + err(1, "malloc"); + } + + np->user = &np->data[0]; + (void)strcpy(np->user, user); + np->group = &np->data[ulen + 1]; + (void)strcpy(np->group, group); +#ifdef __APPLE__ + if (cur->fts_level == FTS_ROOTLEVEL) { + filename = cur->fts_name; + } else { + snprintf(path, sizeof(path), "%s/%s", cur->fts_parent->fts_accpath, cur->fts_name); + filename = path; + } + xattr_size = listxattr(filename, NULL, 0, XATTR_NOFOLLOW); + if (xattr_size < 0) { + xattr_size = 0; + } + if ((xattr_size > 0) && f_xattr) { + /* collect sizes */ + np->xattr_names = malloc(xattr_size); + listxattr(filename, np->xattr_names, xattr_size, XATTR_NOFOLLOW); + for (char *name = np->xattr_names; name < np->xattr_names + xattr_size; + name += strlen(name)+1) { + np->xattr_sizes = reallocf(np->xattr_sizes, (np->xattr_count+1) * sizeof(np->xattr_sizes[0])); + np->xattr_sizes[np->xattr_count] = getxattr(filename, name, 0, 0, 0, XATTR_NOFOLLOW); + np->xattr_count++; + } + } + /* symlinks can not have ACLs */ + np->acl = acl_get_link_np(filename, ACL_TYPE_EXTENDED); + if (np->acl) { + if (acl_get_entry(np->acl, ACL_FIRST_ENTRY, &dummy) == -1) { + acl_free(np->acl); + np->acl = NULL; + } + } + if (xattr_size > 0) { + np->mode_suffix = '@'; + } else if (np->acl) { + np->mode_suffix = '+'; + } else { + np->mode_suffix = ' '; + } + if (!f_acl) { + acl_free(np->acl); + np->acl = NULL; + } +#endif // __APPLE__ + if (S_ISCHR(sp->st_mode) || + S_ISBLK(sp->st_mode)) + bcfile = 1; + + if (f_flags) { + np->flags = &np->data[ulen + glen + 2]; + (void)strcpy(np->flags, flags); + free(flags); + } + cur->fts_pointer = np; + } + } + ++entries; + } + + if (!entries) + return; + + d.list = list; + d.entries = entries; + d.maxlen = maxlen; + if (needstats) { + d.bcfile = bcfile; + d.btotal = btotal; + (void)snprintf(buf, sizeof(buf), "%qu", (u_int64_t)maxblock); + d.s_block = strlen(buf); + d.s_flags = maxflags; + d.s_lattr = maxlattr; + d.s_group = maxgroup; +#if _DARWIN_FEATURE_64_BIT_INODE + (void)snprintf(buf, sizeof(buf), "%llu", maxinode); +#else + (void)snprintf(buf, sizeof(buf), "%lu", maxinode); +#endif + d.s_inode = strlen(buf); + (void)snprintf(buf, sizeof(buf), "%lu", maxnlink); + d.s_nlink = strlen(buf); + (void)snprintf(buf, sizeof(buf), "%qu", (u_int64_t)maxsize); + d.s_size = strlen(buf); + d.s_user = maxuser; + } + printfcn(&d); + output = 1; + + if (f_longform) { + for (cur = list; cur; cur = cur->fts_link) { + np = cur->fts_pointer; + if (np) { + if (np->acl) { + acl_free(np->acl); + } + free(np->xattr_names); + free(np->xattr_sizes); + free(np); + cur->fts_pointer = NULL; + } + } + } +} + +/* + * Ordering for mastercmp: + * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories + * as larger than directories. Within either group, use the sort function. + * All other levels use the sort function. Error entries remain unsorted. + */ +static int +mastercmp(const FTSENT **a, const FTSENT **b) +{ + int a_info, b_info; + + a_info = (*a)->fts_info; + if (a_info == FTS_ERR) + return (0); + b_info = (*b)->fts_info; + if (b_info == FTS_ERR) + return (0); + + if (a_info == FTS_NS || b_info == FTS_NS) + return (namecmp(*a, *b)); + + if (a_info != b_info && + (*a)->fts_level == FTS_ROOTLEVEL && !f_listdir) { + if (a_info == FTS_D) + return (1); + if (b_info == FTS_D) + return (-1); + } + return (sortfcn(*a, *b)); +} + +/* + * Makenines() returns (10**n)-1. This is useful for converting a width + * into a number that wide in decimal. + */ +static u_quad_t +makenines(u_quad_t n) +{ + u_long i; + u_quad_t reg; + + reg = 1; + /* Use a loop instead of pow(), since all values of n are small. */ + for (i = 0; i < n; i++) + reg *= 10; + reg--; + + return reg; +} diff --git a/files/Sources/files/ls/ls.h b/files/Sources/files/ls/ls.h new file mode 100644 index 00000000..691ab6cc --- /dev/null +++ b/files/Sources/files/ls/ls.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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. + * + * from: @(#)ls.h 8.1 (Berkeley) 5/31/93 + * $FreeBSD: src/bin/ls/ls.h,v 1.18 2002/05/19 02:51:36 tjr Exp $ + */ + +#define NO_PRINT 1 + +extern __thread long blocksize; /* block size units */ + +extern __thread int f_accesstime; /* use time of last access */ +extern __thread int f_birthtime; /* use time of file birth */ +extern __thread int f_flags; /* show flags associated with a file */ +extern __thread int f_humanval; /* show human-readable file sizes */ +extern __thread int f_inode; /* print inode */ +extern __thread int f_longform; /* long listing format */ +extern __thread int f_octal; /* print unprintables in octal */ +extern __thread int f_octal_escape; /* like f_octal but use C escapes if possible */ +extern __thread int f_nonprint; /* show unprintables as ? */ +extern __thread int f_sectime; /* print the real time for all files */ +extern __thread int f_size; /* list size in short listing */ +extern __thread int f_slash; /* append a '/' if the file is a directory */ +extern __thread int f_sortacross; /* sort across rows, not down columns */ +extern __thread int f_statustime; /* use time of last mode change */ +extern __thread int f_notabs; /* don't use tab-separated multi-col output */ +extern __thread int f_type; /* add type character for non-regular files */ +extern __thread int f_acl; /* print ACLs in long format */ +extern __thread int f_xattr; /* print extended attributes in long format */ +extern __thread int f_group; /* list group without owner */ +extern __thread int f_owner; /* list owner without group */ +#ifdef COLORLS +extern __thread int f_color; /* add type in color for non-regular files */ +#endif +extern __thread int f_numericonly; /* don't convert uid/gid to name */ + +#ifdef __APPLE__ +#include +#endif // __APPLE__ + +typedef struct { + FTSENT *list; + u_int64_t btotal; + int bcfile; + int entries; + int maxlen; + u_int s_block; + u_int s_flags; + u_int s_lattr; + u_int s_group; + u_int s_inode; + u_int s_nlink; + u_int s_size; + u_int s_user; +} DISPLAY; + +typedef struct { + char *user; + char *group; + char *flags; +#ifndef __APPLE__ + char *lattr; +#else + char *xattr_names; /* f_xattr */ + int *xattr_sizes; + acl_t acl; /* f_acl */ + int xattr_count; + char mode_suffix; /* @ | + | */ +#endif /* __APPLE__ */ + char data[1]; +} NAMES; diff --git a/files/Sources/files/ls/membershipPriv.h b/files/Sources/files/ls/membershipPriv.h new file mode 100644 index 00000000..348f78cd --- /dev/null +++ b/files/Sources/files/ls/membershipPriv.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef _MEMBERSHIPPRIV_H_ +#define _MEMBERSHIPPRIV_H_ + +#include +#include + +#define MBR_UU_STRING_SIZE 37 +#define MBR_MAX_SID_STRING_SIZE 200 + +#define SID_TYPE_USER 0 +#define SID_TYPE_GROUP 1 + +#define MBR_REC_TYPE_USER 1 +#define MBR_REC_TYPE_GROUP 2 + +/* only supported by mbr_identifier_translate for target type */ +#define ID_TYPE_UID_OR_GID 30 +#define ID_TYPE_NAME 31 +#define ID_TYPE_WINDOWS_FQN 32 + +__BEGIN_DECLS + +int mbr_reset_cache(); +int mbr_user_name_to_uuid(const char *name, uuid_t uu); +int mbr_group_name_to_uuid(const char *name, uuid_t uu); +int mbr_check_membership_by_id(uuid_t user, gid_t group, int *ismember); +int mbr_check_membership_refresh(const uuid_t user, uuid_t group, int *ismember); + +/* mbr_uuid_to_string should use uuid_unparse from uuid.h */ +int mbr_uuid_to_string(const uuid_t uu, char *string) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_8, __IPHONE_NA, __IPHONE_NA); + +/* mbr_string_to_uuid should use uuid_parse from uuid.h */ +int mbr_string_to_uuid(const char *string, uuid_t uu) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4, __MAC_10_8, __IPHONE_NA, __IPHONE_NA); + +int mbr_uuid_to_sid_type(const uuid_t uu, nt_sid_t *sid, int *id_type); +int mbr_set_identifier_ttl(int id_type, const void *identifier, size_t identifier_size, unsigned int seconds); + +/* new SPI to allow translation from any-to-any type, pass ID_TYPE_UID_OR_GID when translating to a UID */ +int mbr_identifier_translate(int id_type, const void *identifier, size_t identifier_size, int target_type, void **result, int *rec_type); + +/* + * groupid_type does not support ID_TYPE_GSS_EXPORT_NAME + */ +int mbr_check_membership_ext(int userid_type, const void *userid, size_t userid_size, int groupid_type, const void *groupid, int refresh, int *isMember); + +__END_DECLS + +#endif /* !_MEMBERSHIPPRIV_H_ */ diff --git a/files/Sources/files/ls/print.c b/files/Sources/files/ls/print.c new file mode 100644 index 00000000..37f3462b --- /dev/null +++ b/files/Sources/files/ls/print.c @@ -0,0 +1,1029 @@ +/* + * 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 + * Michael Fischbein. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)print.c 8.4 (Berkeley) 4/17/94"; +#endif /* not lint */ +#endif +#include +__RCSID("$FreeBSD: src/bin/ls/print.c,v 1.57 2002/08/29 14:29:09 keramida Exp $"); + +#include +#include +#ifdef __APPLE__ +#include +#include +#include +#include +#include +#include +#include +#include "membershipPriv.h" +#include +#endif + +#include +#include +#include +#include +#include +#include "libutil.h" +#include +#include +#include +#include +#include +#ifdef COLORLS +#include +#include "termcap.h" +#include +#endif +#include /* intmax_t */ +#include +#ifdef __APPLE__ +// #include +// #else +#define COMPAT_MODE(a,b) (1) +#endif /* __APPLE__ */ + +#include "ls.h" +#include "extern.h" +#include +#include "ios_error.h" + + +static int printaname(FTSENT *, u_long, u_long); +static void printlink(FTSENT *); +static void printtime(time_t); +static int printtype(u_int); +static void printsize(size_t, off_t); +#ifdef COLORLS +static void endcolor(int); +static int colortype(mode_t); +#endif + +#define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT) + +#ifdef COLORLS +/* Most of these are taken from */ +typedef enum Colors { + C_DIR, /* directory */ + C_LNK, /* symbolic link */ + C_SOCK, /* socket */ + C_FIFO, /* pipe */ + C_EXEC, /* executable */ + C_BLK, /* block special */ + C_CHR, /* character special */ + C_SUID, /* setuid executable */ + C_SGID, /* setgid executable */ + C_WSDIR, /* directory writeble to others, with sticky + * bit */ + C_WDIR, /* directory writeble to others, without + * sticky bit */ + C_NUMCOLORS /* just a place-holder */ +} Colors; + +static const char *defcolors = "exfxcxdxbxegedabagacad"; + +/* colors for file types */ +static struct { + int num[2]; + int bold; +} colors[C_NUMCOLORS]; +#endif + +void +printscol(DISPLAY *dp) +{ + FTSENT *p; + + assert(dp); + if (COMPAT_MODE("bin/ls", "Unix2003") && (dp->list != NULL)) { + if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) + (void)fprintf(thread_stdout, "total %qu\n", (u_int64_t)howmany(dp->btotal, blocksize)); + } + + for (p = dp->list; p; p = p->fts_link) { + if (IS_NOPRINT(p)) + continue; + (void)printaname(p, dp->s_inode, dp->s_block); + (void)putchar('\n'); + } +} + +/* + * print name in current style + */ +static int +printname(const char *name) +{ + if (f_octal || f_octal_escape) + return prn_octal(name); + else if (f_nonprint) + return prn_printable(name); + else + return prn_normal(name); +} + +/* + * print access control list + */ +static struct { + acl_perm_t perm; + char *name; + int flags; +#define ACL_PERM_DIR (1<<0) +#define ACL_PERM_FILE (1<<1) +} acl_perms[] = { + {ACL_READ_DATA, "read", ACL_PERM_FILE}, + {ACL_LIST_DIRECTORY, "list", ACL_PERM_DIR}, + {ACL_WRITE_DATA, "write", ACL_PERM_FILE}, + {ACL_ADD_FILE, "add_file", ACL_PERM_DIR}, + {ACL_EXECUTE, "execute", ACL_PERM_FILE}, + {ACL_SEARCH, "search", ACL_PERM_DIR}, + {ACL_DELETE, "delete", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_APPEND_DATA, "append", ACL_PERM_FILE}, + {ACL_ADD_SUBDIRECTORY, "add_subdirectory", ACL_PERM_DIR}, + {ACL_DELETE_CHILD, "delete_child", ACL_PERM_DIR}, + {ACL_READ_ATTRIBUTES, "readattr", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_WRITE_ATTRIBUTES, "writeattr", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_READ_EXTATTRIBUTES, "readextattr", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_WRITE_EXTATTRIBUTES, "writeextattr", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_READ_SECURITY, "readsecurity", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_WRITE_SECURITY, "writesecurity", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_CHANGE_OWNER, "chown", ACL_PERM_FILE | ACL_PERM_DIR}, + {0, NULL, 0} +}; + +static struct { + acl_flag_t flag; + char *name; + int flags; +} acl_flags[] = { + {ACL_ENTRY_FILE_INHERIT, "file_inherit", ACL_PERM_DIR}, + {ACL_ENTRY_DIRECTORY_INHERIT, "directory_inherit", ACL_PERM_DIR}, + {ACL_ENTRY_LIMIT_INHERIT, "limit_inherit", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_ENTRY_ONLY_INHERIT, "only_inherit", ACL_PERM_DIR}, + {0, NULL, 0} +}; + +// mbr_identifier_translate source code +#define MBR_REC_TYPE_USER 1 +#define MBR_REC_TYPE_GROUP 2 + +/* only supported by mbr_identifier_translate for target type */ +#define ID_TYPE_UID_OR_GID 30 +#define ID_TYPE_NAME 31 +#define ID_TYPE_WINDOWS_FQN 32 + +static const uuid_t _user_compat_prefix = {0xff, 0xff, 0xee, 0xee, 0xdd, 0xdd, 0xcc, 0xcc, 0xbb, 0xbb, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00}; +static const uuid_t _group_compat_prefix = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x00}; + +#define COMPAT_PREFIX_LEN (sizeof(uuid_t) - sizeof(id_t)) + +static int +_ios_mbr_od_available(void) +{ +#if DS_AVAILABLE + xpc_pipe_t pipe = _mbr_xpc_pipe(false); + if (pipe != NULL) { + xpc_release(pipe); + return 1; + } +#endif + return 0; +} + +static int +ios_mbr_identifier_translate(int id_type, const void *identifier, size_t identifier_size, int target_type, void **result, int *rec_type) +{ +#if DS_AVAILABLE + xpc_object_t payload, reply; +#endif + id_t tempID; + size_t identifier_len; + int rc = EIO; + + if (identifier == NULL || result == NULL || identifier_size == 0) return EIO; + + if (identifier_size == -1) { + identifier_size = strlen(identifier); + } else { + /* 10898647: For types that are known to be strings, send the smallest necessary amount of data. */ + switch (id_type) { + case ID_TYPE_USERNAME: + case ID_TYPE_GROUPNAME: + case ID_TYPE_GROUP_NFS: + case ID_TYPE_USER_NFS: + case ID_TYPE_X509_DN: + case ID_TYPE_KERBEROS: + case ID_TYPE_NAME: + identifier_len = strlen(identifier); + if (identifier_size > identifier_len) { + identifier_size = identifier_len; + } + break; + } + } + + switch (target_type) { + case ID_TYPE_GID: + case ID_TYPE_UID: + case ID_TYPE_UID_OR_GID: + /* shortcut UUIDs using compatibilty prefixes */ + if (id_type == ID_TYPE_UUID) { + const uint8_t *uu = identifier; + + if (identifier_size != sizeof(uuid_t)) return EINVAL; + + if (memcmp(uu, _user_compat_prefix, COMPAT_PREFIX_LEN) == 0) { + id_t *tempRes = malloc(sizeof(*tempRes)); + memcpy(&tempID, &uu[COMPAT_PREFIX_LEN], sizeof(tempID)); + (*tempRes) = ntohl(tempID); + (*result) = tempRes; + if (rec_type != NULL) { + (*rec_type) = MBR_REC_TYPE_USER; + } + return 0; + } else if (memcmp(uu, _group_compat_prefix, COMPAT_PREFIX_LEN) == 0) { + id_t *tempRes = malloc(sizeof(*tempRes)); + memcpy(&tempID, &uu[COMPAT_PREFIX_LEN], sizeof(tempID)); + (*tempRes) = ntohl(tempID); + (*result) = tempRes; + if (rec_type != NULL) { + (*rec_type) = MBR_REC_TYPE_GROUP; + } + return 0; + } + } + break; + + case ID_TYPE_UUID: + /* if this is a UID or GID translation, we shortcut UID/GID 0 */ + /* or if no OD, we return compatibility UUIDs */ + switch (id_type) { + case ID_TYPE_UID: + if (identifier_size != sizeof(tempID)) return EINVAL; + + tempID = *((id_t *) identifier); + if ((tempID == 0) || (_ios_mbr_od_available() == 0)) { + uint8_t *tempUU = malloc(sizeof(uuid_t)); + uuid_copy(tempUU, _user_compat_prefix); + *((id_t *) &tempUU[COMPAT_PREFIX_LEN]) = htonl(tempID); + (*result) = tempUU; + if (rec_type != NULL) { + (*rec_type) = MBR_REC_TYPE_USER; + } + return 0; + } + break; + + case ID_TYPE_GID: + if (identifier_size != sizeof(tempID)) return EINVAL; + + tempID = *((id_t *) identifier); + if ((tempID == 0) || (_ios_mbr_od_available() == 0)) { + uint8_t *tempUU = malloc(sizeof(uuid_t)); + uuid_copy(tempUU, _group_compat_prefix); + *((id_t *) &tempUU[COMPAT_PREFIX_LEN]) = htonl(tempID); + (*result) = tempUU; + if (rec_type != NULL) { + (*rec_type) = MBR_REC_TYPE_GROUP; + } + return 0; + } + break; + } + break; + } + +#if DS_AVAILABLE + payload = xpc_dictionary_create(NULL, NULL, 0); + if (payload == NULL) return EIO; + + xpc_dictionary_set_int64(payload, "requesting", target_type); + xpc_dictionary_set_int64(payload, "type", id_type); + xpc_dictionary_set_data(payload, "identifier", identifier, identifier_size); + + reply = _od_rpc_call("mbr_identifier_translate", payload, _mbr_xpc_pipe); + if (reply != NULL) { + const void *reply_id; + size_t idLen; + + rc = (int) xpc_dictionary_get_int64(reply, "error"); + if (rc == 0) { + reply_id = xpc_dictionary_get_data(reply, "identifier", &idLen); + if (reply_id != NULL) { + char *identifier = malloc(idLen); + + memcpy(identifier, reply_id, idLen); // should already be NULL terminated, etc. + (*result) = identifier; + + if (rec_type != NULL) { + (*rec_type) = (int) xpc_dictionary_get_int64(reply, "rectype"); + } + } else { + (*result) = NULL; + rc = ENOENT; + } + } + + xpc_release(reply); + } + + xpc_release(payload); +#endif + + return rc; +} + + +static char * +uuid_to_name(uuid_t *uu) +{ + int type; + char *name = NULL; + char *recname = NULL; + +#define MAXNAMETAG (MAXLOGNAME + 6) /* + strlen("group:") */ + name = (char *) malloc(MAXNAMETAG); + + if (NULL == name) { + err(1, "malloc"); + } + + if (f_numericonly) { + goto errout; + } + + if (ios_mbr_identifier_translate(ID_TYPE_UUID, *uu, sizeof(*uu), ID_TYPE_NAME, (void **) &recname, &type)) { + goto errout; + } + + snprintf(name, MAXNAMETAG, "%s:%s", (type == MBR_REC_TYPE_USER ? "user" : "group"), recname); + free(recname); + + return name; +errout: + uuid_unparse_upper(*uu, name); + + return name; +} + +static void +printxattr(DISPLAY *dp, int count, char *buf, int sizes[]) +{ + for (int i = 0; i < count; i++) { + putchar('\t'); + printname(buf); + putchar('\t'); + printsize(dp->s_size, sizes[i]); + putchar('\n'); + buf += strlen(buf) + 1; + } +} + +static void +printacl(acl_t acl, int isdir) +{ + acl_entry_t entry = NULL; + int index; + uuid_t *applicable; + char *name = NULL; + acl_tag_t tag; + acl_flagset_t flags; + acl_permset_t perms; + char *type; + int i, first; + + + for (index = 0; + acl_get_entry(acl, entry == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, &entry) == 0; + index++) { + if (acl_get_tag_type(entry, &tag) != 0) + continue; + if (acl_get_flagset_np(entry, &flags) != 0) + continue; + if (acl_get_permset(entry, &perms) != 0) + continue; + if ((applicable = (uuid_t *) acl_get_qualifier(entry)) == NULL) + continue; + name = uuid_to_name(applicable); + acl_free(applicable); + switch(tag) { + case ACL_EXTENDED_ALLOW: + type = "allow"; + break; + case ACL_EXTENDED_DENY: + type = "deny"; + break; + default: + type = "unknown"; + } + + (void)fprintf(thread_stdout, " %d: %s%s %s ", + index, + name, + acl_get_flag_np(flags, ACL_ENTRY_INHERITED) ? " inherited" : "", + type); + + if (name) + free(name); + + for (i = 0, first = 0; acl_perms[i].name != NULL; i++) { + if (acl_get_perm_np(perms, acl_perms[i].perm) == 0) + continue; + if (!(acl_perms[i].flags & (isdir ? ACL_PERM_DIR : ACL_PERM_FILE))) + continue; + (void)fprintf(thread_stdout, "%s%s", first++ ? "," : "", acl_perms[i].name); + } + for (i = 0; acl_flags[i].name != NULL; i++) { + if (acl_get_flag_np(flags, acl_flags[i].flag) == 0) + continue; + if (!(acl_flags[i].flags & (isdir ? ACL_PERM_DIR : ACL_PERM_FILE))) + continue; + (void)fprintf(thread_stdout, "%s%s", first++ ? "," : "", acl_flags[i].name); + } + + (void)putchar('\n'); + } + +} + +void +printlong(DISPLAY *dp) +{ + struct stat *sp; + FTSENT *p; + NAMES *np; + char buf[20]; +#ifdef COLORLS + int color_printed = 0; +#endif + + if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) + (void)fprintf(thread_stdout, "total %qu\n", (u_int64_t)howmany(dp->btotal, blocksize)); + + for (p = dp->list; p; p = p->fts_link) { + if (IS_NOPRINT(p)) + continue; + sp = p->fts_statp; + if (f_inode) +#if _DARWIN_FEATURE_64_BIT_INODE + (void)fprintf(thread_stdout, "%*llu ", dp->s_inode, (u_quad_t)sp->st_ino); +#else + (void)fprintf(thread_stdout, "%*lu ", dp->s_inode, (u_long)sp->st_ino); +#endif + if (f_size) + (void)fprintf(thread_stdout, "%*qu ", + dp->s_block, (u_int64_t)howmany(sp->st_blocks, blocksize)); + strmode(sp->st_mode, buf); + np = p->fts_pointer; +#ifdef __APPLE__ + buf[10] = '\0'; /* make +/@ abut the mode */ + char str[2] = { np->mode_suffix, '\0' }; +#endif /* __APPLE__ */ + if (f_group && f_owner) { /* means print neither */ +#ifdef __APPLE__ + (void)fprintf(thread_stdout, "%s%s %*u ", buf, str, dp->s_nlink, + sp->st_nlink); +#else /* ! __APPLE__ */ + (void)fprintf(thread_stdout, "%s %*u ", buf, dp->s_nlink, + sp->st_nlink); +#endif /* __APPLE__ */ + } + else if (f_group) { +#ifdef __APPLE__ + (void)fprintf(thread_stdout, "%s%s %*u %-*s ", buf, str, dp->s_nlink, + sp->st_nlink, dp->s_group, np->group); +#else /* ! __APPLE__ */ + (void)fprintf(thread_stdout, "%s %*u %-*s ", buf, dp->s_nlink, + sp->st_nlink, dp->s_group, np->group); +#endif /* __APPLE__ */ + } + else if (f_owner) { +#ifdef __APPLE__ + (void)fprintf(thread_stdout, "%s%s %*u %-*s ", buf, str, dp->s_nlink, + sp->st_nlink, dp->s_user, np->user); +#else /* ! __APPLE__ */ + (void)fprintf(thread_stdout, "%s %*u %-*s ", buf, dp->s_nlink, + sp->st_nlink, dp->s_user, np->user); +#endif /* __APPLE__ */ + } + else { +#ifdef __APPLE__ + (void)fprintf(thread_stdout, "%s%s %*u %-*s %-*s ", buf, str, dp->s_nlink, + sp->st_nlink, dp->s_user, np->user, dp->s_group, + np->group); +#else /* ! __APPLE__ */ + (void)fprintf(thread_stdout, "%s %*u %-*s %-*s ", buf, dp->s_nlink, + sp->st_nlink, dp->s_user, np->user, dp->s_group, + np->group); +#endif /* ! __APPLE__ */ + } + if (f_flags) + (void)fprintf(thread_stdout, "%-*s ", dp->s_flags, np->flags); + if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) + if (minor(sp->st_rdev) > 255 || minor(sp->st_rdev) < 0) + (void)fprintf(thread_stdout, "%3d, 0x%08x ", + major(sp->st_rdev), + (u_int)minor(sp->st_rdev)); + else + (void)fprintf(thread_stdout, "%3d, %3d ", + major(sp->st_rdev), minor(sp->st_rdev)); + else if (dp->bcfile) + (void)fprintf(thread_stdout, "%*s%*qu ", + 8 - dp->s_size, "", dp->s_size, (u_int64_t)sp->st_size); + else + printsize(dp->s_size, sp->st_size); + if (f_accesstime) + printtime(sp->st_atime); + else if (f_statustime) + printtime(sp->st_ctime); + else if (f_birthtime) + printtime(sp->st_birthtime); + else + printtime(sp->st_mtime); +#ifdef COLORLS + if (f_color) + color_printed = colortype(sp->st_mode); +#endif + (void)printname(p->fts_name); +#ifdef COLORLS + if (f_color && color_printed) + endcolor(0); +#endif + if (f_type) + (void)printtype(sp->st_mode); + if (S_ISLNK(sp->st_mode)) + printlink(p); + (void)putchar('\n'); +#ifdef __APPLE__ + if (np->xattr_count && f_xattr) { + printxattr(dp, np->xattr_count, np->xattr_names, np->xattr_sizes); + } + if (np->acl != NULL && f_acl) { + printacl(np->acl, S_ISDIR(sp->st_mode)); + } +#endif /* __APPLE__ */ + } +} + +void +printstream(DISPLAY *dp) +{ + FTSENT *p; + extern __thread int termwidth; + int chcnt; + + for (p = dp->list, chcnt = 0; p; p = p->fts_link) { + if (p->fts_number == NO_PRINT) + continue; + if (strlen(p->fts_name) + chcnt + + (p->fts_link ? 2 : 0) >= (unsigned)termwidth) { + putchar('\n'); + chcnt = 0; + } + chcnt += printaname(p, dp->s_inode, dp->s_block); + if (p->fts_link) { + fprintf(thread_stdout, ", "); + chcnt += 2; + } + } + if (chcnt) + putchar('\n'); +} + +void +printcol(DISPLAY *dp) +{ + extern __thread int termwidth; + static FTSENT **array; + static int lastentries = -1; + FTSENT *p; + int base; + int chcnt; + int cnt; + int col; + int colwidth; + int endcol; + int num; + int numcols; + int numrows; + int row; + int tabwidth; + + if (f_notabs) + tabwidth = 1; + else + tabwidth = 8; + + /* + * Have to do random access in the linked list -- build a table + * of pointers. + */ + if ((lastentries == -1) || (dp->entries > lastentries)) { + lastentries = dp->entries; + if ((array = realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) { + warn(NULL); + printscol(dp); + return; + } + } + memset(array, 0, dp->entries * sizeof(FTSENT *)); + for (p = dp->list, num = 0; p; p = p->fts_link) + if (p->fts_number != NO_PRINT) + array[num++] = p; + + colwidth = dp->maxlen; + if (f_inode) + colwidth += dp->s_inode + 1; + if (f_size) + colwidth += dp->s_block + 1; + if (f_type) + colwidth += 1; + + colwidth = (colwidth + tabwidth) & ~(tabwidth - 1); + if (termwidth < 2 * colwidth) { + printscol(dp); + return; + } + numcols = termwidth / colwidth; + numrows = num / numcols; + if (num % numcols) + ++numrows; + + assert(dp->list); + if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) + (void)fprintf(thread_stdout, "total %qu\n", (u_int64_t)howmany(dp->btotal, blocksize)); + + base = 0; + for (row = 0; row < numrows; ++row) { + endcol = colwidth; + if (!f_sortacross) + base = row; + for (col = 0, chcnt = 0; col < numcols; ++col) { + assert(base < dp->entries); + chcnt += printaname(array[base], dp->s_inode, dp->s_block); + if (f_sortacross) + base++; + else + base += numrows; + if (base >= num) + break; + while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1))) + <= endcol) { + if (f_sortacross && col + 1 >= numcols) + break; + (void)putchar(f_notabs ? ' ' : '\t'); + chcnt = cnt; + } + endcol += colwidth; + } + (void)putchar('\n'); + } +} + +/* + * print [inode] [size] name + * return # of characters printed, no trailing characters. + */ +static int +printaname(FTSENT *p, u_long inodefield, u_long sizefield) +{ + struct stat *sp; + int chcnt; +#ifdef COLORLS + int color_printed = 0; +#endif + + sp = p->fts_statp; + chcnt = 0; + if (f_inode) +#if _DARWIN_FEATURE_64_BIT_INODE + chcnt += fprintf(thread_stdout, "%*llu ", (int)inodefield, (u_quad_t)sp->st_ino); +#else + chcnt += fprintf(thread_stdout, "%*lu ", (int)inodefield, (u_long)sp->st_ino); +#endif + if (f_size) + chcnt += fprintf(thread_stdout, "%*qu ", + (int)sizefield, (u_int64_t)howmany(sp->st_blocks, blocksize)); +#ifdef COLORLS + if (f_color) + color_printed = colortype(sp->st_mode); +#endif + chcnt += printname(p->fts_name); +#ifdef COLORLS + if (f_color && color_printed) + endcolor(0); +#endif + if (f_type) + chcnt += printtype(sp->st_mode); + return (chcnt); +} + +static void +printtime(time_t ftime) +{ + char longstring[80]; + static time_t now; + const char *format; + static int d_first = -1; + + if (d_first < 0) + d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); + if (now == 0) + now = time(NULL); + +#define SIXMONTHS ((365 / 2) * 86400) + if (f_sectime) + /* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */ + format = d_first ? "%e %b %T %Y " : "%b %e %T %Y "; + else if (COMPAT_MODE("bin/ls", "Unix2003")) { + if (ftime + SIXMONTHS > now && ftime <= now) + /* mmm dd hh:mm || dd mmm hh:mm */ + format = d_first ? "%e %b %R " : "%b %e %R "; + else + /* mmm dd yyyy || dd mmm yyyy */ + format = d_first ? "%e %b %Y " : "%b %e %Y "; + } + else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS) + /* mmm dd hh:mm || dd mmm hh:mm */ + format = d_first ? "%e %b %R " : "%b %e %R "; + else + /* mmm dd yyyy || dd mmm yyyy */ + format = d_first ? "%e %b %Y " : "%b %e %Y "; + strftime(longstring, sizeof(longstring), format, localtime(&ftime)); + fputs(longstring, thread_stdout); +} + +static int +printtype(u_int mode) +{ + + if (f_slash) { + if ((mode & S_IFMT) == S_IFDIR) { + (void)putchar('/'); + return (1); + } + return (0); + } + + switch (mode & S_IFMT) { + case S_IFDIR: + (void)putchar('/'); + return (1); + case S_IFIFO: + (void)putchar('|'); + return (1); + case S_IFLNK: + (void)putchar('@'); + return (1); + case S_IFSOCK: + (void)putchar('='); + return (1); + case S_IFWHT: + (void)putchar('%'); + return (1); + default: + break; + } + if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + (void)putchar('*'); + return (1); + } + return (0); +} + +#ifdef COLORLS +static int +putch(int c) +{ + (void)putchar(c); + return 0; +} + +static int +writech(int c) +{ + char tmp = c; + + //(void)write(fileno(thread_stdout), &tmp, 1); + fwrite(&tmp, 1, 1, thread_stdout); + return 0; +} + +static void +printcolor(Colors c) +{ + char ansiseq[10]; + + if (colors[c].bold) + fprintf(thread_stdout, "%s", enter_bold); + // tputs(enter_bold, 1, putch); + + if (colors[c].num[0] != -1) { +#ifndef TARGET_OS_IPHONE + ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]); +#else + sprintf(ansiseq, "%s%dm", ansi_fgcol,colors[c].num[0]); +#endif + if (ansiseq) + fprintf(thread_stdout, "%s", ansiseq); + // tputs(ansiseq, 1, putch); + } + if (colors[c].num[1] != -1) { +#ifndef TARGET_OS_IPHONE + ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]); +#else + sprintf(ansiseq, "%s%dm", ansi_bgcol,colors[c].num[1]); +#endif + if (ansiseq) + fprintf(thread_stdout, "%s", ansiseq); + // tputs(ansiseq, 1, putch); + } +} + +static void +endcolor(int sig) +{ + fprintf(thread_stdout, "%s", ansi_coloff); + fprintf(thread_stdout, "%s", attrs_off); + // tputs(ansi_coloff, 1, sig ? writech : putch); + // tputs(attrs_off, 1, sig ? writech : putch); +} + +static int +colortype(mode_t mode) +{ + switch (mode & S_IFMT) { + case S_IFDIR: + if (mode & S_IWOTH) + if (mode & S_ISTXT) + printcolor(C_WSDIR); + else + printcolor(C_WDIR); + else + printcolor(C_DIR); + return (1); + case S_IFLNK: + printcolor(C_LNK); + return (1); + case S_IFSOCK: + printcolor(C_SOCK); + return (1); + case S_IFIFO: + printcolor(C_FIFO); + return (1); + case S_IFBLK: + printcolor(C_BLK); + return (1); + case S_IFCHR: + printcolor(C_CHR); + return (1); + } + if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + if (mode & S_ISUID) + printcolor(C_SUID); + else if (mode & S_ISGID) + printcolor(C_SGID); + else + printcolor(C_EXEC); + return (1); + } + return (0); +} + +void +parsecolors(const char *cs) +{ + int i; + int j; + int len; + char c[2]; + short legacy_warn = 0; + + if (cs == NULL) + cs = ""; /* LSCOLORS not set */ + len = strlen(cs); + for (i = 0; i < C_NUMCOLORS; i++) { + colors[i].bold = 0; + + if (len <= 2 * i) { + c[0] = defcolors[2 * i]; + c[1] = defcolors[2 * i + 1]; + } else { + c[0] = cs[2 * i]; + c[1] = cs[2 * i + 1]; + } + for (j = 0; j < 2; j++) { + /* Legacy colours used 0-7 */ + if (c[j] >= '0' && c[j] <= '7') { + colors[i].num[j] = c[j] - '0'; + if (!legacy_warn) { + fprintf(thread_stderr, + "warn: LSCOLORS should use " + "characters a-h instead of 0-9 (" + "see the manual page)\n"); + } + legacy_warn = 1; + } else if (c[j] >= 'a' && c[j] <= 'h') + colors[i].num[j] = c[j] - 'a'; + else if (c[j] >= 'A' && c[j] <= 'H') { + colors[i].num[j] = c[j] - 'A'; + colors[i].bold = 1; + } else if (tolower((unsigned char)c[j] == 'x')) + colors[i].num[j] = -1; + else { + fprintf(thread_stderr, + "error: invalid character '%c' in LSCOLORS" + " env var\n", c[j]); + colors[i].num[j] = -1; + } + } + } +} + +void +colorquit(int sig) +{ + endcolor(sig); + + // (void)signal(sig, SIG_DFL); + // back to default behaviour on both signals: + (void)signal(SIGINT, SIG_DFL); + (void)signal(SIGQUIT, SIG_DFL); + ios_exit(0); + // (void)kill(getpid(), sig); +} + +#endif /* COLORLS */ + +static void +printlink(FTSENT *p) +{ + int lnklen; + char name[MAXPATHLEN + 1]; + char path[MAXPATHLEN + 1]; + + if (p->fts_level == FTS_ROOTLEVEL) + (void)snprintf(name, sizeof(name), "%s", p->fts_name); + else + (void)snprintf(name, sizeof(name), + "%s/%s", p->fts_parent->fts_accpath, p->fts_name); + if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) { + (void)fprintf(thread_stderr, "\nls: %s: %s\n", name, strerror(errno)); + return; + } + path[lnklen] = '\0'; + (void)fprintf(thread_stdout, " -> "); + (void)printname(path); +} + +static void +printsize(size_t width, off_t bytes) +{ + + if (f_humanval) { + char buf[5]; + + humanize_number(buf, sizeof(buf), (int64_t)bytes, "", + HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); + (void)fprintf(thread_stdout, "%5s ", buf); + } else + (void)fprintf(thread_stdout, "%*jd ", (u_int)width, (intmax_t)bytes); +} diff --git a/files/Sources/files/ls/util.c b/files/Sources/files/ls/util.c new file mode 100644 index 00000000..4e80de39 --- /dev/null +++ b/files/Sources/files/ls/util.c @@ -0,0 +1,245 @@ +/*- + * 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 + * Michael Fischbein. + * + * 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. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)util.c 8.3 (Berkeley) 4/2/94"; +#endif /* not lint */ +#endif +#include +__FBSDID("$FreeBSD: src/bin/ls/util.c,v 1.38 2005/06/03 11:05:58 dd Exp $"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ls.h" +#include "extern.h" +#include +#include "ios_error.h" + +int +prn_normal(const char *s) +{ +#ifdef TARGET_OS_IPHONE + // This is a bit extreme, but we need to keep the chars together for + // the rest of the pipeline + fprintf(thread_stdout, "%s", s); + return strlen(s); +#else + mbstate_t mbs; + wchar_t wc; + int i, n; + size_t clen; + + memset(&mbs, 0, sizeof(mbs)); + n = 0; + while ((clen = mbrtowc(&wc, s, MB_LEN_MAX, &mbs)) != 0) { + if (clen == (size_t)-2) { + n += fprintf(thread_stdout, "%s", s); + break; + } + if (clen == (size_t)-1) { + memset(&mbs, 0, sizeof(mbs)); + putchar((unsigned char)*s); + s++; + n++; + continue; + } + for (i = 0; i < (int)clen; i++) + putchar((unsigned char)s[i]); + s += clen; + if (iswprint(wc)) + n += wcwidth(wc); + } + return (n); +#endif +} + +int +prn_printable(const char *s) +{ + mbstate_t mbs; + wchar_t wc; + int i, n; + size_t clen; + +#ifdef TARGET_OS_IPHONE + fprintf(thread_stdout, "%s", s); + return strlen(s); +#else + memset(&mbs, 0, sizeof(mbs)); + n = 0; + while ((clen = mbrtowc(&wc, s, MB_LEN_MAX, &mbs)) != 0) { + if (clen == (size_t)-1) { + putchar('?'); + s++; + n++; + memset(&mbs, 0, sizeof(mbs)); + continue; + } + if (clen == (size_t)-2) { + putchar('?'); + n++; + break; + } + if (!iswprint(wc)) { + putchar('?'); + s += clen; + n++; + continue; + } + for (i = 0; i < (int)clen; i++) + putchar((unsigned char)s[i]); + s += clen; + n += wcwidth(wc); + } + return (n); +#endif +} + +/* + * The fts system makes it difficult to replace fts_name with a different- + * sized string, so we just calculate the real length here and do the + * conversion in prn_octal() + * + * XXX when using f_octal_escape (-b) rather than f_octal (-B), the + * length computed by len_octal may be too big. I just can't be buggered + * to fix this as an efficient fix would involve a lookup table. Same goes + * for the rather inelegant code in prn_octal. + * + * DES 1998/04/23 + */ + +size_t +len_octal(const char *s, int len) +{ + mbstate_t mbs; + wchar_t wc; + size_t clen, r; + + memset(&mbs, 0, sizeof(mbs)); + r = 0; + while (len != 0 && (clen = mbrtowc(&wc, s, len, &mbs)) != 0) { + if (clen == (size_t)-1) { + r += 4; + s++; + len--; + memset(&mbs, 0, sizeof(mbs)); + continue; + } + if (clen == (size_t)-2) { + r += 4 * len; + break; + } + if (iswprint(wc)) + r++; + else + r += 4 * clen; + s += clen; + } + return (r); +} + +int +prn_octal(const char *s) +{ + static const char esc[] = "\\\\\"\"\aa\bb\ff\nn\rr\tt\vv"; + const char *p; + mbstate_t mbs; + wchar_t wc; + size_t clen; + unsigned char ch; + int goodchar, i, len, prtlen; + + memset(&mbs, 0, sizeof(mbs)); + len = 0; + while ((clen = mbrtowc(&wc, s, MB_LEN_MAX, &mbs)) != 0) { + goodchar = clen != (size_t)-1 && clen != (size_t)-2; + if (goodchar && iswprint(wc) && wc != L'\"' && wc != L'\\') { + for (i = 0; i < (int)clen; i++) + putchar((unsigned char)s[i]); + len += wcwidth(wc); + } else if (goodchar && f_octal_escape && wc >= 0 && + wc <= (wchar_t)UCHAR_MAX && + (p = strchr(esc, (char)wc)) != NULL) { + putchar('\\'); + putchar(p[1]); + len += 2; + } else { + if (goodchar) + prtlen = clen; + else if (clen == (size_t)-1) + prtlen = 1; + else + prtlen = strlen(s); + for (i = 0; i < prtlen; i++) { + ch = (unsigned char)s[i]; + putchar('\\'); + putchar('0' + (ch >> 6)); + putchar('0' + ((ch >> 3) & 7)); + putchar('0' + (ch & 7)); + len += 4; + } + } + if (clen == (size_t)-2) + break; + if (clen == (size_t)-1) { + memset(&mbs, 0, sizeof(mbs)); + s++; + } else + s += clen; + } + return (len); +} + +void +ls_usage(void) +{ + (void)fprintf(thread_stderr, +#ifdef COLORLS + "usage: ls [-ABCFGHLOPRSTUWabcdefghiklmnopqrstuwx1]" +#else + "usage: ls [-ABCFHLOPRSTUWabcdefghiklmnopqrstuwx1]" +#endif + " [file ...]\n"); + exit(1); +} diff --git a/files/Sources/files/mkdir/mkdir.1 b/files/Sources/files/mkdir/mkdir.1 new file mode 100644 index 00000000..2903a002 --- /dev/null +++ b/files/Sources/files/mkdir/mkdir.1 @@ -0,0 +1,108 @@ +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 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. +.\" +.\" @(#)mkdir.1 8.2 (Berkeley) 1/25/94 +.\" $FreeBSD: src/bin/mkdir/mkdir.1,v 1.17 2002/06/30 06:50:16 tjr Exp $ +.\" +.Dd January 25, 1994 +.Dt MKDIR 1 +.Os +.Sh NAME +.Nm mkdir +.Nd make directories +.Sh SYNOPSIS +.Nm +.Op Fl pv +.Op Fl m Ar mode +.Ar directory_name ... +.Sh DESCRIPTION +The +.Nm +utility creates the directories named as operands, in the order specified, +using mode +.Li rwxrwxrwx (\&0777) +as modified by the current +.Xr umask 2 . +.Pp +The options are as follows: +.Pp +.Bl -tag -width indent +.It Fl m Ar mode +Set the file permission bits of the final created directory to +the specified mode. +The +.Ar mode +argument can be in any of the formats specified to the +.Xr chmod 1 +command. +If a symbolic mode is specified, the operation characters +.Dq + +and +.Dq - +are interpreted relative to an initial mode of +.Dq a=rwx . +.It Fl p +Create intermediate directories as required. +If this option is not specified, the full path prefix of each +operand must already exist. +On the other hand, with this option specified, no error will +be reported if a directory given as an operand already exists. +Intermediate directories are created with permission bits of +.Li rwxrwxrwx (\&0777) +as modified by the current umask, plus write and search +permission for the owner. +.It Fl v +Be verbose when creating directories, listing them as they are created. +.El +.Pp +The user must have write permission in the parent directory. +.Sh DIAGNOSTICS +.Ex -std +.Sh SEE ALSO +.Xr rmdir 1 +.Sh COMPATIBILITY +The +.Fl v +option is non-standard and its use in scripts is not recommended. +.Sh STANDARDS +The +.Nm +utility is expected to be +.St -p1003.2 +compatible. +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . diff --git a/files/Sources/files/mkdir/mkdir.c b/files/Sources/files/mkdir/mkdir.c new file mode 100644 index 00000000..b3e45f8e --- /dev/null +++ b/files/Sources/files/mkdir/mkdir.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 1983, 1992, 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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 +#ifndef lint +__used static char const copyright[] = +"@(#) Copyright (c) 1983, 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mkdir.c 8.2 (Berkeley) 1/25/94"; +#endif +#endif /* not lint */ +#include +__RCSID("$FreeBSD: src/bin/mkdir/mkdir.c,v 1.26 2002/06/30 05:13:54 obrien Exp $"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ios_error.h" + +static void usage(void); + +static int vflag; + +int +mkdir_main(int argc, char *argv[]) +{ + int ch, exitval, success, pflag; + mode_t omode, *set = (mode_t *)NULL; + char *mode; + // initialize flag + vflag = 0; + optind = 1; opterr = 1; optreset = 1; + + pflag = 0; + mode = NULL; + while ((ch = getopt(argc, argv, "m:pv")) != -1) + switch(ch) { + case 'm': + mode = optarg; + break; + case 'p': + pflag = 1; + break; + case 'v': + vflag = 1; + break; + case '?': + default: + usage(); + } + +// argc -= optind; + argv += optind; + if (argv[0] == NULL) + usage(); + + if (mode == NULL) { + omode = S_IRWXU | S_IRWXG | S_IRWXO; + } else { + if ((set = setmode(mode)) == NULL) { + errx(1, "invalid file mode: %s", mode); + } + omode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO); + free(set); + } + + for (exitval = 0; *argv != NULL; ++argv) { + success = 1; + if (pflag) { + int status = mkpath_np(*argv, omode); + if (status && status != EEXIST) { + fprintf(thread_stderr, "mkdir: %s: %s\n", *argv, strerror(status)); + // warnc(status, "%s", *argv); + success = 0; + } + } else if (mkdir(*argv, omode) < 0) { + if (errno == ENOTDIR || errno == ENOENT) + warn("%s", dirname(*argv)); + else + warn("%s", *argv); + success = 0; + } else if (vflag) + (void)fprintf(thread_stdout, "mkdir: created directory '%s'\n", *argv); + + if (!success) + exitval = 1; + /* + * The mkdir() and umask() calls both honor only the low + * nine bits, so if you try to set a mode including the + * sticky, setuid, setgid bits you lose them. Don't do + * this unless the user has specifically requested a mode, + * as chmod will (obviously) ignore the umask. + */ + if (success && mode != NULL && chmod(*argv, omode) == -1) { + warn("%s", *argv); + exitval = 1; + } + } + exit(exitval); // don't exit from main thread +} + +void +usage(void) +{ + + (void)fprintf(thread_stderr, "usage: mkdir [-pv] [-m mode] directory ...\n"); + exit (EX_USAGE); +} diff --git a/files/Sources/files/mv/mv.1 b/files/Sources/files/mv/mv.1 new file mode 100644 index 00000000..569e5c58 --- /dev/null +++ b/files/Sources/files/mv/mv.1 @@ -0,0 +1,184 @@ +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 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. +.\" +.\" @(#)mv.1 8.1 (Berkeley) 5/31/93 +.\" $FreeBSD: src/bin/mv/mv.1,v 1.25 2002/08/26 06:16:51 keramida Exp $ +.\" +.Dd July 9, 2002 +.Dt MV 1 +.Os +.Sh NAME +.Nm mv +.Nd move files +.Sh SYNOPSIS +.Nm mv +.Op Fl f | i | n +.Op Fl v +.Ar source target +.Nm mv +.Op Fl f | i | n +.Op Fl v +.Ar source ... directory +.Sh DESCRIPTION +In its first form, the +.Nm mv +utility renames the file named by the +.Ar source +operand to the destination path named by the +.Ar target +operand. +This form is assumed when the last operand does not name an already +existing directory. +.Pp +In its second form, +.Nm mv +moves each file named by a +.Ar source +operand to a destination file in the existing directory named by the +.Ar directory +operand. +The destination path for each operand is the pathname produced by the +concatenation of the last operand, a slash, and the final pathname +component of the named file. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl f +Do not prompt for confirmation before overwriting the destination +path. +(The +.Fl f +option overrides any previous +.Fl i +or +.Fl n +options.) +.It Fl i +Cause +.Nm mv +to write a prompt to standard error before moving a file that would +overwrite an existing file. +If the response from the standard input begins with the character +.Ql y +or +.Ql Y , +the move is attempted. +(The +.Fl i +option overrides any previous +.Fl f +or +.Fl n +options.) +.It Fl n +Do not overwrite an existing file. +(The +.Fl n +option overrides any previous +.Fl f +or +.Fl i +options.) +.It Fl v +Cause +.Nm mv +to be verbose, showing files after they are moved. +.El +.Pp +It is an error for either the +.Ar source +operand or the destination path to specify a directory unless both do. +.Pp +If the destination path does not have a mode which permits writing, +.Nm mv +prompts the user for confirmation as specified for the +.Fl i +option. +.Pp +As the +.Xr rename 2 +call does not work across file systems, +.Nm mv +uses +.Xr cp 1 +and +.Xr rm 1 +to accomplish the move. +The effect is equivalent to: +.Bd -literal -offset indent +rm -f destination_path && \e +cp -pRP source_file destination && \e +rm -rf source_file +.Ed +.Sh DIAGNOSTICS +.Ex -std +.Pp +The command "mv dir/afile dir" will abort with an error message. +.Sh LEGACY DIAGNOSTICS +In legacy mode, the command "mv dir/afile dir" will fail silently, +returning an exit code of 0. +.Pp +For more information about legacy mode, see +.Xr compat 5 . +.Sh SEE ALSO +.Xr cp 1 , +.Xr rm 1 , +.Xr symlink 7 +.Sh COMPATIBILITY +The +.Fl n +and +.Fl v +options are non-standard and their use in scripts is not recommended. +.Pp +The +.Nm mv +utility now supports HFS+ Finder and Extended Attributes and resource forks. +The +.Nm mv +utility will no longer strip resource forks off of HFS files. +For an alternative method, +refer to +.Xr cp 1 . +.Sh STANDARDS +The +.Nm mv +utility is expected to be +.St -p1003.2 +compatible. +.Sh HISTORY +A +.Nm mv +command appeared in +.At v1 . diff --git a/files/Sources/files/mv/mv.c b/files/Sources/files/mv/mv.c new file mode 100644 index 00000000..5911c379 --- /dev/null +++ b/files/Sources/files/mv/mv.c @@ -0,0 +1,455 @@ +/* + * 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 + * Ken Smith of The State University of New York at Buffalo. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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 +#ifndef lint +__used static char const copyright[] = +"@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mv.c 8.2 (Berkeley) 4/2/94"; +#endif +#endif /* not lint */ +#include +__RCSID("$FreeBSD: src/bin/mv/mv.c,v 1.39 2002/07/09 17:45:13 johan Exp $"); + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#include +#endif + +#ifdef __APPLE__ +// #include +// #else +#define COMPAT_MODE(a,b) (1) +#endif /* __APPLE__ */ + +#include "pathnames.h" +#include "ios_error.h" + +static int fflg, iflg, nflg, vflg; + +static int do_move(char *, char *); +static int fastcopy(char *, char *, struct stat *); +static void usage(void); + +int +mv_main(int argc, char *argv[]) +{ + size_t baselen, len; + int rval; + char *p, *endp; + struct stat sb; +#ifdef __APPLE__ + struct stat fsb, tsb; +#endif /* __APPLE__ */ + int ch; + char path[PATH_MAX]; + // initialize flags + fflg = iflg = nflg = vflg = 0; + optind = 1; opterr = 1; optreset = 1; + + + while ((ch = getopt(argc, argv, "finv")) != -1) + switch (ch) { + case 'i': + iflg = 1; + fflg = nflg = 0; + break; + case 'f': + fflg = 1; + iflg = nflg = 0; + break; + case 'n': + nflg = 1; + fflg = iflg = 0; + break; + case 'v': + vflg = 1; + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc < 2) + usage(); + + /* + * If the stat on the target fails or the target isn't a directory, + * try the move. More than 2 arguments is an error in this case. + */ + if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) { + if (argc > 2) + usage(); + exit(do_move(argv[0], argv[1])); + } + +#ifdef __APPLE__ + if (argc == 2 && !lstat(argv[0], &fsb) && !lstat(argv[1], &tsb) && + fsb.st_ino == tsb.st_ino && fsb.st_dev == tsb.st_dev && + fsb.st_gen == tsb.st_gen) { + /* + * We appear to be trying to move a directory into itself, + * but it may be that the filesystem is case insensitive and + * we are trying to rename the directory to a case-variant. + * Ignoring trailing slashes, we look for any difference in + * the directory names. If there is a difference we do + * the rename, otherwise we fall-thru to the traditional + * error. Note the lstat calls above (rather than stat) + * permit the renaming of symlinks to case-variants. + */ + char *q; + + for (p = argv[0] + strlen(argv[0]); p != argv[0]; ) { + p--; + if (*p != '/') + break; + } + for (q = argv[1] + strlen(argv[1]); q != argv[1]; ) { + q--; + if (*q != '/') + break; + } + for ( ; ; p--, q--) { + if (*p != *q) + exit(do_move(argv[0], argv[1])); + if (*p == '/') + break; + if (p == argv[0]) { + if (q == argv[1] || *(q-1) == '/') + break; + exit(do_move(argv[0], argv[1])); + } + if (q == argv[1]) { + if (p == argv[0] || *(p-1) == '/') + break; + exit(do_move(argv[0], argv[1])); + } + } + } +#endif /* __APPLE__ */ + + /* It's a directory, move each file into it. */ + if (strlen(argv[argc - 1]) > sizeof(path) - 1) { + errx(1, "%s: destination pathname too long", *argv); + } + (void)strcpy(path, argv[argc - 1]); + baselen = strlen(path); + endp = &path[baselen]; + if (!baselen || *(endp - 1) != '/') { + *endp++ = '/'; + ++baselen; + } + for (rval = 0; --argc; ++argv) { + /* + * Find the last component of the source pathname. It + * may have trailing slashes. + */ + p = *argv + strlen(*argv); + while (p != *argv && p[-1] == '/') + --p; + while (p != *argv && p[-1] != '/') + --p; + + if ((baselen + (len = strlen(p))) >= PATH_MAX) { + warnx("%s: destination pathname too long", *argv); + rval = 1; + } else { + memmove(endp, p, (size_t)len + 1); + if (COMPAT_MODE("bin/mv", "unix2003")) { + /* + * For Unix 2003 compatibility, check if old and new are + * same file, and produce an error * (like on Sun) that + * conformance test 66 in mv.ex expects. + */ + if (!stat(*argv, &fsb) && !stat(path, &tsb) && + fsb.st_ino == tsb.st_ino && + fsb.st_dev == tsb.st_dev && + fsb.st_gen == tsb.st_gen) { + (void)fprintf(thread_stderr, "mv: %s and %s are identical\n", + *argv, path); + rval = 2; /* Like the Sun */ + } else { + if (do_move(*argv, path)) + rval = 1; + } + } else { + if (do_move(*argv, path)) + rval = 1; + } + } + } + exit(rval); +} + +int +do_move(char *from, char *to) +{ + struct stat sb; + int ask, ch, first; + char modep[15]; + + /* + * Check access. If interactive and file exists, ask user if it + * should be replaced. Otherwise if file exists but isn't writable + * make sure the user wants to clobber it. + */ + if (!fflg && !access(to, F_OK)) { + + /* prompt only if source exist */ + if (lstat(from, &sb) == -1) { + warn("%s", from); + return (1); + } + +#define YESNO "(y/n [n]) " + ask = 0; + if (nflg) { + if (vflg) + fprintf(thread_stdout, "%s not overwritten\n", to); + return (0); + } else if (iflg) { + (void)fprintf(thread_stderr, "overwrite %s? %s", to, YESNO); + ask = 1; + } else if (access(to, W_OK) && !stat(to, &sb)) { + strmode(sb.st_mode, modep); + (void)fprintf(thread_stderr, "override %s%s%s/%s for %s? %s", + modep + 1, modep[9] == ' ' ? "" : " ", + user_from_uid(sb.st_uid, 0), + group_from_gid(sb.st_gid, 0), to, YESNO); + ask = 1; + } + if (ask) { + fflush(thread_stderr); + first = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + if (first != 'y' && first != 'Y') { + (void)fprintf(thread_stderr, "not overwritten\n"); + return (0); + } + } + } + if (!rename(from, to)) { + if (vflg) + fprintf(thread_stdout, "%s -> %s\n", from, to); + return (0); + } + + if (errno == EXDEV) { + struct statfs sfs; + char path[PATH_MAX]; + + /* Can't mv(1) a mount point. */ + if (realpath(from, path) == NULL) { + warnx("cannot resolve %s: %s", from, path); + return (1); + } + if (!statfs(path, &sfs) && !strcmp(path, sfs.f_mntonname)) { + warnx("cannot rename a mount point"); + return (1); + } + } else { + warn("rename %s to %s", from, to); + return (1); + } + + /* + * If rename fails because we're trying to cross devices, and + * it's a regular file, do the copy internally; otherwise, use + * cp and rm. + */ + if (lstat(from, &sb)) { + warn("%s", from); + return (1); + } + return (S_ISREG(sb.st_mode) ? + fastcopy(from, to, &sb): 0); // : copy(from, to)); +} + +int +fastcopy(char *from, char *to, struct stat *sbp) +{ + struct timeval tval[2]; + static u_int blen; + static char *bp; + mode_t oldmode; + ssize_t nread; + int from_fd, to_fd; + + if ((from_fd = open(from, O_RDONLY, 0)) < 0) { + warn("%s", from); + return (1); + } + if (blen < sbp->st_blksize) { + if (bp != NULL) + free(bp); + if ((bp = malloc((size_t)sbp->st_blksize)) == NULL) { + blen = 0; + warnx("malloc failed"); + return (1); + } + blen = sbp->st_blksize; + } + while ((to_fd = + open(to, O_CREAT | O_EXCL | O_TRUNC | O_WRONLY, 0)) < 0) { + if (errno == EEXIST && unlink(to) == 0) + continue; + warn("%s", to); + (void)close(from_fd); + return (1); + } +#ifdef __APPLE__ + { + struct statfs sfs; + + /* + * Pre-allocate blocks for the destination file if it + * resides on Xsan. + */ + if (fstatfs(to_fd, &sfs) == 0 && + strcmp(sfs.f_fstypename, "acfs") == 0) { + fstore_t fst; + + fst.fst_flags = 0; + fst.fst_posmode = F_PEOFPOSMODE; + fst.fst_offset = 0; + fst.fst_length = sbp->st_size; + + (void) fcntl(to_fd, F_PREALLOCATE, &fst); + } + } +#endif /* __APPLE__ */ + while ((nread = read(from_fd, bp, (size_t)blen)) > 0) + if (write(to_fd, bp, (size_t)nread) != nread) { + warn("%s", to); + goto err; + } + if (nread < 0) { + warn("%s", from); +err: if (unlink(to)) + warn("%s: remove", to); + (void)close(from_fd); + (void)close(to_fd); + return (1); + } +#ifdef __APPLE__ + /* XATTR can fail if to_fd has mode 000 */ + if (fcopyfile(from_fd, to_fd, NULL, COPYFILE_ACL | COPYFILE_XATTR) < 0) { + warn("%s: unable to move extended attributes and ACL from %s", + to, from); + } +#endif + (void)close(from_fd); + + oldmode = sbp->st_mode & ALLPERMS; + if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) { + warn("%s: set owner/group (was: %lu/%lu)", to, + (u_long)sbp->st_uid, (u_long)sbp->st_gid); + if (oldmode & (S_ISUID | S_ISGID)) { + warnx( + "mv: %s: owner/group changed; clearing suid/sgid (mode was 0%03o)\n", + to, oldmode); + sbp->st_mode &= ~(S_ISUID | S_ISGID); + } + } + if (fchmod(to_fd, sbp->st_mode)) + warn("%s: set mode (was: 0%03o)", to, oldmode); + /* + * XXX + * NFS doesn't support chflags; ignore errors unless there's reason + * to believe we're losing bits. (Note, this still won't be right + * if the server supports flags and we were trying to *remove* flags + * on a file that we copied, i.e., that we didn't create.) + */ + errno = 0; + if (fchflags(to_fd, (u_int)sbp->st_flags)) + if (errno != ENOTSUP || sbp->st_flags != 0) + warn("%s: set flags (was: 0%07o)", to, sbp->st_flags); + + tval[0].tv_sec = sbp->st_atime; + tval[1].tv_sec = sbp->st_mtime; + tval[0].tv_usec = tval[1].tv_usec = 0; + if (utimes(to, tval)) + warn("%s: set times", to); + + if (close(to_fd)) { + warn("%s", to); + return (1); + } + + if (unlink(from)) { + warn("%s: remove", from); + return (1); + } + if (vflg) + fprintf(thread_stdout, "%s -> %s\n", from, to); + return (0); +} + +void +usage(void) +{ + + (void)fprintf(thread_stderr, "%s\n%s\n", + "usage: mv [-f | -i | -n] [-v] source target", + " mv [-f | -i | -n] [-v] source ... directory"); + exit(EX_USAGE); +} diff --git a/files/Sources/files/mv/pathnames.h b/files/Sources/files/mv/pathnames.h new file mode 100644 index 00000000..514678ff --- /dev/null +++ b/files/Sources/files/mv/pathnames.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1989, 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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. + * + * @(#)pathnames.h 8.1 (Berkeley) 5/31/93 + * $FreeBSD: src/bin/mv/pathnames.h,v 1.6 2002/05/17 11:38:48 jmallett Exp $ + */ + +#define _PATH_RM "/bin/rm" +#ifdef __APPLE__ +#define _PATH_CP "/bin/cp" +#endif /* __APPLE__ */ diff --git a/files/Sources/files/ncurses_dll.h b/files/Sources/files/ncurses_dll.h new file mode 100644 index 00000000..e24feee2 --- /dev/null +++ b/files/Sources/files/ncurses_dll.h @@ -0,0 +1,86 @@ +/**************************************************************************** + * Copyright (c) 1998-2006,2007 Free Software Foundation, Inc. * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, distribute with modifications, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included * + * in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * + * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name(s) of the above copyright * + * holders shall not be used in advertising or otherwise to promote the * + * sale, use or other dealings in this Software without prior written * + * authorization. * + ****************************************************************************/ +/* $Id: ncurses_dll.h,v 1.6 2007/03/10 19:21:49 tom Exp $ */ + +#ifndef NCURSES_DLL_H_incl +#define NCURSES_DLL_H_incl 1 + +/* no longer needed on cygwin or mingw, thanks to auto-import */ +/* but this structure may be useful at some point for an MSVC build */ +/* so, for now unconditionally define the important flags */ +/* "the right way" for proper static and dll+auto-import behavior */ +#undef NCURSES_DLL +#define NCURSES_STATIC + +#if defined(__CYGWIN__) +# if defined(NCURSES_DLL) +# if defined(NCURSES_STATIC) +# undef NCURSES_STATIC +# endif +# endif +# undef NCURSES_IMPEXP +# undef NCURSES_API +# undef NCURSES_EXPORT +# undef NCURSES_EXPORT_VAR +# if defined(NCURSES_DLL) +/* building a DLL */ +# define NCURSES_IMPEXP __declspec(dllexport) +# elif defined(NCURSES_STATIC) +/* building or linking to a static library */ +# define NCURSES_IMPEXP /* nothing */ +# else +/* linking to the DLL */ +# define NCURSES_IMPEXP __declspec(dllimport) +# endif +# define NCURSES_API __cdecl +# define NCURSES_EXPORT(type) NCURSES_IMPEXP type NCURSES_API +# define NCURSES_EXPORT_VAR(type) NCURSES_IMPEXP type +#endif + +/* Take care of non-cygwin platforms */ +#if !defined(NCURSES_IMPEXP) +# define NCURSES_IMPEXP /* nothing */ +#endif +#if !defined(NCURSES_API) +# define NCURSES_API /* nothing */ +#endif +#if !defined(NCURSES_EXPORT) +# define NCURSES_EXPORT(type) NCURSES_IMPEXP type NCURSES_API +#endif +#if !defined(NCURSES_EXPORT_VAR) +# define NCURSES_EXPORT_VAR(type) NCURSES_IMPEXP type +#endif + +/* + * For reentrant code, we map the various global variables into SCREEN by + * using functions to access them. + */ +#define NCURSES_PUBLIC_VAR(name) _nc_##name +#define NCURSES_WRAPPED_VAR(type,name) extern type NCURSES_PUBLIC_VAR(name)(void) + +#endif /* NCURSES_DLL_H_incl */ diff --git a/files/Sources/files/rm/rm.1 b/files/Sources/files/rm/rm.1 new file mode 100644 index 00000000..c40f8a7b --- /dev/null +++ b/files/Sources/files/rm/rm.1 @@ -0,0 +1,216 @@ +.\" Copyright (c) 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 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. +.\" +.\" @(#)rm.1 8.5 (Berkeley) 12/5/94 +.\" $FreeBSD: src/bin/rm/rm.1,v 1.23 2001/07/15 07:49:05 dd Exp $ +.\" +.Dd January 28, 1999 +.Dt RM 1 +.Os +.Sh NAME +.Nm rm , +.Nm unlink +.Nd remove directory entries +.Sh SYNOPSIS +.Nm +.Op Fl dfiPRrvW +.Ar +.Nm unlink +.Ar file +.Sh DESCRIPTION +The +.Nm +utility attempts to remove the non-directory type files specified on the +command line. +If the permissions of the file do not permit writing, and the standard +input device is a terminal, the user is prompted (on the standard error +output) for confirmation. +.Pp +The options are as follows: +.Bl -tag -width Fl +.It Fl d +Attempt to remove directories as well as other types of files. +.It Fl f +Attempt to remove the files without prompting for confirmation, +regardless of the file's permissions. +If the file does not exist, do not display a diagnostic message or modify +the exit status to reflect an error. +The +.Fl f +option overrides any previous +.Fl i +options. +.It Fl i +Request confirmation before attempting to remove each file, regardless of +the file's permissions, or whether or not the standard input device is a +terminal. +The +.Fl i +option overrides any previous +.Fl f +options. +.It Fl P +Overwrite regular files before deleting them. +Files are overwritten three times, first with the byte pattern 0xff, +then 0x00, and then 0xff again, before they are deleted. +.It Fl R +Attempt to remove the file hierarchy rooted in each file argument. +The +.Fl R +option implies the +.Fl d +option. +If the +.Fl i +option is specified, the user is prompted for confirmation before +each directory's contents are processed (as well as before the attempt +is made to remove the directory). +If the user does not respond affirmatively, the file hierarchy rooted in +that directory is skipped. +.Pp +.It Fl r +Equivalent to +.Fl R . +.It Fl v +Be verbose when deleting files, showing them as they are removed. +.It Fl W +Attempt to undelete the named files. +Currently, this option can only be used to recover +files covered by whiteouts. +.El +.Pp +The +.Nm +utility removes symbolic links, not the files referenced by the links. +.Pp +It is an error to attempt to remove the files +.Dq .\& +or +.Dq .. . +.Pp +When the utility is called as +.Nm unlink , +only one argument, +which must not be a directory, +may be supplied. +No options may be supplied in this simple mode of operation, +which performs an +.Xr unlink 2 +operation on the passed argument. +.Pp +The +.Nm +utility exits 0 if all of the named files or file hierarchies were removed, +or if the +.Fl f +option was specified and all of the existing files or file hierarchies were +removed. +If an error occurs, +.Nm +exits with a value >0. +.Sh NOTE +The +.Nm +command uses +.Xr getopt 3 +to parse its arguments, which allows it to accept +the +.Sq Li -- +option which will cause it to stop processing flag options at that +point. This will allow the removal of file names that begin +with a dash +.Pq Sq - . +For example: +.Dl rm -- -filename +The same behavior can be obtained by using an absolute or relative +path reference. For example: +.Dl rm /home/user/-filename +.Dl rm ./-filename +.Sh SEE ALSO +.Xr rmdir 1 , +.Xr undelete 2 , +.Xr unlink 2 , +.Xr fts 3 , +.Xr getopt 3 , +.Xr symlink 7 +.Sh BUGS +The +.Fl P +option assumes that the underlying file system is a fixed-block file +system. In addition, only regular files are overwritten, other types of files +are not. +.Sh COMPATIBILITY +The +.Nm +utility differs from historical implementations in that the +.Fl f +option only masks attempts to remove non-existent files instead of +masking a large variety of errors. +The +.Fl v +option is non-standard and its use in scripts is not recommended. +.Pp +Also, historical +.Bx +implementations prompted on the standard output, +not the standard error output. +.Sh STANDARDS +The +.Nm +command is almost +.St -p1003.2 +compatible, except that +.Tn POSIX +requires +.Nm +to act like +.Xr rmdir 1 +when the +.Ar file +specified is a directory. This implementation requires the +.Fl d +option if such behavior is desired. This follows the historical +behavior of +.Nm +with respect to directories. +.Pp +The simplified +.Nm unlink +command conforms to +.St -susv2 . +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . diff --git a/files/Sources/files/rm/rm.c b/files/Sources/files/rm/rm.c new file mode 100644 index 00000000..2acb7442 --- /dev/null +++ b/files/Sources/files/rm/rm.c @@ -0,0 +1,583 @@ +/*- + * Copyright (c) 1990, 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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 +#ifndef lint +__used static const char copyright[] = +"@(#) Copyright (c) 1990, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)rm.c 8.5 (Berkeley) 4/18/94"; +#else +__used static const char rcsid[] = + "$FreeBSD: src/bin/rm/rm.c,v 1.33 2001/06/13 15:01:25 ru Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#include +#include +// #include "get_compat.h" +// #else +#define COMPAT_MODE(func, mode) 1 +#endif +#include "ios_error.h" + +static int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok; +static uid_t uid; + +static int check __P((char *, char *, struct stat *)); +static int checkdir __P((char *)); +static int yes_or_no __P((void)); +static void checkdot __P((char **)); +static void rm_file __P((char **)); +static void rm_overwrite __P((char *, struct stat *)); +static void rm_tree __P((char **)); +static void usage __P((void)); + +/* + * rm -- + * This rm is different from historic rm's, but is expected to match + * POSIX 1003.2 behavior. The most visible difference is that -f + * has two specific effects now, ignore non-existent files and force + * file removal. + */ +int +rm_main(argc, argv) + int argc; + char *argv[]; +{ + int ch, rflag; + char *p; + + if (argc < 1) + usage(); + + // init all flags, something quite important for rm + dflag = eval = fflag = iflag = Pflag = vflag = Wflag = stdin_ok = 0; + optind = 1; opterr = 1; optreset = 1; + + /* + * Test for the special case where the utility is called as + * "unlink", for which the functionality provided is greatly + * simplified. + */ + if ((p = rindex(argv[0], '/')) == NULL) + p = argv[0]; + else + ++p; + uid = geteuid(); + if (strcmp(p, "unlink") == 0) { + if (argc == 2) { + rm_file(&argv[1]); + exit(eval); + } else + usage(); + } + + Pflag = rflag = 0; + while ((ch = getopt(argc, argv, "dfiPRrvW")) != -1) + switch(ch) { + case 'd': + dflag = 1; + break; + case 'f': + fflag = 1; + iflag = 0; + break; + case 'i': + fflag = 0; + iflag = 1; + break; + case 'P': + Pflag = 1; + break; + case 'R': + case 'r': /* Compatibility. */ + rflag = 1; + break; + case 'v': + vflag = 1; + break; + case 'W': + Wflag = 1; + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc < 1) { + if (fflag) + return 0; + usage(); + } + + checkdot(argv); + + if (*argv) { + stdin_ok = ios_isatty(STDIN_FILENO); + + if (rflag) + rm_tree(argv); + else + rm_file(argv); + } + + exit (eval); +} + +void +rm_tree(argv) + char **argv; +{ + FTS *fts; + FTSENT *p; + int needstat; + int flags; + int rval; + int wantConformance = COMPAT_MODE("bin/rm", "unix2003"); + /* + * Remove a file hierarchy. If forcing removal (-f), or interactive + * (-i) or can't ask anyway (stdin_ok), don't stat the file. + */ + needstat = !uid || (!fflag && !iflag && stdin_ok); + + /* + * If the -i option is specified, the user can skip on the pre-order + * visit. The fts_number field flags skipped directories. + */ +#define SKIPPED 1 + + flags = FTS_PHYSICAL; + if (!needstat) + flags |= FTS_NOSTAT; + if (Wflag) + flags |= FTS_WHITEOUT; + if (!(fts = fts_open(argv, flags, NULL))) { + if (fflag && errno == ENOENT) + return; + err(1, NULL); + } + while ((p = fts_read(fts)) != NULL) { + switch (p->fts_info) { + case FTS_DNR: + if (!fflag || p->fts_errno != ENOENT) { + warnx("%s: %s", + p->fts_path); + eval = 1; + } + continue; + case FTS_ERR: + errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno)); + case FTS_NS: + /* + * FTS_NS: assume that if can't stat the file, it + * can't be unlinked. + */ + if (!needstat) + break; + if (!fflag || p->fts_errno != ENOENT) { + warnx("%s: %s", + p->fts_path); + eval = 1; + } + continue; + case FTS_D: + /* Pre-order: give user chance to skip. */ + /* In conformance mode the user is prompted to skip processing the contents. + * Then the option to delete the dir is presented post-order */ + if (!fflag && + ( (wantConformance && !checkdir(p->fts_path)) || + (!wantConformance && !check(p->fts_path, p->fts_accpath, p->fts_statp)) + ) + ){ + (void)fts_set(fts, p, FTS_SKIP); + p->fts_number = SKIPPED; + } + else if (!uid && + (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && + !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && + chflags(p->fts_accpath, + p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0) + goto err; + continue; + case FTS_DP: + /* Post-order: see if user skipped. */ + if(p->fts_number == SKIPPED)/*in legacy mode, the user was prompted pre-order */ + continue; + else if(wantConformance) + { + /* delete directory if force is on, or if user answers Y to prompt */ + if(fflag || check(p->fts_path, p->fts_accpath, p->fts_statp)) + break; + else + continue; + } + break; + default: + if (!fflag && + !check(p->fts_path, p->fts_accpath, p->fts_statp)) + continue; + } + + rval = 0; + if (!uid && + (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && + !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE))) + rval = chflags(p->fts_accpath, + p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); + if (rval == 0) { + /* + * If we can't read or search the directory, may still be + * able to remove it. Don't print out the un{read,search}able + * message unless the remove fails. + */ + switch (p->fts_info) { + case FTS_DP: + case FTS_DNR: + rval = rmdir(p->fts_accpath); + if (rval == 0 || (fflag && errno == ENOENT)) { + if (rval == 0 && vflag) + (void)fprintf(thread_stdout, "%s\n", + p->fts_path); + continue; + } + break; + + case FTS_W: + rval = undelete(p->fts_accpath); + if (rval == 0 && (fflag && errno == ENOENT)) { + if (vflag) + (void)fprintf(thread_stdout, "%s\n", + p->fts_path); + continue; + } + break; + + default: +#ifdef __APPLE__ + if (Pflag) { + if (removefile(p->fts_accpath, NULL, REMOVEFILE_SECURE_7_PASS)) /* overwrites and unlinks */ + eval = rval = 1; + } else + rval = unlink(p->fts_accpath); +#else /* !__APPLE_ */ + if (Pflag) + rm_overwrite(p->fts_accpath, NULL); + rval = unlink(p->fts_accpath); +#endif /* __APPLE__ */ + if (rval == 0 || (fflag && errno == ENOENT)) { + if (rval == 0 && vflag) + (void)fprintf(thread_stdout, "%s\n", + p->fts_path); + continue; + } + } + } +err: + warn("%s", p->fts_path); + eval = 1; + } + fts_close(fts); + if (errno) { + err(1, "fts_read"); + } +} + +void +rm_file(argv) + char **argv; +{ + struct stat sb; + int rval; + char *f; + + /* + * Remove a file. POSIX 1003.2 states that, by default, attempting + * to remove a directory is an error, so must always stat the file. + */ + while ((f = *argv++) != NULL) { + /* Assume if can't stat the file, can't unlink it. */ + if (lstat(f, &sb)) { + if (Wflag) { + sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR; + } else { + if (!fflag || errno != ENOENT) { + warn("%s", f); + eval = 1; + } + continue; + } + } else if (Wflag) { + warnx("%s: %s", f, strerror(EEXIST)); + eval = 1; + continue; + } + + if (S_ISDIR(sb.st_mode) && !dflag) { + warnx("%s: is a directory", f); + eval = 1; + continue; + } + if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb)) + continue; + rval = 0; + if (!uid && + (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) && + !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE))) + rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE)); + if (rval == 0) { + if (S_ISWHT(sb.st_mode)) + rval = undelete(f); + else if (S_ISDIR(sb.st_mode)) + rval = rmdir(f); + else { +#ifdef __APPLE__ + if (Pflag) { + if (removefile(f, NULL, REMOVEFILE_SECURE_7_PASS)) /* overwrites and unlinks */ + eval = rval = 1; + } else + rval = unlink(f); +#else /* !__APPLE__ */ + if (Pflag) + rm_overwrite(f, &sb); + rval = unlink(f); +#endif /* __APPLE__ */ + } + } + if (rval && (!fflag || errno != ENOENT)) { + warn("%s", f); + eval = 1; + } + if (vflag && rval == 0) + (void)fprintf(thread_stdout, "%s\n", f); + } +} + +/* + * rm_overwrite -- + * Overwrite the file 3 times with varying bit patterns. + * + * XXX + * This is a cheap way to *really* delete files. Note that only regular + * files are deleted, directories (and therefore names) will remain. + * Also, this assumes a fixed-block file system (like FFS, or a V7 or a + * System V file system). In a logging file system, you'll have to have + * kernel support. + */ +void +rm_overwrite(file, sbp) + char *file; + struct stat *sbp; +{ + struct stat sb; + struct statfs fsb; + off_t len; + int bsize, fd, wlen; + char *buf = NULL; + + if (sbp == NULL) { + if (lstat(file, &sb)) + goto err; + sbp = &sb; + } + if (!S_ISREG(sbp->st_mode)) + return; + if ((fd = open(file, O_WRONLY, 0)) == -1) + goto err; + if (fstatfs(fd, &fsb) == -1) + goto err; + bsize = MAX(fsb.f_iosize, 1024); + if ((buf = malloc(bsize)) == NULL) { + err(1, "malloc"); + } + +#define PASS(byte) { \ + memset(buf, byte, bsize); \ + for (len = sbp->st_size; len > 0; len -= wlen) { \ + wlen = len < bsize ? (int)len : bsize; \ + if (write(fd, buf, wlen) != wlen) \ + goto err; \ + } \ +} + PASS(0xff); + if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) + goto err; + PASS(0x00); + if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) + goto err; + PASS(0xff); + if (!fsync(fd) && !close(fd)) { + free(buf); + return; + } + +err: eval = 1; + if (buf) + free(buf); + warn("%s", file); +} + +int +yes_or_no() +{ + int ch, first; + (void)fflush(thread_stderr); + + first = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + return (first == 'y' || first == 'Y'); +} + +int +checkdir(path) + char *path; +{ + if(!iflag) + return 1; //if not interactive, process directory's contents + (void)fprintf(thread_stderr, "examine files in directory %s? ", path); + return yes_or_no(); +} + +int +check(path, name, sp) + char *path, *name; + struct stat *sp; +{ + char modep[15], *flagsp; + + /* Check -i first. */ + if (iflag) + (void)fprintf(thread_stderr, "remove %s? ", path); + else { + /* + * If it's not a symbolic link and it's unwritable and we're + * talking to a terminal, ask. Symbolic links are excluded + * because their permissions are meaningless. Check stdin_ok + * first because we may not have stat'ed the file. + */ + if (!stdin_ok || S_ISLNK(sp->st_mode) || + (!access(name, W_OK) && + !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && + (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid))) + return (1); + strmode(sp->st_mode, modep); + if ((flagsp = fflagstostr(sp->st_flags)) == NULL) { + err(1, NULL); + } + (void)fprintf(thread_stderr, "override %s%s%s/%s %s%sfor %s? ", + modep + 1, modep[9] == ' ' ? "" : " ", + user_from_uid(sp->st_uid, 0), + group_from_gid(sp->st_gid, 0), + *flagsp ? flagsp : "", *flagsp ? " " : "", + path); + free(flagsp); + } + return yes_or_no(); +} + + +#define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2]))) +void +checkdot(argv) + char **argv; +{ + char *p, **save, **t; + int complained; + + complained = 0; + for (t = argv; *t;) { + size_t len = strlen(*t); + char truncated[len]; + + if ((p = strrchr(*t, '/')) != NULL) { + if (p[1] == '\0') { // one or more trailing / -- treat as if not present + for (; (p > *t) && (p[-1] == '/');) { + len--; + p--; + } + strlcpy(truncated, *t, len); + p = strrchr(truncated, '/'); + if (p) { + ++p; + } else { + p = truncated; + } + } else { + ++p; + } + } else { + p = *t; + } + if (ISDOT(p)) { + if (!complained++) + warnx("\".\" and \"..\" may not be removed"); + eval = 1; + for (save = t; (t[0] = t[1]) != NULL; ++t) + continue; + t = save; + } else + ++t; + } +} + +void +usage() +{ + + (void)fprintf(thread_stderr, "%s\n%s\n", + "usage: rm [-f | -i] [-dPRrvW] file ...", + " unlink file"); + exit(EX_USAGE); +} diff --git a/files/Sources/files/rm/unlink.1 b/files/Sources/files/rm/unlink.1 new file mode 100644 index 00000000..6764e6d1 --- /dev/null +++ b/files/Sources/files/rm/unlink.1 @@ -0,0 +1 @@ +.so man1/rm.1 diff --git a/files/Sources/files/rmdir/rmdir.1 b/files/Sources/files/rmdir/rmdir.1 new file mode 100644 index 00000000..577637a5 --- /dev/null +++ b/files/Sources/files/rmdir/rmdir.1 @@ -0,0 +1,100 @@ +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 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. +.\" +.\" @(#)rmdir.1 8.1 (Berkeley) 5/31/93 +.\" $FreeBSD: src/bin/rmdir/rmdir.1,v 1.9 2000/11/20 11:39:40 ru Exp $ +.\" +.Dd May 31, 1993 +.Dt RMDIR 1 +.Os +.Sh NAME +.Nm rmdir +.Nd remove directories +.Sh SYNOPSIS +.Nm +.Op Fl p +.Ar directory ... +.Sh DESCRIPTION +The +.Nm +utility removes the directory entry specified by +each +.Ar directory +argument, provided it is empty. +.Pp +Arguments are processed in the order given. +In order to remove both a parent directory and a subdirectory +of that parent, the subdirectory +must be specified first so the parent directory +is empty when +.Nm +tries to remove it. +.Pp +The following option is available: +.Bl -tag -width indent +.It Fl p +Each +.Ar directory +argument is treated as a pathname of which all +components will be removed, if they are empty, +starting with the last most component. +(See +.Xr rm 1 +for fully non-discriminant recursive removal.) +.El +.Pp +The +.Nm +utility exits with one of the following values: +.Bl -tag -width Ds +.It Li \&0 +Each directory entry specified by a dir operand +referred to an empty directory and was removed +successfully. +.It Li \&>\&0 +An error occurred. +.El +.Sh SEE ALSO +.Xr rm 1 +.Sh STANDARDS +The +.Nm +command is expected to be +.St -p1003.2 +compatible. +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . diff --git a/files/Sources/files/rmdir/rmdir.c b/files/Sources/files/rmdir/rmdir.c new file mode 100644 index 00000000..46059355 --- /dev/null +++ b/files/Sources/files/rmdir/rmdir.c @@ -0,0 +1,125 @@ +/*- + * Copyright (c) 1992, 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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 +#ifndef lint +__used static char const copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)rmdir.c 8.3 (Berkeley) 4/2/94"; +#endif +#endif /* not lint */ +#include +__RCSID("$FreeBSD: src/bin/rmdir/rmdir.c,v 1.13 2002/06/30 05:15:03 obrien Exp $"); + +#include +#include +#include +#include +#include +#include +#include "ios_error.h" + +static int rm_path(char *); +static void usage(void); + +int +rmdir_main(int argc, char *argv[]) +{ + int ch, errors; + int pflag; + + pflag = 0; + optind = 1; opterr = 1; optreset = 1; + + while ((ch = getopt(argc, argv, "p")) != -1) + switch(ch) { + case 'p': + pflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc == 0) + usage(); + + for (errors = 0; *argv; argv++) { + if (rmdir(*argv) < 0) { + warn("%s", *argv); + errors = 1; + } else if (pflag) + errors |= rm_path(*argv); + } + + exit(errors); +} + +int +rm_path(char *path) +{ + char *p; + + p = path + strlen(path); + while (--p > path && *p == '/') + ; + *++p = '\0'; + while ((p = strrchr(path, '/')) != NULL) { + /* Delete trailing slashes. */ + while (--p > path && *p == '/') + ; + *++p = '\0'; + + if (rmdir(path) < 0) { + warn("%s", path); + return (1); + } + } + + return (0); +} + +void +usage(void) +{ + + (void)fprintf(thread_stderr, "usage: rmdir [-p] directory ...\n"); + exit(1); +} diff --git a/files/Sources/files/stat/readlink.1 b/files/Sources/files/stat/readlink.1 new file mode 100644 index 00000000..968704b1 --- /dev/null +++ b/files/Sources/files/stat/readlink.1 @@ -0,0 +1 @@ +.so man1/stat.1 diff --git a/files/Sources/files/stat/stat.1 b/files/Sources/files/stat/stat.1 new file mode 100644 index 00000000..79c774fd --- /dev/null +++ b/files/Sources/files/stat/stat.1 @@ -0,0 +1,534 @@ +.\" $NetBSD: stat.1,v 1.11 2003/05/08 13:07:10 wiz Exp $ +.\" +.\" Copyright (c) 2002 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Andrew Brown and Jan Schaumann. +.\" +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the NetBSD +.\" Foundation, Inc. and its contributors. +.\" 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +.\" +.\" $FreeBSD: src/usr.bin/stat/stat.1,v 1.8 2005/06/14 11:50:53 ru Exp $ +.\" +.Dd May 8, 2003 +.Dt STAT 1 +.Os +.Sh NAME +.Nm readlink , +.Nm stat +.Nd display file status +.Sh SYNOPSIS +.Nm stat +.Op Fl FLnq +.Op Fl f Ar format | Fl l | r | s | x +.Op Fl t Ar timefmt +.Op Ar +.Nm readlink +.Op Fl n +.Op Ar +.Sh DESCRIPTION +The +.Nm stat +utility displays information about the file pointed to by +.Ar file . +Read, write or execute permissions of the named file are not required, but +all directories listed in the path name leading to the file must be +searchable. +If no argument is given, +.Nm stat +displays information about the file descriptor for standard input. +.Pp +When invoked as +.Nm readlink , +only the target of the symbolic link is printed. +If the given argument is not a symbolic link, +.Nm readlink +will print nothing and exit with an error. +.Pp +The information displayed is obtained by calling +.Xr lstat 2 +with the given argument and evaluating the returned structure. +.Pp +The options are as follows: +.Bl -tag -width indent +.\" ========== +.It Fl F +As in +.Xr ls 1 , +display a slash +.Pq Ql / +immediately after each pathname that is a directory, +an asterisk +.Pq Ql * +after each that is executable, +an at sign +.Pq Ql @ +after each symbolic link, +a percent sign +.Pq Ql % +after each whiteout, +an equal sign +.Pq Ql = +after each socket, +and a vertical bar +.Pq Ql | +after each that is a FIFO. +The use of +.Fl F +implies +.Fl l . +.\" ========== +.It Fl f Ar format +Display information using the specified format. +See the +.Sx FORMATS +section for a description of valid formats. +.\" ========== +.It Fl L +Use +.Xr stat 2 +instead of +.Xr lstat 2 . +The information reported by +.Nm stat +will refer to the target of +.Ar file , +if file is a symbolic link, and not to +.Ar file +itself. +.\" ========== +.It Fl l +Display output in +.Nm ls Fl lT +format. +.\" ========== +.It Fl n +Do not force a newline to appear at the end of each piece of output. +.\" ========== +.It Fl q +Suppress failure messages if calls to +.Xr stat 2 +or +.Xr lstat 2 +fail. +When run as +.Nm readlink , +error messages are automatically suppressed. +.\" ========== +.It Fl r +Display raw information. +That is, for all the fields in the +.Vt stat +structure, +display the raw, numerical value (for example, times in seconds since the +epoch, etc.). +.\" ========== +.It Fl s +Display information in +.Dq "shell output" , +suitable for initializing variables. +.\" ========== +.It Fl t Ar timefmt +Display timestamps using the specified format. +This format is +passed directly to +.Xr strftime 3 . +.\" ========== +.It Fl x +Display information in a more verbose way as known from some +.Tn Linux +distributions. +.El +.Ss Formats +Format strings are similar to +.Xr printf 3 +formats in that they start with +.Cm % , +are then followed by a sequence of formatting characters, and end in +a character that selects the field of the +.Vt "struct stat" +which is to be formatted. +If the +.Cm % +is immediately followed by one of +.Cm n , t , % , +or +.Cm @ , +then a newline character, a tab character, a percent character, +or the current file number is printed, otherwise the string is +examined for the following: +.Pp +Any of the following optional flags: +.Bl -tag -width indent +.It Cm # +Selects an alternate output form for octal and hexadecimal output. +Non-zero octal output will have a leading zero, and non-zero +hexadecimal output will have +.Dq Li 0x +prepended to it. +.It Cm + +Asserts that a sign indicating whether a number is positive or negative +should always be printed. +Non-negative numbers are not usually printed +with a sign. +.It Cm - +Aligns string output to the left of the field, instead of to the right. +.It Cm 0 +Sets the fill character for left padding to the +.Ql 0 +character, instead of a space. +.It space +Reserves a space at the front of non-negative signed output fields. +A +.Sq Cm + +overrides a space if both are used. +.El +.Pp +Then the following fields: +.Bl -tag -width indent +.It Ar size +An optional decimal digit string specifying the minimum field width. +.It Ar prec +An optional precision composed of a decimal point +.Sq Cm \&. +and a decimal digit string that indicates the maximum string length, +the number of digits to appear after the decimal point in floating point +output, or the minimum number of digits to appear in numeric output. +.It Ar fmt +An optional output format specifier which is one of +.Cm D , O , U , X , F , +or +.Cm S . +These represent signed decimal output, octal output, unsigned decimal +output, hexadecimal output, floating point output, and string output, +respectively. +Some output formats do not apply to all fields. +Floating point output only applies to +.Vt timespec +fields (the +.Cm a , m , +and +.Cm c +fields). +.Pp +The special output specifier +.Cm S +may be used to indicate that the output, if +applicable, should be in string format. +May be used in combination with: +.Bl -tag -width indent +.It Cm amc +Display date in +.Xr strftime 3 +format. +.It Cm dr +Display actual device name. +.It Cm gu +Display group or user name. +.It Cm p +Display the mode of +.Ar file +as in +.Nm ls Fl lTd . +.It Cm N +Displays the name of +.Ar file . +.It Cm T +Displays the type of +.Ar file . +.It Cm Y +Insert a +.Dq Li " -\*[Gt] " +into the output. +Note that the default output format +for +.Cm Y +is a string, but if specified explicitly, these four characters are +prepended. +.El +.It Ar sub +An optional sub field specifier (high, middle, low). +Only applies to +the +.Cm p , d , r , +and +.Cm T +output formats. +It can be one of the following: +.Bl -tag -width indent +.It Cm H +.Dq High +\[em] +specifies the major number for devices from +.Cm r +or +.Cm d , +the +.Dq user +bits for permissions from the string form of +.Cm p , +the file +.Dq type +bits from the numeric forms of +.Cm p , +and the long output form of +.Cm T . +.It Cm L +.Dq Low +\[em] +specifies the minor number for devices from +.Cm r +or +.Cm d , +the +.Dq other +bits for permissions from the string form of +.Cm p , +the +.Dq user , +.Dq group , +and +.Dq other +bits from the numeric forms of +.Cm p , +and the +.Nm ls Fl F +style output character for file type when used with +.Cm T +(the use of +.Cm L +for this is optional). +.It Cm M +.Dq Middle +\[em] +specifies the +.Dq group +bits for permissions from the +string output form of +.Cm p , +or the +.Dq suid , +.Dq sgid , +and +.Dq sticky +bits for the numeric forms of +.Cm p . +.El +.It Ar datum +A required field specifier, being one of the following: +.Bl -tag -width indent +.It Cm d +Device upon which +.Ar file +resides. +.It Cm i +.Ar file Ns 's +inode number. +.It Cm p +File type and permissions. +.It Cm l +Number of hard links to +.Ar file . +.It Cm u , g +User ID and group ID of +.Ar file Ns 's +owner. +.It Cm r +Device number for character and block device special files. +.It Cm a , m , c , B +The time +.Ar file +was last accessed or modified, of when the inode was last changed, or +the birth time of the inode. +.It Cm z +The size of +.Ar file +in bytes. +.It Cm b +Number of blocks allocated for +.Ar file . +.It Cm k +Optimal file system I/O operation block size. +.It Cm f +User defined flags for +.Ar file . +.It Cm v +Inode generation number. +.El +.Pp +The following four field specifiers are not drawn directly from the +data in +.Vt "struct stat" , +but are: +.Bl -tag -width indent +.It Cm N +The name of the file. +.It Cm T +The file type, either as in +.Nm ls Fl F +or in a more descriptive form if the +.Ar sub +field specifier +.Cm H +is given. +.It Cm Y +The target of a symbolic link. +.It Cm Z +Expands to +.Dq major,minor +from the +.Va rdev +field for character or block +special devices and gives size output for all others. +.El +.El +.Pp +Only the +.Cm % +and the field specifier are required. +Most field specifiers default to +.Cm U +as an output form, with the +exception of +.Cm p +which defaults to +.Cm O , +.Cm a , m , +and +.Cm c +which default to +.Cm D , +and +.Cm Y , T , +and +.Cm N +which default to +.Cm S . +.Sh EXIT STATUS +.Ex -std stat readlink +.Sh EXAMPLES +Given a symbolic link +.Pa foo +that points from +.Pa /tmp/foo +to +.Pa / , +you would use +.Nm stat +as follows: +.Bd -literal -offset indent +\*[Gt] stat -F /tmp/foo +lrwxrwxrwx 1 jschauma cs 1 Apr 24 16:37:28 2002 /tmp/foo@ -\*[Gt] / + +\*[Gt] stat -LF /tmp/foo +drwxr-xr-x 16 root wheel 512 Apr 19 10:57:54 2002 /tmp/foo/ +.Ed +.Pp +To initialize some shell variables, you could use the +.Fl s +flag as follows: +.Bd -literal -offset indent +\*[Gt] csh +% eval set `stat -s .cshrc` +% echo $st_size $st_mtimespec +1148 1015432481 + +\*[Gt] sh +$ eval $(stat -s .profile) +$ echo $st_size $st_mtimespec +1148 1015432481 +.Ed +.Pp +In order to get a list of the kind of files including files pointed to if the +file is a symbolic link, you could use the following format: +.Bd -literal -offset indent +$ stat -f "%N: %HT%SY" /tmp/* +/tmp/bar: Symbolic Link -\*[Gt] /tmp/foo +/tmp/output25568: Regular File +/tmp/blah: Directory +/tmp/foo: Symbolic Link -\*[Gt] / +.Ed +.Pp +In order to get a list of the devices, their types and the major and minor +device numbers, formatted with tabs and linebreaks, you could use the +following format: +.Bd -literal -offset indent +stat -f "Name: %N%n%tType: %HT%n%tMajor: %Hr%n%tMinor: %Lr%n%n" /dev/* +[...] +Name: /dev/wt8 + Type: Block Device + Major: 3 + Minor: 8 + +Name: /dev/zero + Type: Character Device + Major: 2 + Minor: 12 +.Ed +.Pp +In order to determine the permissions set on a file separately, you could use +the following format: +.Bd -literal -offset indent +\*[Gt] stat -f "%Sp -\*[Gt] owner=%SHp group=%SMp other=%SLp" . +drwxr-xr-x -\*[Gt] owner=rwx group=r-x other=r-x +.Ed +.Pp +In order to determine the three files that have been modified most recently, +you could use the following format: +.Bd -literal -offset indent +\*[Gt] stat -f "%m%t%Sm %N" /tmp/* | sort -rn | head -3 | cut -f2- +Apr 25 11:47:00 2002 /tmp/blah +Apr 25 10:36:34 2002 /tmp/bar +Apr 24 16:47:35 2002 /tmp/foo +.Ed +.Sh SEE ALSO +.Xr file 1 , +.Xr ls 1 , +.Xr lstat 2 , +.Xr readlink 2 , +.Xr stat 2 , +.Xr printf 3 , +.Xr strftime 3 +.Sh HISTORY +The +.Nm stat +utility appeared in +.Nx 1.6 +and +.Fx 4.10 . +.Sh AUTHORS +.An -nosplit +The +.Nm stat +utility was written by +.An Andrew Brown +.Aq atatat@NetBSD.org . +This man page was written by +.An Jan Schaumann +.Aq jschauma@NetBSD.org . diff --git a/files/Sources/files/stat/stat.c b/files/Sources/files/stat/stat.c new file mode 100644 index 00000000..66a4a1a0 --- /dev/null +++ b/files/Sources/files/stat/stat.c @@ -0,0 +1,1008 @@ +/* + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Andrew Brown. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#if 0 +#ifndef lint +__RCSID("$NetBSD: stat.c,v 1.13 2003/07/25 03:21:17 atatat Exp $"); +#endif +#endif + +__FBSDID("$FreeBSD: src/usr.bin/stat/stat.c,v 1.6 2003/10/06 01:55:17 dougb Exp $"); + +#if HAVE_CONFIG_H +#include "config.h" +#else /* HAVE_CONFIG_H */ +#define HAVE_STRUCT_STAT_ST_FLAGS 1 +#define HAVE_STRUCT_STAT_ST_GEN 1 +#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1 +#define HAVE_STRUCT_STAT_ST_MTIMENSEC 1 +#define HAVE_STRUCT_STAT_ST_ATIM 0 +#define HAVE_DEVNAME 1 +#endif /* HAVE_CONFIG_H */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ios_error.h" + +#if HAVE_STRUCT_STAT_ST_FLAGS +#define DEF_F "%#Xf " +#define RAW_F "%f " +#define SHELL_F " st_flags=%f" +#else /* HAVE_STRUCT_STAT_ST_FLAGS */ +#define DEF_F +#define RAW_F +#define SHELL_F +#endif /* HAVE_STRUCT_STAT_ST_FLAGS */ + +#if HAVE_STRUCT_STAT_ST_BIRTHTIME +#define DEF_B "\"%SB\" " +#define RAW_B "%B " +#define SHELL_B "st_birthtime=%B " +#else /* HAVE_STRUCT_STAT_ST_BIRTHTIME */ +#define DEF_B +#define RAW_B +#define SHELL_B +#endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */ + +#if HAVE_STRUCT_STAT_ST_ATIM +#define st_atimespec st_atim +#define st_ctimespec st_ctim +#define st_mtimespec st_mtim +#endif /* HAVE_STRUCT_STAT_ST_ATIM */ + +#define DEF_FORMAT \ + "%d %i %Sp %l %Su %Sg %r %z \"%Sa\" \"%Sm\" \"%Sc\" " DEF_B \ + "%k %b " DEF_F "%N" +#define RAW_FORMAT "%d %i %#p %l %u %g %r %z %a %m %c " RAW_B \ + "%k %b " RAW_F "%N" +#define LS_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%SY" +#define LSF_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%T%SY" +#define SHELL_FORMAT \ + "st_dev=%d st_ino=%i st_mode=%#p st_nlink=%l " \ + "st_uid=%u st_gid=%g st_rdev=%r st_size=%z " \ + "st_atime=%a st_mtime=%m st_ctime=%c " SHELL_B \ + "st_blksize=%k st_blocks=%b" SHELL_F +#define LINUX_FORMAT \ + " File: \"%N\"%n" \ + " Size: %-11z FileType: %HT%n" \ + " Mode: (%04OA/%.10Sp) Uid: (%5u/%8Su) Gid: (%5g/%8Sg)%n" \ + "Device: %Hd,%Ld Inode: %i Links: %l%n" \ + "Access: %Sa%n" \ + "Modify: %Sm%n" \ + "Change: %Sc" + +#define TIME_FORMAT "%b %e %T %Y" + +#define FLAG_POUND 0x01 +#define FLAG_SPACE 0x02 +#define FLAG_PLUS 0x04 +#define FLAG_ZERO 0x08 +#define FLAG_MINUS 0x10 + +/* + * These format characters must all be unique, except the magic one. + */ +#define FMT_MAGIC '%' +#define FMT_DOT '.' + +#define SIMPLE_NEWLINE 'n' +#define SIMPLE_TAB 't' +#define SIMPLE_PERCENT '%' +#define SIMPLE_NUMBER '@' + +#define FMT_POUND '#' +#define FMT_SPACE ' ' +#define FMT_PLUS '+' +#define FMT_ZERO '0' +#define FMT_MINUS '-' + +#define FMT_DECIMAL 'D' +#define FMT_OCTAL 'O' +#define FMT_UNSIGNED 'U' +#define FMT_HEX 'X' +#define FMT_FLOAT 'F' +#define FMT_STRING 'S' + +#define FMTF_DECIMAL 0x01 +#define FMTF_OCTAL 0x02 +#define FMTF_UNSIGNED 0x04 +#define FMTF_HEX 0x08 +#define FMTF_FLOAT 0x10 +#define FMTF_STRING 0x20 + +#define HIGH_PIECE 'H' +#define MIDDLE_PIECE 'M' +#define LOW_PIECE 'L' + +#define SHOW_st_dev 'd' +#define SHOW_st_ino 'i' +#define SHOW_st_mode 'p' +#define SHOW_st_mode2 'A' +#define SHOW_st_nlink 'l' +#define SHOW_st_uid 'u' +#define SHOW_st_gid 'g' +#define SHOW_st_rdev 'r' +#define SHOW_st_atime 'a' +#define SHOW_st_mtime 'm' +#define SHOW_st_ctime 'c' +#define SHOW_st_btime 'B' +#define SHOW_st_size 'z' +#define SHOW_st_blocks 'b' +#define SHOW_st_blksize 'k' +#define SHOW_st_flags 'f' +#define SHOW_st_gen 'v' +#define SHOW_symlink 'Y' +#define SHOW_filetype 'T' +#define SHOW_filename 'N' +#define SHOW_sizerdev 'Z' + +static void usage(const char *); +static void output(const struct stat *, const char *, + const char *, int, int, int); +static int format1(const struct stat *, /* stat info */ + const char *, /* the file name */ + const char *, int, /* the format string itself */ + char *, size_t, /* a place to put the output */ + int, int, int, int, /* the parsed format */ + int, int); + +static char *timefmt; +static int linkfail; + +#define addchar(s, c, nl) \ + do { \ + (void)fputc((c), (s)); \ + (*nl) = ((c) == '\n'); \ + } while (0/*CONSTCOND*/) + +static char* progname; + +int +stat_main(int argc, char *argv[]) +{ + struct stat st; + int ch, rc, errs, am_readlink; + int lsF, fmtchar, usestat, fn, nonl, quiet; + char *statfmt, *options, *synopsis; + + am_readlink = 0; + lsF = 0; + fmtchar = '\0'; + usestat = 0; + nonl = 0; + quiet = 0; + linkfail = 0; + statfmt = NULL; + timefmt = NULL; + optind = 1; opterr = 1; optreset = 1; + progname = argv[0]; + + // if (strcmp(getprogname(), "readlink") == 0) { + if (strcmp(progname, "readlink") == 0) { + am_readlink = 1; + options = "n"; + synopsis = "[-n] [file ...]"; + statfmt = "%Y"; + fmtchar = 'f'; + quiet = 1; + } else { + options = "f:FlLnqrst:x"; + synopsis = "[-FlLnqrsx] [-f format] [-t timefmt] [file ...]"; + } + + while ((ch = getopt(argc, argv, options)) != -1) + switch (ch) { + case 'F': + lsF = 1; + break; + case 'L': + usestat = 1; + break; + case 'n': + nonl = 1; + break; + case 'q': + quiet = 1; + break; + case 'f': + statfmt = optarg; + /* FALLTHROUGH */ + case 'l': + case 'r': + case 's': + case 'x': + if (fmtchar != 0) { + errx(1, "can't use format '%c' with '%c'", + fmtchar, ch); + } + fmtchar = ch; + break; + case 't': + timefmt = optarg; + break; + default: + usage(synopsis); + } + + argc -= optind; + argv += optind; + fn = 1; + + if (fmtchar == '\0') { + if (lsF) + fmtchar = 'l'; + else { + fmtchar = 'f'; + statfmt = DEF_FORMAT; + } + } + + if (lsF && fmtchar != 'l') { + errx(1, "can't use format '%c' with -F", fmtchar); + } + + switch (fmtchar) { + case 'f': + /* statfmt already set */ + break; + case 'l': + statfmt = lsF ? LSF_FORMAT : LS_FORMAT; + break; + case 'r': + statfmt = RAW_FORMAT; + break; + case 's': + statfmt = SHELL_FORMAT; + break; + case 'x': + statfmt = LINUX_FORMAT; + if (timefmt == NULL) + timefmt = "%c"; + break; + default: + usage(synopsis); + /*NOTREACHED*/ + } + + if (timefmt == NULL) + timefmt = TIME_FORMAT; + + errs = 0; + do { + if (argc == 0) + rc = fstat(fileno(thread_stdin), &st); + else if (usestat) + rc = stat(argv[0], &st); + else + rc = lstat(argv[0], &st); + + if (rc == -1) { + errs = 1; + linkfail = 1; + if (!quiet) + warn("%s: stat", + argc == 0 ? "(stdin)" : argv[0]); + } + else + output(&st, argv[0], statfmt, fn, nonl, quiet); + + argv++; + argc--; + fn++; + } while (argc > 0); + + return (am_readlink ? linkfail : errs); +} + +void +usage(const char *synopsis) +{ + // (void)fprintf(thread_stderr, "usage: %s %s\n", getprogname(), synopsis); + (void)fprintf(thread_stderr, "usage: %s %s\n", progname, synopsis); + exit(1); +} + +/* + * Parses a format string. + */ +void +output(const struct stat *st, const char *file, + const char *statfmt, int fn, int nonl, int quiet) +{ + int flags, size, prec, ofmt, hilo, what; + char buf[PATH_MAX]; + const char *subfmt; + int nl, t, i; + + nl = 1; + while (*statfmt != '\0') { + + /* + * Non-format characters go straight out. + */ + if (*statfmt != FMT_MAGIC) { + addchar(thread_stdout, *statfmt, &nl); + statfmt++; + continue; + } + + /* + * The current format "substring" starts here, + * and then we skip the magic. + */ + subfmt = statfmt; + statfmt++; + + /* + * Some simple one-character "formats". + */ + switch (*statfmt) { + case SIMPLE_NEWLINE: + addchar(thread_stdout, '\n', &nl); + statfmt++; + continue; + case SIMPLE_TAB: + addchar(thread_stdout, '\t', &nl); + statfmt++; + continue; + case SIMPLE_PERCENT: + addchar(thread_stdout, '%', &nl); + statfmt++; + continue; + case SIMPLE_NUMBER: { + char num[12], *p; + + snprintf(num, sizeof(num), "%d", fn); + for (p = &num[0]; *p; p++) + addchar(thread_stdout, *p, &nl); + statfmt++; + continue; + } + } + + /* + * This must be an actual format string. Format strings are + * similar to printf(3) formats up to a point, and are of + * the form: + * + * % required start of format + * [-# +0] opt. format characters + * size opt. field width + * . opt. decimal separator, followed by + * prec opt. precision + * fmt opt. output specifier (string, numeric, etc.) + * sub opt. sub field specifier (high, middle, low) + * datum required field specifier (size, mode, etc) + * + * Only the % and the datum selector are required. All data + * have reasonable default output forms. The "sub" specifier + * only applies to certain data (mode, dev, rdev, filetype). + * The symlink output defaults to STRING, yet will only emit + * the leading " -> " if STRING is explicitly specified. The + * sizerdev datum will generate rdev output for character or + * block devices, and size output for all others. + */ + flags = 0; + do { + if (*statfmt == FMT_POUND) + flags |= FLAG_POUND; + else if (*statfmt == FMT_SPACE) + flags |= FLAG_SPACE; + else if (*statfmt == FMT_PLUS) + flags |= FLAG_PLUS; + else if (*statfmt == FMT_ZERO) + flags |= FLAG_ZERO; + else if (*statfmt == FMT_MINUS) + flags |= FLAG_MINUS; + else + break; + statfmt++; + } while (1/*CONSTCOND*/); + + size = -1; + if (isdigit((unsigned)*statfmt)) { + size = 0; + while (isdigit((unsigned)*statfmt)) { + size = (size * 10) + (*statfmt - '0'); + statfmt++; + if (size < 0) + goto badfmt; + } + } + + prec = -1; + if (*statfmt == FMT_DOT) { + statfmt++; + + prec = 0; + while (isdigit((unsigned)*statfmt)) { + prec = (prec * 10) + (*statfmt - '0'); + statfmt++; + if (prec < 0) + goto badfmt; + } + } + +#define fmtcase(x, y) case (y): (x) = (y); statfmt++; break +#define fmtcasef(x, y, z) case (y): (x) = (z); statfmt++; break + switch (*statfmt) { + fmtcasef(ofmt, FMT_DECIMAL, FMTF_DECIMAL); + fmtcasef(ofmt, FMT_OCTAL, FMTF_OCTAL); + fmtcasef(ofmt, FMT_UNSIGNED, FMTF_UNSIGNED); + fmtcasef(ofmt, FMT_HEX, FMTF_HEX); + fmtcasef(ofmt, FMT_FLOAT, FMTF_FLOAT); + fmtcasef(ofmt, FMT_STRING, FMTF_STRING); + default: + ofmt = 0; + break; + } + + switch (*statfmt) { + fmtcase(hilo, HIGH_PIECE); + fmtcase(hilo, MIDDLE_PIECE); + fmtcase(hilo, LOW_PIECE); + default: + hilo = 0; + break; + } + + switch (*statfmt) { + fmtcase(what, SHOW_st_dev); + fmtcase(what, SHOW_st_ino); + fmtcase(what, SHOW_st_mode); + fmtcase(what, SHOW_st_mode2); + fmtcase(what, SHOW_st_nlink); + fmtcase(what, SHOW_st_uid); + fmtcase(what, SHOW_st_gid); + fmtcase(what, SHOW_st_rdev); + fmtcase(what, SHOW_st_atime); + fmtcase(what, SHOW_st_mtime); + fmtcase(what, SHOW_st_ctime); + fmtcase(what, SHOW_st_btime); + fmtcase(what, SHOW_st_size); + fmtcase(what, SHOW_st_blocks); + fmtcase(what, SHOW_st_blksize); + fmtcase(what, SHOW_st_flags); + fmtcase(what, SHOW_st_gen); + fmtcase(what, SHOW_symlink); + fmtcase(what, SHOW_filetype); + fmtcase(what, SHOW_filename); + fmtcase(what, SHOW_sizerdev); + default: + goto badfmt; + } +#undef fmtcasef +#undef fmtcase + + t = format1(st, + file, + subfmt, statfmt - subfmt, + buf, sizeof(buf), + flags, size, prec, ofmt, hilo, what); + + for (i = 0; i < t && i < sizeof(buf); i++) + addchar(thread_stdout, buf[i], &nl); + + continue; + + badfmt: + errx(1, "%.*s: bad format", + (int)(statfmt - subfmt + 1), subfmt); + } + + if (!nl && !nonl) + (void)fputc('\n', thread_stdout); + (void)fflush(thread_stdout); +} + +/* + * Arranges output according to a single parsed format substring. + */ +int +format1(const struct stat *st, + const char *file, + const char *fmt, int flen, + char *buf, size_t blen, + int flags, int size, int prec, int ofmt, + int hilo, int what) +{ + u_int64_t data; + char *sdata, lfmt[24], tmp[20]; + char smode[12], sid[12], path[PATH_MAX + 4]; + struct passwd *pw; + struct group *gr; + const struct timespec *tsp; + struct timespec ts = {0,0}; + struct tm *tm; + int l, small, formats; + + tsp = NULL; +// formats = 0; +// small = 0; + + /* + * First, pick out the data and tweak it based on hilo or + * specified output format (symlink output only). + */ + switch (what) { + case SHOW_st_dev: + case SHOW_st_rdev: + small = (sizeof(st->st_dev) == 4); + data = (what == SHOW_st_dev) ? st->st_dev : st->st_rdev; +#if HAVE_DEVNAME + sdata = (what == SHOW_st_dev) ? + devname(st->st_dev, S_IFBLK) : + devname(st->st_rdev, + S_ISCHR(st->st_mode) ? S_IFCHR : + S_ISBLK(st->st_mode) ? S_IFBLK : + 0U); + if (sdata == NULL) + sdata = "???"; +#endif /* HAVE_DEVNAME */ + if (hilo == HIGH_PIECE) { + data = major(data); + hilo = 0; + } + else if (hilo == LOW_PIECE) { + data = minor((unsigned)data); + hilo = 0; + } + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | +#if HAVE_DEVNAME + FMTF_STRING; +#else /* HAVE_DEVNAME */ + 0; +#endif /* HAVE_DEVNAME */ + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; + case SHOW_st_ino: + small = (sizeof(st->st_ino) == 4); + data = st->st_ino; + sdata = NULL; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; + case SHOW_st_mode: + case SHOW_st_mode2: + small = (sizeof(st->st_mode) == 4); + data = st->st_mode; + strmode(st->st_mode, smode); + sdata = smode; + l = strlen(sdata); + if (sdata[l - 1] == ' ') + sdata[--l] = '\0'; + if (what == SHOW_st_mode2) + data &= 07777; + if (hilo == HIGH_PIECE) { + data >>= 12; + sdata += 1; + sdata[3] = '\0'; + hilo = 0; + } + else if (hilo == MIDDLE_PIECE) { + data = (data >> 9) & 07; + sdata += 4; + sdata[3] = '\0'; + hilo = 0; + } + else if (hilo == LOW_PIECE) { + data &= 0777; + sdata += 7; + sdata[3] = '\0'; + hilo = 0; + } + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | + FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_OCTAL; + break; + case SHOW_st_nlink: + small = (sizeof(st->st_dev) == 4); + data = st->st_nlink; + sdata = NULL; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; + case SHOW_st_uid: + small = (sizeof(st->st_uid) == 4); + data = st->st_uid; + if ((pw = getpwuid(st->st_uid)) != NULL) + sdata = pw->pw_name; + else { + snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_uid); + sdata = sid; + } + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | + FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; + case SHOW_st_gid: + small = (sizeof(st->st_gid) == 4); + data = st->st_gid; + if ((gr = getgrgid(st->st_gid)) != NULL) + sdata = gr->gr_name; + else { + snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_gid); + sdata = sid; + } + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | + FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; + case SHOW_st_atime: + tsp = &st->st_atimespec; + /* FALLTHROUGH */ + case SHOW_st_mtime: + if (tsp == NULL) + tsp = &st->st_mtimespec; + /* FALLTHROUGH */ + case SHOW_st_ctime: + if (tsp == NULL) + tsp = &st->st_ctimespec; + /* FALLTHROUGH */ +#if HAVE_STRUCT_STAT_ST_BIRTHTIME + case SHOW_st_btime: + if (tsp == NULL) + tsp = &st->st_birthtimespec; +#endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */ + ts = *tsp; /* copy so we can muck with it */ + small = (sizeof(ts.tv_sec) == 4); + data = ts.tv_sec; + tm = localtime(&ts.tv_sec); + if (tm == NULL) { + ts.tv_sec = 0; + tm = localtime(&ts.tv_sec); + } + (void)strftime(path, sizeof(path), timefmt, tm); + sdata = path; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | + FMTF_FLOAT | FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_DECIMAL; + break; + case SHOW_st_size: + small = (sizeof(st->st_size) == 4); + data = st->st_size; + sdata = NULL; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; + case SHOW_st_blocks: + small = (sizeof(st->st_blocks) == 4); + data = st->st_blocks; + sdata = NULL; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; + case SHOW_st_blksize: + small = (sizeof(st->st_blksize) == 4); + data = st->st_blksize; + sdata = NULL; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; +#if HAVE_STRUCT_STAT_ST_FLAGS + case SHOW_st_flags: + small = (sizeof(st->st_flags) == 4); + data = st->st_flags; + sdata = NULL; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; +#endif /* HAVE_STRUCT_STAT_ST_FLAGS */ +#if HAVE_STRUCT_STAT_ST_GEN + case SHOW_st_gen: + small = (sizeof(st->st_gen) == 4); + data = st->st_gen; + sdata = NULL; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; +#endif /* HAVE_STRUCT_STAT_ST_GEN */ + case SHOW_symlink: + small = 0; + data = 0; + if (S_ISLNK(st->st_mode)) { + snprintf(path, sizeof(path), " -> "); + l = readlink(file, path + 4, sizeof(path) - 4 - 1); + if (l == -1) { + linkfail = 1; + l = 0; + path[0] = '\0'; + } + path[l + 4] = '\0'; + sdata = path + (ofmt == FMTF_STRING ? 0 : 4); + } + else { + linkfail = 1; + sdata = ""; + } + formats = FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_STRING; + break; + case SHOW_filetype: + small = 0; + data = 0; + sdata = smode; + sdata[0] = '\0'; + if (hilo == 0 || hilo == LOW_PIECE) { + switch (st->st_mode & S_IFMT) { + case S_IFIFO: (void)strcat(sdata, "|"); break; + case S_IFDIR: (void)strcat(sdata, "/"); break; + case S_IFREG: + if (st->st_mode & + (S_IXUSR | S_IXGRP | S_IXOTH)) + (void)strcat(sdata, "*"); + break; + case S_IFLNK: (void)strcat(sdata, "@"); break; + case S_IFSOCK: (void)strcat(sdata, "="); break; +#ifdef S_IFWHT + case S_IFWHT: (void)strcat(sdata, "%"); break; +#endif /* S_IFWHT */ +#ifdef S_IFDOOR + case S_IFDOOR: (void)strcat(sdata, ">"); break; +#endif /* S_IFDOOR */ + } + hilo = 0; + } + else if (hilo == HIGH_PIECE) { + switch (st->st_mode & S_IFMT) { + case S_IFIFO: sdata = "Fifo File"; break; + case S_IFCHR: sdata = "Character Device"; break; + case S_IFDIR: sdata = "Directory"; break; + case S_IFBLK: sdata = "Block Device"; break; + case S_IFREG: sdata = "Regular File"; break; + case S_IFLNK: sdata = "Symbolic Link"; break; + case S_IFSOCK: sdata = "Socket"; break; +#ifdef S_IFWHT + case S_IFWHT: sdata = "Whiteout File"; break; +#endif /* S_IFWHT */ +#ifdef S_IFDOOR + case S_IFDOOR: sdata = "Door"; break; +#endif /* S_IFDOOR */ + default: sdata = "???"; break; + } + hilo = 0; + } + formats = FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_STRING; + break; + case SHOW_filename: + small = 0; + data = 0; + if (file == NULL) + (void)strncpy(path, "(stdin)", sizeof(path)); + else + (void)strncpy(path, file, sizeof(path)); + sdata = path; + formats = FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_STRING; + break; + case SHOW_sizerdev: + if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { + char majdev[20], mindev[20]; + int l1, l2; + + l1 = format1(st, + file, + fmt, flen, + majdev, sizeof(majdev), + flags, size, prec, + ofmt, HIGH_PIECE, SHOW_st_rdev); + l2 = format1(st, + file, + fmt, flen, + mindev, sizeof(mindev), + flags, size, prec, + ofmt, LOW_PIECE, SHOW_st_rdev); + return (snprintf(buf, blen, "%.*s,%.*s", + l1, majdev, l2, mindev)); + } + else { + return (format1(st, + file, + fmt, flen, + buf, blen, + flags, size, prec, + ofmt, 0, SHOW_st_size)); + } + /*NOTREACHED*/ + default: + errx(1, "%.*s: bad format", (int)flen, fmt); + } + + /* + * If a subdatum was specified but not supported, or an output + * format was selected that is not supported, that's an error. + */ + if (hilo != 0 || (ofmt & formats) == 0) { + errx(1, "%.*s: bad format", (int)flen, fmt); + } + + /* + * Assemble the format string for passing to printf(3). + */ + lfmt[0] = '\0'; + (void)strcat(lfmt, "%"); + if (flags & FLAG_POUND) + (void)strcat(lfmt, "#"); + if (flags & FLAG_SPACE) + (void)strcat(lfmt, " "); + if (flags & FLAG_PLUS) + (void)strcat(lfmt, "+"); + if (flags & FLAG_MINUS) + (void)strcat(lfmt, "-"); + if (flags & FLAG_ZERO) + (void)strcat(lfmt, "0"); + + /* + * Only the timespecs support the FLOAT output format, and that + * requires work that differs from the other formats. + */ + if (ofmt == FMTF_FLOAT) { + /* + * Nothing after the decimal point, so just print seconds. + */ + if (prec == 0) { + if (size != -1) { + (void)snprintf(tmp, sizeof(tmp), "%d", size); + (void)strcat(lfmt, tmp); + } + (void)strcat(lfmt, "d"); + return (snprintf(buf, blen, lfmt, ts.tv_sec)); + } + + /* + * Unspecified precision gets all the precision we have: + * 9 digits. + */ + if (prec == -1) + prec = 9; + + /* + * Adjust the size for the decimal point and the digits + * that will follow. + */ + size -= prec + 1; + + /* + * Any leftover size that's legitimate will be used. + */ + if (size > 0) { + (void)snprintf(tmp, sizeof(tmp), "%d", size); + (void)strcat(lfmt, tmp); + } + (void)strcat(lfmt, "d"); + + /* + * The stuff after the decimal point always needs zero + * filling. + */ + (void)strcat(lfmt, ".%0"); + + /* + * We can "print" at most nine digits of precision. The + * rest we will pad on at the end. + */ + (void)snprintf(tmp, sizeof(tmp), "%dd", prec > 9 ? 9 : prec); + (void)strcat(lfmt, tmp); + + /* + * For precision of less that nine digits, trim off the + * less significant figures. + */ + for (; prec < 9; prec++) + ts.tv_nsec /= 10; + + /* + * Use the format, and then tack on any zeroes that + * might be required to make up the requested precision. + */ + l = snprintf(buf, blen, lfmt, ts.tv_sec, ts.tv_nsec); + for (; prec > 9 && l < blen; prec--, l++) + (void)strcat(buf, "0"); + return (l); + } + + /* + * Add on size and precision, if specified, to the format. + */ + if (size != -1) { + (void)snprintf(tmp, sizeof(tmp), "%d", size); + (void)strcat(lfmt, tmp); + } + if (prec != -1) { + (void)snprintf(tmp, sizeof(tmp), ".%d", prec); + (void)strcat(lfmt, tmp); + } + + /* + * String output uses the temporary sdata. + */ + if (ofmt == FMTF_STRING) { + if (sdata == NULL) { + errx(1, "%.*s: bad format", (int)flen, fmt); + } + (void)strcat(lfmt, "s"); + return (snprintf(buf, blen, lfmt, sdata)); + } + + /* + * Ensure that sign extension does not cause bad looking output + * for some forms. + */ + if (small && ofmt != FMTF_DECIMAL) + data = (u_int32_t)data; + + /* + * The four "numeric" output forms. + */ + (void)strcat(lfmt, "ll"); + switch (ofmt) { + case FMTF_DECIMAL: (void)strcat(lfmt, "d"); break; + case FMTF_OCTAL: (void)strcat(lfmt, "o"); break; + case FMTF_UNSIGNED: (void)strcat(lfmt, "u"); break; + case FMTF_HEX: (void)strcat(lfmt, "x"); break; + } + + return (snprintf(buf, blen, lfmt, data)); +} diff --git a/files/Sources/files/termcap.h b/files/Sources/files/termcap.h new file mode 100644 index 00000000..351c50b0 --- /dev/null +++ b/files/Sources/files/termcap.h @@ -0,0 +1,75 @@ +/**************************************************************************** + * Copyright (c) 1998,2000 Free Software Foundation, Inc. * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, distribute with modifications, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included * + * in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * + * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name(s) of the above copyright * + * holders shall not be used in advertising or otherwise to promote the * + * sale, use or other dealings in this Software without prior written * + * authorization. * + ****************************************************************************/ + +/**************************************************************************** + * Author: Zeyd M. Ben-Halim 1992,1995 * + * and: Eric S. Raymond * + ****************************************************************************/ + +/* $Id: termcap.h.in,v 1.16 2001/03/24 21:53:27 tom Exp $ */ + +#ifndef NCURSES_TERMCAP_H_incl +#define NCURSES_TERMCAP_H_incl 1 + +#undef NCURSES_VERSION +#define NCURSES_VERSION "5.7" + +#include "ncurses_dll.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include + +#undef NCURSES_CONST +#define NCURSES_CONST /*nothing*/ + +#undef NCURSES_OSPEED +#define NCURSES_OSPEED short + +extern NCURSES_EXPORT_VAR(char) PC; +extern NCURSES_EXPORT_VAR(char *) UP; +extern NCURSES_EXPORT_VAR(char *) BC; +extern NCURSES_EXPORT_VAR(NCURSES_OSPEED) ospeed; + +#if !defined(NCURSES_TERM_H_incl) +extern NCURSES_EXPORT(char *) tgetstr (NCURSES_CONST char *, char **); +extern NCURSES_EXPORT(char *) tgoto (const char *, int, int); +extern NCURSES_EXPORT(int) tgetent (char *, const char *); +extern NCURSES_EXPORT(int) tgetflag (NCURSES_CONST char *); +extern NCURSES_EXPORT(int) tgetnum (NCURSES_CONST char *); +extern NCURSES_EXPORT(int) tputs (const char *, int, int (*)(int)); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* NCURSES_TERMCAP_H_incl */ diff --git a/files/Sources/files/touch/touch.1 b/files/Sources/files/touch/touch.1 new file mode 100644 index 00000000..6e6678b6 --- /dev/null +++ b/files/Sources/files/touch/touch.1 @@ -0,0 +1,221 @@ +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 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. +.\" +.\" @(#)touch.1 8.3 (Berkeley) 4/28/95 +.\" $FreeBSD: src/usr.bin/touch/touch.1,v 1.16 2007/04/10 07:24:47 grog Exp $ +.\" +.Dd April 28, 1995 +.Dt TOUCH 1 +.Os +.Sh NAME +.Nm touch +.Nd change file access and modification times +.Sh SYNOPSIS +.Nm +.Op Fl A Ar [-][[hh]mm]SS +.Op Fl acfhm +.Op Fl r Ar file +.Op Fl t Ar [[CC]YY]MMDDhhmm[.SS] +.Ar +.Sh DESCRIPTION +The +.Nm +utility sets the modification and access times of files. +If any file does not exist, it is created with default permissions. +.Pp +By default, +.Nm +changes both modification and access times. The +.Fl a +and +.Fl m +flags may be used to select the access time or the modification time +individually. +Selecting both is equivalent to the default. +By default, the timestamps are set to the current time. +The +.Fl t +flag explicitly specifies a different time, and the +.Fl r +flag specifies to set the times those of the specified file. +The +.Fl A +flag adjusts the values by a specified amount. +.Pp +The following options are available: +.Bl -tag -width Ds +.It Fl A +Adjust the access and modification time stamps for the file by the +specified value. +This flag is intended for use in modifying files with incorrectly set +time stamps. +.Pp +The argument is of the form +.Dq [-][[hh]mm]SS +where each pair of letters represents the following: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It Ar - +Make the adjustment negative: the new time stamp is set to be before +the old one. +.It Ar hh +The number of hours, from 00 to 99. +.It Ar mm +The number of minutes, from 00 to 59. +.It Ar SS +The number of seconds, from 00 to 59. +.El +.Pp +The +.Fl A +flag implies the +.Fl c +flag: if any file specified does not exist, it will be silently ignored. +.It Fl a +Change the access time of the file. +The modification time of the file is not changed unless the +.Fl m +flag is also specified. +.It Fl c +Do not create the file if it does not exist. +The +.Nm +utility does not treat this as an error. +No error messages are displayed and the exit value is not affected. +.It Fl f +Attempt to force the update, even if the file permissions do not +currently permit it. +.It Fl h +If the file is a symbolic link, change the times of the link +itself rather than the file that the link points to. +Note that +.Fl h +implies +.Fl c +and thus will not create any new files. +.It Fl m +Change the modification time of the file. +The access time of the file is not changed unless the +.Fl a +flag is also specified. +.It Fl r +Use the access and modifications times from the specified file +instead of the current time of day. +.It Fl t +Change the access and modification times to the specified time instead +of the current time of day. +The argument is of the form +.Dq [[CC]YY]MMDDhhmm[.SS] +where each pair of letters represents the following: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It Ar CC +The first two digits of the year (the century). +.It Ar YY +The second two digits of the year. +If +.Dq YY +is specified, but +.Dq CC +is not, a value for +.Dq YY +between 69 and 99 results in a +.Dq CC +value of 19. +Otherwise, a +.Dq CC +value of 20 is used. +.It Ar MM +The month of the year, from 01 to 12. +.It Ar DD +the day of the month, from 01 to 31. +.It Ar hh +The hour of the day, from 00 to 23. +.It Ar mm +The minute of the hour, from 00 to 59. +.It Ar SS +The second of the minute, from 00 to 61. +.El +.Pp +If the +.Dq CC +and +.Dq YY +letter pairs are not specified, the values default to the current +year. +If the +.Dq SS +letter pair is not specified, the value defaults to 0. +.El +.Sh EXIT STATUS +.Ex -std +.Sh COMPATIBILITY +The obsolescent form of +.Nm , +where a time format is specified as the first argument, is supported. +When no +.Fl r +or +.Fl t +option is specified, there are at least two arguments, and the first +argument is a string of digits either eight or ten characters in length, +the first argument is interpreted as a time specification of the form +.Dq MMDDhhmm[YY] . +.Pp +The +.Dq MM , +.Dq DD , +.Dq hh +and +.Dq mm +letter pairs are treated as their counterparts specified to the +.Fl t +option. +If the +.Dq YY +letter pair is in the range 39 to 99, the year is set to 1939 to 1999, +otherwise, the year is set in the 21st century. +.Sh SEE ALSO +.Xr utimes 2 +.Sh STANDARDS +The +.Nm +utility is expected to be a superset of the +.St -p1003.2 +specification. +.Sh HISTORY +A +.Nm +utility appeared in +.At v7 . diff --git a/files/Sources/files/touch/touch.c b/files/Sources/files/touch/touch.c new file mode 100644 index 00000000..3a0a0b67 --- /dev/null +++ b/files/Sources/files/touch/touch.c @@ -0,0 +1,437 @@ +/* + * Copyright (c) 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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: src/usr.bin/touch/touch.c,v 1.25 2010/03/28 13:16:08 ed Exp $"); + +#ifndef lint +__used static const char copyright[] = +"@(#) Copyright (c) 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif + +#ifndef lint +__used static const char sccsid[] = "@(#)touch.c 8.1 (Berkeley) 6/6/93"; +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ios_error.h" + +static int rw(char *, struct stat *, int); +static void stime_arg1(char *, struct timeval *); +static void stime_arg2(char *, int, struct timeval *); +static void stime_file(char *, struct timeval *); +static int timeoffset(char *); +static void usage(char *); + +int +touch_main(int argc, char *argv[]) +{ + struct stat sb; + struct timeval tv[2]; + int (*stat_f)(const char *, struct stat *); + int (*utimes_f)(const char *, const struct timeval *); + int Aflag, aflag, cflag, fflag, mflag, ch, fd, len, rval, timeset; + char *p; + char *myname; + + myname = basename(argv[0]); + Aflag = aflag = cflag = fflag = mflag = timeset = 0; + optind = 1; opterr = 1; optreset = 1; + stat_f = stat; + utimes_f = utimes; + if (gettimeofday(&tv[0], NULL)) { + err(1, "gettimeofday"); + } + + while ((ch = getopt(argc, argv, "A:acfhmr:t:")) != -1) + switch(ch) { + case 'A': + Aflag = timeoffset(optarg); + break; + case 'a': + aflag = 1; + break; + case 'c': + cflag = 1; + break; + case 'f': + fflag = 1; + break; + case 'h': + cflag = 1; + stat_f = lstat; + utimes_f = lutimes; + break; + case 'm': + mflag = 1; + break; + case 'r': + timeset = 1; + stime_file(optarg, tv); + break; + case 't': + timeset = 1; + stime_arg1(optarg, tv); + break; + case '?': + default: + usage(myname); + } + argc -= optind; + argv += optind; + + if (aflag == 0 && mflag == 0) + aflag = mflag = 1; + + if (timeset) { + if (Aflag) { + /* + * We're setting the time to an offset from a specified + * time. God knows why, but it means that we can set + * that time once and for all here. + */ + if (aflag) + tv[0].tv_sec += Aflag; + if (mflag) + tv[1].tv_sec += Aflag; + Aflag = 0; /* done our job */ + } + } else { + /* + * If no -r or -t flag, at least two operands, the first of + * which is an 8 or 10 digit number, use the obsolete time + * specification, otherwise use the current time. + */ + if (argc > 1) { + strtol(argv[0], &p, 10); + len = p - argv[0]; + if (*p == '\0' && (len == 8 || len == 10)) { + timeset = 1; + stime_arg2(*argv++, len == 10, tv); + } + } + /* Both times default to the same. */ + tv[1] = tv[0]; + } + + if (*argv == NULL) + usage(myname); + + if (Aflag) + cflag = 1; + + for (rval = 0; *argv; ++argv) { + /* See if the file exists. */ + if (stat_f(*argv, &sb) != 0) { + if (errno != ENOENT) { + rval = 1; + warn("%s", *argv); + continue; + } + if (!cflag) { + /* Create the file. */ + fd = open(*argv, + O_WRONLY | O_CREAT, DEFFILEMODE); + if (fd == -1 || fstat(fd, &sb) || close(fd)) { + rval = 1; + warn("%s", *argv); + continue; + } + + /* If using the current time, we're done. */ + if (!timeset) + continue; + } else + continue; + } + + if (!aflag) + TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atimespec); + if (!mflag) + TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec); + + /* + * We're adjusting the times based on the file times, not a + * specified time (that gets handled above). + */ + if (Aflag) { + if (aflag) { + TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atimespec); + tv[0].tv_sec += Aflag; + } + if (mflag) { + TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec); + tv[1].tv_sec += Aflag; + } + } + + /* Try utimes(2). */ + if (!utimes_f(*argv, tv)) + continue; + + /* If the user specified a time, nothing else we can do. */ + if (timeset || Aflag) { + rval = 1; + warn("%s", *argv); + continue; + } + + /* + * System V and POSIX 1003.1 require that a NULL argument + * set the access/modification times to the current time. + * The permission checks are different, too, in that the + * ability to write the file is sufficient. Take a shot. + */ + if (!utimes_f(*argv, NULL)) + continue; + + /* Try reading/writing. */ + if (!S_ISLNK(sb.st_mode) && !S_ISDIR(sb.st_mode)) { + if (rw(*argv, &sb, fflag)) + rval = 1; + } else { + rval = 1; + warn("%s", *argv); + } + } + exit(rval); +} + +#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; + +void +stime_arg1(char *arg, struct timeval *tvp) +{ + time_t now; + struct tm *t; + int yearset; + char *p; + /* Start with the current time. */ + now = tvp[0].tv_sec; + if ((t = localtime(&now)) == NULL) { + err(1, "localtime"); + } + /* [[CC]YY]MMDDhhmm[.SS] */ + if ((p = strchr(arg, '.')) == NULL) + t->tm_sec = 0; /* Seconds defaults to 0. */ + else { + if (strlen(p + 1) != 2) + goto terr; + *p++ = '\0'; + t->tm_sec = ATOI2(p); + } + + yearset = 0; + switch(strlen(arg)) { + case 12: /* CCYYMMDDhhmm */ + t->tm_year = ATOI2(arg); + t->tm_year *= 100; + yearset = 1; + /* FALLTHROUGH */ + case 10: /* YYMMDDhhmm */ + if (yearset) { + yearset = ATOI2(arg); + t->tm_year += yearset; + } else { + yearset = ATOI2(arg); + if (yearset < 69) + t->tm_year = yearset + 2000; + else + t->tm_year = yearset + 1900; + } + t->tm_year -= 1900; /* Convert to UNIX time. */ + /* FALLTHROUGH */ + case 8: /* MMDDhhmm */ + t->tm_mon = ATOI2(arg); + --t->tm_mon; /* Convert from 01-12 to 00-11 */ + t->tm_mday = ATOI2(arg); + t->tm_hour = ATOI2(arg); + t->tm_min = ATOI2(arg); + break; + default: + goto terr; + } + + t->tm_isdst = -1; /* Figure out DST. */ + tvp[0].tv_sec = tvp[1].tv_sec = mktime(t); + if (tvp[0].tv_sec == -1) + terr: errx(1, + "touch: out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]\n"); + + tvp[0].tv_usec = tvp[1].tv_usec = 0; +} + +void +stime_arg2(char *arg, int year, struct timeval *tvp) +{ + time_t now; + struct tm *t; + /* Start with the current time. */ + now = tvp[0].tv_sec; + if ((t = localtime(&now)) == NULL) { + err(1, "localtime"); + } + + t->tm_mon = ATOI2(arg); /* MMDDhhmm[yy] */ + --t->tm_mon; /* Convert from 01-12 to 00-11 */ + t->tm_mday = ATOI2(arg); + t->tm_hour = ATOI2(arg); + t->tm_min = ATOI2(arg); + if (year) { + t->tm_year = ATOI2(arg); + if (t->tm_year < 39) /* support 2000-2038 not 1902-1969 */ + t->tm_year += 100; + } + + t->tm_isdst = -1; /* Figure out DST. */ + tvp[0].tv_sec = tvp[1].tv_sec = mktime(t); + if (tvp[0].tv_sec == -1) { + errx(1, + "touch: out of range or illegal time specification: MMDDhhmm[yy]\n"); + } + + tvp[0].tv_usec = tvp[1].tv_usec = 0; +} + +/* Calculate a time offset in seconds, given an arg of the format [-]HHMMSS. */ +int +timeoffset(char *arg) +{ + int offset; + int isneg; + + offset = 0; + isneg = *arg == '-'; + if (isneg) + arg++; + switch (strlen(arg)) { + default: /* invalid */ + errx(1, "Invalid offset spec, must be [-][[HH]MM]SS"); + + case 6: /* HHMMSS */ + offset = ATOI2(arg); + /* FALLTHROUGH */ + case 4: /* MMSS */ + offset = offset * 60 + ATOI2(arg); + /* FALLTHROUGH */ + case 2: /* SS */ + offset = offset * 60 + ATOI2(arg); + } + if (isneg) + return (-offset); + else + return (offset); +} + +void +stime_file(char *fname, struct timeval *tvp) +{ + struct stat sb; + + if (stat(fname, &sb)) { + err(1, "%s", fname); + } + TIMESPEC_TO_TIMEVAL(tvp, &sb.st_atimespec); + TIMESPEC_TO_TIMEVAL(tvp + 1, &sb.st_mtimespec); +} + +int +rw(char *fname, struct stat *sbp, int force) +{ + int fd, needed_chmod, rval; + u_char byte; + + /* Try regular files. */ + if (!S_ISREG(sbp->st_mode)) { + warnx("%s: %s", fname, strerror(EFTYPE)); + return (1); + } + + needed_chmod = rval = 0; + if ((fd = open(fname, O_RDWR, 0)) == -1) { + if (!force || chmod(fname, DEFFILEMODE)) + goto err; + if ((fd = open(fname, O_RDWR, 0)) == -1) + goto err; + needed_chmod = 1; + } + + if (sbp->st_size != 0) { + if (read(fd, &byte, sizeof(byte)) != sizeof(byte)) + goto err; + if (lseek(fd, (off_t)0, SEEK_SET) == -1) + goto err; + if (write(fd, &byte, sizeof(byte)) != sizeof(byte)) + goto err; + } else { + if (write(fd, &byte, sizeof(byte)) != sizeof(byte)) { +err: rval = 1; + warn("%s", fname); + } else if (ftruncate(fd, (off_t)0)) { + rval = 1; + warn("%s: file modified", fname); + } + } + + if (close(fd) && rval != 1) { + rval = 1; + warn("%s", fname); + } + if (needed_chmod && chmod(fname, sbp->st_mode) && rval != 1) { + rval = 1; + warn("%s: permissions modified", fname); + } + return (rval); +} + +void +usage(char *myname) +{ + fprintf(thread_stderr, "usage:\n" "%s [-A [-][[hh]mm]SS] [-acfhm] [-r file] " + "[-t [[CC]YY]MMDDhhmm[.SS]] file ...\n", myname); + exit(1); +} diff --git a/files/Sources/ios_error.h b/files/Sources/ios_error.h new file mode 100644 index 00000000..a4490144 --- /dev/null +++ b/files/Sources/ios_error.h @@ -0,0 +1,103 @@ +// +// error.h +// shell_cmds_ios +// +// Created by Nicolas Holzschuch on 16/06/2017. +// Copyright © 2017 Nicolas Holzschuch. All rights reserved. +// + +#ifndef ios_error_h +#define ios_error_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +/* #define errx compileError +#define err compileError +#define warn compileError +#define warnx compileError +#ifndef printf +#define printf(...) fprintf (thread_stdout, ##__VA_ARGS__) +#endif */ + +#define putchar(a) fputc(a, thread_stdout) +#define getchar() fgetc(thread_stdin) +// these functions are defined differently in C++. The #define approach breaks things. +#ifndef __cplusplus + #define getwchar() fgetwc(thread_stdin) + #define putwchar(a) fputwc(a, thread_stdout) + // iswprint depends on the given locale, and setlocale() fails on iOS: + #define iswprint(a) 1 + #define write ios_write + #define fwrite ios_fwrite + #define puts ios_puts + #define fputs ios_fputs + #define fputc ios_fputc + #define putw ios_putw + #define fflush ios_fflush +#endif + +// Thread-local input and output streams +extern __thread FILE* thread_stdin; +extern __thread FILE* thread_stdout; +extern __thread FILE* thread_stderr; + +#define exit ios_exit +#define abort() ios_exit(1) +#define _exit ios_exit +#define kill ios_killpid +#define _kill ios_killpid +#define killpg ios_killpid +#define popen ios_popen +#define pclose fclose +#define system ios_system +#define execv ios_execv +#define execvp ios_execv +#define execve ios_execve +#define dup2 ios_dup2 +#define getenv ios_getenv + +extern int ios_executable(const char* cmd); // is this command part of the "shell" commands? +extern int ios_system(const char* inputCmd); // execute this command (executable file or builtin command) +extern FILE *ios_popen(const char *command, const char *type); // Execute this command and pipe the result +extern int ios_kill(void); // kill the current running command +extern int ios_killpid(pid_t pid, int sig); // kill the current running command + +extern void ios_exit(int errorCode) __dead2; // set error code and exits from the thread. +extern int ios_execv(const char *path, char* const argv[]); +extern int ios_execve(const char *path, char* const argv[], char** envlist); +extern int ios_dup2(int fd1, int fd2); +extern char * ios_getenv(const char *name); + +extern int ios_isatty(int fd); +extern pthread_t ios_getLastThreadId(void); // deprecated +extern pthread_t ios_getThreadId(pid_t pid); +extern void ios_storeThreadId(pthread_t thread); +extern void ios_releaseThread(pthread_t thread); +extern void ios_releaseThreadId(pid_t pid); +extern pid_t ios_currentPid(void); +extern int ios_getCommandStatus(void); +extern const char* ios_progname(void); +extern pid_t ios_fork(void); +extern void ios_waitpid(pid_t pid); +extern void ios_signal(int signal); + +extern ssize_t ios_write(int fildes, const void *buf, size_t nbyte); +extern size_t ios_fwrite(const void *ptr, size_t size, size_t nitems, FILE *stream); +extern int ios_puts(const char *s); +extern int ios_fputs(const char* s, FILE *stream); +extern int ios_fputc(int c, FILE *stream); +extern int ios_putw(int w, FILE *stream); +extern int ios_fflush(FILE *stream); +extern int ios_gettty(void); + +#ifdef __cplusplus +} +#endif +#endif /* ios_error_h */ diff --git a/files/Tests/LinuxMain.swift b/files/Tests/LinuxMain.swift new file mode 100644 index 00000000..bd2ac655 --- /dev/null +++ b/files/Tests/LinuxMain.swift @@ -0,0 +1,7 @@ +import XCTest + +import filesTests + +var tests = [XCTestCaseEntry]() +tests += filesTests.allTests() +XCTMain(tests) diff --git a/files/Tests/filesTests/XCTestManifests.swift b/files/Tests/filesTests/XCTestManifests.swift new file mode 100644 index 00000000..99559edf --- /dev/null +++ b/files/Tests/filesTests/XCTestManifests.swift @@ -0,0 +1,9 @@ +import XCTest + +#if !canImport(ObjectiveC) +public func allTests() -> [XCTestCaseEntry] { + return [ + testCase(filesTests.allTests), + ] +} +#endif diff --git a/files/Tests/filesTests/filesTests.swift b/files/Tests/filesTests/filesTests.swift new file mode 100644 index 00000000..bd72401e --- /dev/null +++ b/files/Tests/filesTests/filesTests.swift @@ -0,0 +1,15 @@ +import XCTest +@testable import files + +final class filesTests: XCTestCase { + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct + // results. + XCTAssertEqual(files().text, "Hello, World!") + } + + static var allTests = [ + ("testExample", testExample), + ] +} diff --git a/files/files.h b/files/files.h deleted file mode 100644 index e5695993..00000000 --- a/files/files.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// files.h -// files -// -// Created by Nicolas Holzschuch on 25/03/2018. -// Copyright © 2018 Nicolas Holzschuch. All rights reserved. -// - -#import - -//! Project version number for files. -FOUNDATION_EXPORT double filesVersionNumber; - -//! Project version string for files. -FOUNDATION_EXPORT const unsigned char filesVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import - - diff --git a/ios_system/Info.plist b/ios_system/Info.plist deleted file mode 100644 index 1007fd9d..00000000 --- a/ios_system/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSPrincipalClass - - - diff --git a/ios_system/Package.swift b/ios_system/Package.swift new file mode 100644 index 00000000..9d0bc2bb --- /dev/null +++ b/ios_system/Package.swift @@ -0,0 +1,42 @@ +// swift-tools-version:5.2 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "ios_system", + // thread-local variables are only available with iOS 11+. This setting is required for compilation. + platforms: [.iOS(.v11)], + products: [ + // Products define the executables and libraries produced by a package, and make them visible to other packages. + .library( + name: "ios_system", + type: .dynamic, + targets: ["ios_system"]), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0"), + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages which this package depends on. + // binaryTargets will need the next version of Xcode. It will be great for libssh2/openssl. + .target( + name: "ios_system", + dependencies: [], + cSettings: [.headerSearchPath("..")]), + ] +) + +// cSettings or cxxSettings. Each have 3 options: +// cSettings: [ .define("ONLY_ACTIVE_ARCH", to: "NO"), .headerSearchPath(".."), .unsafeFlags([""])]), +// Files and the others will have to be a separate package. +// .library( +// name: "files", +// targets: ["files"]), +// .target( +// name: "files", +// dependencies: ["ios_system"], +// exclude: ["gzip/unbzip2.c", "gzip/zuncompress.c", "gzip/unpack.c"], +// cSettings: [.define("COLORLS", to: "1"), .headerSearchPath(".."), .unsafeFlags(["-Wshorten-64-to-32 // -Wno-ambiguous-macro -Wunused-const-variable -Wincompatible-pointer-types-discards-qualifiers"])]), diff --git a/ios_system/Sources/ios_error.h b/ios_system/Sources/ios_error.h new file mode 100644 index 00000000..a4490144 --- /dev/null +++ b/ios_system/Sources/ios_error.h @@ -0,0 +1,103 @@ +// +// error.h +// shell_cmds_ios +// +// Created by Nicolas Holzschuch on 16/06/2017. +// Copyright © 2017 Nicolas Holzschuch. All rights reserved. +// + +#ifndef ios_error_h +#define ios_error_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +/* #define errx compileError +#define err compileError +#define warn compileError +#define warnx compileError +#ifndef printf +#define printf(...) fprintf (thread_stdout, ##__VA_ARGS__) +#endif */ + +#define putchar(a) fputc(a, thread_stdout) +#define getchar() fgetc(thread_stdin) +// these functions are defined differently in C++. The #define approach breaks things. +#ifndef __cplusplus + #define getwchar() fgetwc(thread_stdin) + #define putwchar(a) fputwc(a, thread_stdout) + // iswprint depends on the given locale, and setlocale() fails on iOS: + #define iswprint(a) 1 + #define write ios_write + #define fwrite ios_fwrite + #define puts ios_puts + #define fputs ios_fputs + #define fputc ios_fputc + #define putw ios_putw + #define fflush ios_fflush +#endif + +// Thread-local input and output streams +extern __thread FILE* thread_stdin; +extern __thread FILE* thread_stdout; +extern __thread FILE* thread_stderr; + +#define exit ios_exit +#define abort() ios_exit(1) +#define _exit ios_exit +#define kill ios_killpid +#define _kill ios_killpid +#define killpg ios_killpid +#define popen ios_popen +#define pclose fclose +#define system ios_system +#define execv ios_execv +#define execvp ios_execv +#define execve ios_execve +#define dup2 ios_dup2 +#define getenv ios_getenv + +extern int ios_executable(const char* cmd); // is this command part of the "shell" commands? +extern int ios_system(const char* inputCmd); // execute this command (executable file or builtin command) +extern FILE *ios_popen(const char *command, const char *type); // Execute this command and pipe the result +extern int ios_kill(void); // kill the current running command +extern int ios_killpid(pid_t pid, int sig); // kill the current running command + +extern void ios_exit(int errorCode) __dead2; // set error code and exits from the thread. +extern int ios_execv(const char *path, char* const argv[]); +extern int ios_execve(const char *path, char* const argv[], char** envlist); +extern int ios_dup2(int fd1, int fd2); +extern char * ios_getenv(const char *name); + +extern int ios_isatty(int fd); +extern pthread_t ios_getLastThreadId(void); // deprecated +extern pthread_t ios_getThreadId(pid_t pid); +extern void ios_storeThreadId(pthread_t thread); +extern void ios_releaseThread(pthread_t thread); +extern void ios_releaseThreadId(pid_t pid); +extern pid_t ios_currentPid(void); +extern int ios_getCommandStatus(void); +extern const char* ios_progname(void); +extern pid_t ios_fork(void); +extern void ios_waitpid(pid_t pid); +extern void ios_signal(int signal); + +extern ssize_t ios_write(int fildes, const void *buf, size_t nbyte); +extern size_t ios_fwrite(const void *ptr, size_t size, size_t nitems, FILE *stream); +extern int ios_puts(const char *s); +extern int ios_fputs(const char* s, FILE *stream); +extern int ios_fputc(int c, FILE *stream); +extern int ios_putw(int w, FILE *stream); +extern int ios_fflush(FILE *stream); +extern int ios_gettty(void); + +#ifdef __cplusplus +} +#endif +#endif /* ios_error_h */ diff --git a/ios_system/Sources/ios_system/getopt.c b/ios_system/Sources/ios_system/getopt.c new file mode 100644 index 00000000..a279e061 --- /dev/null +++ b/ios_system/Sources/ios_system/getopt.c @@ -0,0 +1,157 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996,2008 Oracle. All rights reserved. + */ +/* + * Copyright (c) 1987, 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. + * 3. 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. + * + * $Id: getopt.c,v 12.8 2008/03/12 17:50:25 mbrey Exp $ + */ + +// #include "db_config.h" + +// #include "db_int.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ios_error.h" + +__thread int __db_getopt_reset; /* global reset for VxWorks. */ + +// Not __thread unless I rename them. +int opterr = 1, /* if error message should be printed */ +optind = 1, /* index into parent argv vector */ +optopt, /* character checked for validity */ +optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ + +#undef BADCH +#define BADCH (int)'?' +#undef BADARG +#define BADARG (int)':' +#undef EMSG +#define EMSG "" + +/* + * getopt -- + * Parse argc/argv argument vector. + * + * PUBLIC: #ifndef HAVE_GETOPT + * PUBLIC: int getopt __P((int, char * const *, const char *)); + * PUBLIC: #endif + */ +int +getopt(nargc, nargv, ostr) +int nargc; +char * const *nargv; +const char *ostr; +{ + static char *progname; + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + + /* + * VxWorks needs to be able to repeatedly call getopt from multiple + * programs within its global name space. And so does iOS. + */ + if (__db_getopt_reset) { + __db_getopt_reset = 0; + + opterr = optind = 1; + optopt = optreset = 0; + optarg = NULL; + progname = NULL; + place = EMSG; + } + if (!progname) { + // if ((progname = __db_rpath(*nargv)) == NULL) + progname = *nargv; + // else + // ++progname; + } + + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc || *(place = nargv[optind]) != '-') { + place = EMSG; + return (EOF); + } + if (place[1] && *++place == '-') { /* found "--" */ + ++optind; + place = EMSG; + return (EOF); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' || + !(oli = strchr(ostr, optopt))) { + /* + * if the user didn't specify '-' as an option, + * assume it means EOF. + */ + if (optopt == (int)'-') + return (EOF); + if (!*place) + ++optind; + if (opterr && *ostr != ':') + (void)fprintf(thread_stderr, + "%s: illegal option -- %c\n", progname, optopt); + return (BADCH); + } + if (*++oli != ':') { /* don't need argument */ + optarg = NULL; + if (!*place) + ++optind; + } + else { /* need an argument */ + if (*place) /* no white space */ + optarg = place; + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; + if (*ostr == ':') + return (BADARG); + if (opterr) + (void)fprintf(thread_stderr, + "%s: option requires an argument -- %c\n", + progname, optopt); + return (BADCH); + } + else /* white space */ + optarg = nargv[optind]; + place = EMSG; + ++optind; + } + return (optopt); /* dump back option letter */ +} diff --git a/ios_system/Sources/ios_system/getopt_long.c b/ios_system/Sources/ios_system/getopt_long.c new file mode 100644 index 00000000..c42edb36 --- /dev/null +++ b/ios_system/Sources/ios_system/getopt_long.c @@ -0,0 +1,625 @@ +/* $OpenBSD: getopt_long.c,v 1.21 2006/09/22 17:22:05 millert Exp $ */ +/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ + +/* + * Copyright (c) 2002 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#if 0 +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: getopt_long.c,v 1.16 2004/02/04 18:17:25 millert Exp $"; +#endif /* LIBC_SCCS and not lint */ +#endif +#include +__FBSDID("$FreeBSD: src/lib/libc/stdlib/getopt_long.c,v 1.15 2006/09/23 14:48:31 ache Exp $"); + +#include +#include +#include +#include +#include + +#define GNU_COMPATIBLE /* Be more compatible, configure's use us! */ + +#if 0 /* we prefer to keep our getopt(3) */ +#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ +#endif + +#ifdef REPLACE_GETOPT +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ +#endif + +#define PRINT_ERROR ((opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#define EMSG "" + +#ifdef GNU_COMPATIBLE +#define NO_PREFIX (-1) +#define D_PREFIX 0 +#define DD_PREFIX 1 +#define W_PREFIX 2 +#endif + +static int getopt_internal(int, char * const *, const char *, + const struct option *, int *, int); +static int parse_long_options(char * const *, const char *, + const struct option *, int *, int, int); +static int gcd(int, int); +static void permute_args(int, int, int, char * const *); + +static char *place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static const char recargchar[] = "option requires an argument -- %c"; +static const char illoptchar[] = "illegal option -- %c"; /* From P1003.2 */ +#ifdef GNU_COMPATIBLE +static int dash_prefix = NO_PREFIX; +static const char gnuoptchar[] = "invalid option -- %c"; + +static const char recargstring[] = "option `%s%s' requires an argument"; +static const char ambig[] = "option `%s%.*s' is ambiguous"; +static const char noarg[] = "option `%s%.*s' doesn't allow an argument"; +static const char illoptstring[] = "unrecognized option `%s%s'"; +#else +static const char recargstring[] = "option requires an argument -- %s"; +static const char ambig[] = "ambiguous option -- %.*s"; +static const char noarg[] = "option doesn't take an argument -- %.*s"; +static const char illoptstring[] = "unknown option -- %s"; +#endif + +/* + * Compute the greatest common divisor of a and b. + */ +static int +gcd(int a, int b) +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return (b); +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(int panonopt_start, int panonopt_end, int opt_end, + char * const *nargv) +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } +} + +/* + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -1 if short_too is set and the option does not match long_options. + */ +static int +parse_long_options(char * const *nargv, const char *options, + const struct option *long_options, int *idx, int short_too, int flags) +{ + char *current_argv, *has_equal; +#ifdef GNU_COMPATIBLE + char *current_dash; +#endif + size_t current_argv_len; + int i, match, exact_match, second_partial_match; + + current_argv = place; +#ifdef GNU_COMPATIBLE + switch (dash_prefix) { + case D_PREFIX: + current_dash = "-"; + break; + case DD_PREFIX: + current_dash = "--"; + break; + case W_PREFIX: + current_dash = "-W "; + break; + default: + current_dash = ""; + break; + } +#endif + match = -1; + exact_match = 0; + second_partial_match = 0; + + optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + exact_match = 1; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* first partial match */ + match = i; + else if ((flags & FLAG_LONGONLY) || + long_options[i].has_arg != + long_options[match].has_arg || + long_options[i].flag != long_options[match].flag || + long_options[i].val != long_options[match].val) + second_partial_match = 1; + } + if (!exact_match && second_partial_match) { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + warnx(ambig, +#ifdef GNU_COMPATIBLE + current_dash, +#endif + (int)current_argv_len, + current_argv); + optopt = 0; + return (BADCH); + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + warnx(noarg, +#ifdef GNU_COMPATIBLE + current_dash, +#endif + (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; +#ifdef GNU_COMPATIBLE + return (BADCH); +#else + return (BADARG); +#endif + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + warnx(recargstring, +#ifdef GNU_COMPATIBLE + current_dash, +#endif + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } else { /* unknown option */ + if (short_too) { + --optind; + return (-1); + } + if (PRINT_ERROR) + warnx(illoptstring, +#ifdef GNU_COMPATIBLE + current_dash, +#endif + current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + return (0); + } else + return (long_options[match].val); +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + */ +static int +getopt_internal(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx, int flags) +{ + char *oli; /* option letter list index */ + int optchar, short_too; + int posixly_correct; /* no static, can be changed on the fly */ + + if (options == NULL) + return (-1); + + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + */ + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); +#ifdef GNU_COMPATIBLE + if (*options == '-') + flags |= FLAG_ALLARGS; + else if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; +#else + if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + else if (*options == '-') + flags |= FLAG_ALLARGS; +#endif + if (*options == '+' || *options == '-') + options++; + + /* + * XXX Some GNU programs (like cvs) set optind to 0 instead of + * XXX using optreset. Work around this braindamage. + */ + if (optind == 0) + optind = optreset = 1; + + optarg = NULL; + if (optreset) + nonopt_start = nonopt_end = -1; +start: + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + if (*(place = nargv[optind]) != '-' || +#ifdef GNU_COMPATIBLE + place[1] == '\0') { +#else + (place[1] == '\0' && strchr(options, '-') == NULL)) { +#endif + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[optind] && + (*place == '-' || (flags & FLAG_LONGONLY))) { + short_too = 0; +#ifdef GNU_COMPATIBLE + dash_prefix = D_PREFIX; +#endif + if (*place == '-') { + place++; /* --foo long option */ +#ifdef GNU_COMPATIBLE + dash_prefix = DD_PREFIX; +#endif + } else if (*place != ':' && strchr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, + idx, short_too, flags); + if (optchar != -1) { + place = EMSG; + return (optchar); + } + } + + if ((optchar = (int)*place++) == (int)':' || + (optchar == (int)'-' && *place != '\0') || + (oli = strchr(options, optchar)) == NULL) { + /* + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). + */ + if (optchar == (int)'-' && *place == '\0') + return (-1); + if (!*place) + ++optind; +#ifdef GNU_COMPATIBLE + if (PRINT_ERROR) + warnx(posixly_correct ? illoptchar : gnuoptchar, + optchar); +#else + if (PRINT_ERROR) + warnx(illoptchar, optchar); +#endif + optopt = optchar; + return (BADCH); + } + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else /* white space */ + place = nargv[optind]; +#ifdef GNU_COMPATIBLE + dash_prefix = W_PREFIX; +#endif + optchar = parse_long_options(nargv, options, long_options, + idx, 0, flags); + place = EMSG; + return (optchar); + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++optind; + } else { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = place; + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else + optarg = nargv[optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return (optchar); +} + +#ifdef REPLACE_GETOPT +/* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the BSD getopt] + */ +int +getopt(int nargc, char * const *nargv, const char *options) +{ + + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); +} +#endif /* REPLACE_GETOPT */ + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); +} + +/* + * getopt_long_only -- + * Parse argc/argv argument vector. + */ +int +getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE|FLAG_LONGONLY)); +} diff --git a/ios_system/ios_system.h b/ios_system/Sources/ios_system/include/ios_system.h similarity index 100% rename from ios_system/ios_system.h rename to ios_system/Sources/ios_system/include/ios_system.h diff --git a/ios_system/Sources/ios_system/ios_system.m b/ios_system/Sources/ios_system/ios_system.m new file mode 100644 index 00000000..338bc834 --- /dev/null +++ b/ios_system/Sources/ios_system/ios_system.m @@ -0,0 +1,2016 @@ +// +// ios_system.m +// +// Created by Nicolas Holzschuch on 17/11/2017. +// Copyright © 2017 N. Holzschuch. All rights reserved. +// + +#import +#include "ios_system.h" + +// ios_system(cmd): Executes the command in "cmd". The goal is to be a drop-in replacement for system(), as much as possible. +// We assume cmd is the command. If vim has prepared '/bin/sh -c "(command -arguments) < inputfile > outputfile", +// it is easier to remove the "/bin/sh -c" part before calling ios_system than inside ios_system. +// See example in (iVim) os_unix.c +// +// ios_executable(cmd): returns true if the command is one of the commands defined in ios_system, and can be executed. +// This is because mch_can_exe (called by executable()) checks for the existence of binaries with the same name in the +// path. Our commands don't exist in the path. +// +// ios_popen(cmd, type): returns a FILE*, executes cmd, and thread_output into input of cmd (if type=="w") or +// the reverse (if type == "r"). + +#include +#include +#include // for basename() +#include // for dlopen()/dlsym()/dlclose() +#include // for wildcard expansion +// Sideloading: when you compile yourself, as opposed to uploading on the app store +// If true, all commands are enabled + debug messages if dylib not found. +// If false, you get a smaller set, but compliance with AppStore rules. +// *Must* be false in the main branch releases. +// Commands that can be enabled only if sideLoading: ctags, readtags, chgrp, chown, chmod, df, id, w. +bool sideLoading = false; +// Should the main thread be joined (which means it takes priority over other tasks)? +// Default value is true, which makes sense for shell-like applications. +// Should be set to false if significant user interaction is carried by the app and +// the app takes responsibility for waiting for the command to terminate. +bool joinMainThread = true; +// Include file for getrlimit/setrlimit: +#include +static struct rlimit limitFilesOpen; + + +extern __thread int __db_getopt_reset; +__thread FILE* thread_stdin; +__thread FILE* thread_stdout; +__thread FILE* thread_stderr; +__thread void* thread_context; + +// Parameters for each session. We can have multiple sessions running in parallel. +typedef struct _sessionParameters { + bool isMainThread; // are we on the first command? + char currentDir[MAXPATHLEN]; + char previousDirectory[MAXPATHLEN]; + char localMiniRoot[MAXPATHLEN]; + pthread_t current_command_root_thread; // thread ID of first command + pthread_t lastThreadId; // thread ID of last command + FILE* stdin; + FILE* stdout; + FILE* stderr; + FILE* tty; + void* context; + int global_errno; + char commandName[NAME_MAX]; + char columns[4]; + char lines[4]; +} sessionParameters; + +static void initSessionParameters(sessionParameters* sp) { + NSFileManager *fileManager = [[NSFileManager alloc] init]; + sp->isMainThread = TRUE; + sp->current_command_root_thread = 0; + sp->lastThreadId = 0; + NSString* currentDirectory = [fileManager currentDirectoryPath]; + strcpy(sp->currentDir, [currentDirectory UTF8String]); + strcpy(sp->previousDirectory, [currentDirectory UTF8String]); + sp->localMiniRoot[0] = 0; + sp->global_errno = 0; + sp->stdin = stdin; + sp->stdout = stdout; + sp->stderr = stderr; + sp->tty = stdin; + sp->context = nil; + sp->commandName[0] = 0; + strcpy(sp->columns, "80"); + strcpy(sp->lines, "80"); +} + +static NSMutableDictionary* sessionList; + +// pointer to sessionParameters. thread-local variable so the entire system is thread-safe. +// The sessionParameters pointer is shared by all threads in the same session. +static __thread sessionParameters* currentSession; +// Python3 multiple interpreters: +// limit to 6 = 1 kernel, 4 notebooks, one extra. +// App Store limit is 200 MB +static const int MaxPythonInterpreters = 6; // const so we can allocate an array +int numPythonInterpreters = MaxPythonInterpreters; // Apps can overwrite this +static bool PythonIsRunning[MaxPythonInterpreters]; +static int currentPythonInterpreter = 0; +// pointers for sh sessions: +char* sh_session = "sh_session"; + +// replace system-provided exit() by our own: +// Make sure we call pthread_cancel(currentSession->current_command_root_thread) +// as much as possible, because ios_exit can be called from a signal handler now. +void ios_exit(int n) { + if (currentSession != NULL) { + currentSession->global_errno = n; + } + pthread_exit(NULL); +} + +void ios_signal(int signal) { + // Signals the threads of the current session: + if (currentSession != NULL) { + if (currentSession->current_command_root_thread != NULL) { + pthread_kill(currentSession->current_command_root_thread, signal); + } + if (currentSession->lastThreadId != NULL) { + pthread_kill(currentSession->lastThreadId, signal); + } + } +} + +#undef getenv +void ios_setWindowSize(int width, int height, const void* sessionId) { + // You can set the window size for a session that is not currently running (e.g. because "sh_session" is running). + // So we set it without calling ios_switchSession: + sessionParameters* resizedSession; + + id sessionKey = @((NSUInteger)sessionId); + if (sessionList == nil) { + return; + } + resizedSession = (sessionParameters*)[[sessionList objectForKey: sessionKey] pointerValue]; + if (resizedSession == nil) { + return; + } + + sprintf(resizedSession->columns, "%d", width); + sprintf(resizedSession->lines, "%d",height); +} + +char * ios_getenv(const char *name) { + // intercept calls to getenv("COLUMNS") / getenv("LINES") + if (strcmp(name, "COLUMNS") == 0) { + return currentSession->columns; + } + if (strcmp(name, "LINES") == 0) { + return currentSession->lines; + } + if (strcmp(name, "ROWS") == 0) { + return currentSession->lines; + } + return getenv(name); +} + + +int ios_getCommandStatus() { + if (currentSession != NULL) return currentSession->global_errno; + else return 0; +} + +extern const char* ios_progname(void) { + if (currentSession != NULL) return currentSession->commandName; + else return getprogname(); +} + + +typedef struct _functionParameters { + int argc; + char** argv; + char** argv_ref; + int (*function)(int ac, char** av); + FILE *stdin, *stdout, *stderr; + void* context; + void* dlHandle; + bool isPipeOut; + bool isPipeErr; + sessionParameters* session; +} functionParameters; + +static void cleanup_function(void* parameters) { + // This function is called when pthread_exit() or ios_kill() is called + functionParameters *p = (functionParameters *) parameters; + char* commandName = p->argv[0]; + // commandName can be NULL, I don't know exactly under which circumstances. + if (commandName != NULL) { + if ((strcmp(commandName, "less") == 0) || (strcmp(commandName, "more") == 0)) { + if ((currentSession->current_command_root_thread != 0) && (currentSession->current_command_root_thread != pthread_self())) { + // Something started less. Was that a pipe? + NSLog(@"less quitting. currentsession->stdin: %d thread_stdin: %d\n", fileno(currentSession->stdin), fileno(thread_stdin)); + if (fileno(currentSession->stdin) != fileno(thread_stdin)) { + // Command was "root_command | sthg | less". We need to kill the root command: + pthread_kill(currentSession->current_command_root_thread, SIGINT); + // ... and keep listening for input, otherwise the command piping into us will get blocked: + char c; + // Stop when rootthread is 0? will it work? + // fflush(thread_stdin)? + while (((c = fgetc(thread_stdin)) != EOF) && (currentSession->current_command_root_thread != 0)) { + // NSLog(@"Root thread: %x received: %c\n", currentSession->current_command_root_thread, c); + } + } + } + } + } + if ((!joinMainThread) && p->isPipeOut) { + if (currentSession->current_command_root_thread != 0) { + if (currentSession->current_command_root_thread != pthread_self()) { + // NSLog(@"Thread %x is waiting for root_thread of currentSession: %x \n", pthread_self(), currentSession->current_command_root_thread); + while (currentSession->current_command_root_thread != 0) { } + } else { + // NSLog(@"Terminating root_thread of currentSession %x \n", pthread_self()); + currentSession->current_command_root_thread = 0; + } + } + } + fflush(thread_stdin); + fflush(thread_stdout); + fflush(thread_stderr); + // release parameters: + if (commandName != NULL) { + // NSLog(@"Terminating command: %s thread_id %x stdin %d stdout %d stderr %d isPipeOut %d", commandName, pthread_self(), fileno(p->stdin), fileno(p->stdout), fileno(p->stderr), p->isPipeOut); + // Specific to run multiple python3 interpreters: + if ((strncmp(commandName, "python", 6) == 0) && (strlen(commandName) == strlen("python") + 1)) { + // It's one of the multiple python3 interpreters + char commandNumber = commandName[6]; + if (commandNumber == '3') PythonIsRunning[0] = false; + else { + commandNumber -= 'A' - 1; + if ((commandNumber > 0) && (commandNumber < MaxPythonInterpreters)) + PythonIsRunning[commandNumber] = false; + } + } + } + bool isSh = strcmp(p->argv[0], "sh") == 0; + for (int i = 0; i < p->argc; i++) free(p->argv_ref[i]); + free(p->argv_ref); + free(p->argv); + bool isLastThread = (currentSession->lastThreadId == pthread_self()); + // Required for Jupyter. Must check for Blink/LibTerm/iVim: + // Is that the issue in iVim? + bool mustCloseStderr = (fileno(p->stderr) != fileno(stderr)) && (fileno(p->stderr) != fileno(p->stdout)); + if (!isSh) { + mustCloseStderr &= p->isPipeErr; + if (currentSession != nil) { + mustCloseStderr &= fileno(p->stderr) != fileno(currentSession->stderr); + mustCloseStderr &= fileno(p->stderr) != fileno(currentSession->stdout); + } + } + if (mustCloseStderr) { + // NSLog(@"Closing stderr (mustCloseStderr): %d \n", fileno(p->stderr)); + fclose(p->stderr); + } + bool mustCloseStdout = fileno(p->stdout) != fileno(stdout); + if (!isSh) { + mustCloseStdout &= p->isPipeOut; + if (currentSession != nil) { + mustCloseStdout &= fileno(p->stdout) != fileno(currentSession->stdout); + } + } + if (mustCloseStdout) { + // NSLog(@"Closing stdout (mustCloseStdout): %d \n", fileno(p->stdout)); + fclose(p->stdout); + } + if ((p->dlHandle != RTLD_SELF) && (p->dlHandle != RTLD_MAIN_ONLY) + && (p->dlHandle != RTLD_DEFAULT) && (p->dlHandle != RTLD_NEXT)) + dlclose(p->dlHandle); + free(parameters); // This was malloc'ed in ios_system + if (isLastThread) { + // NSLog(@"Terminating lastthread of currentSession %x lastThreadId %x\n", pthread_self(), currentSession->lastThreadId); + currentSession->lastThreadId = 0; + } else { + // NSLog(@"Current thread %x lastthread %x \n", pthread_self(), currentSession->lastThreadId); + } + ios_releaseThread(pthread_self()); + if (currentSession->current_command_root_thread == pthread_self()) { + currentSession->current_command_root_thread = 0; + } +} + +void crash_handler(int sig) { + if (sig == SIGSEGV) { + fputs("segmentation fault\n", thread_stderr); + } else if (sig == SIGBUS) { + fputs("bus error\n", thread_stderr); + } + ios_exit(1); +} + +static void* run_function(void* parameters) { + functionParameters *p = (functionParameters *) parameters; + ios_storeThreadId(pthread_self()); + // NSLog(@"Storing thread_id: %x isPipeOut: %x isPipeErr: %x stdin %d stdout %d stderr %d command= %s\n", pthread_self(), p->isPipeOut, p->isPipeErr, fileno(p->stdin), fileno(p->stdout), fileno(p->stderr), p->argv[0]); + // NSLog(@"Starting command: %s thread_id %x", p->argv[0], pthread_self()); + // re-initialize for getopt: + // TODO: move to __thread variable for optind too + optind = 1; + opterr = 1; + optreset = 1; + __db_getopt_reset = 1; + thread_stdin = p->stdin; + thread_stdout = p->stdout; + thread_stderr = p->stderr; + thread_context = p->context; + currentSession = p->session; + + signal(SIGSEGV, crash_handler); + signal(SIGBUS, crash_handler); + + // Because some commands change argv, keep a local copy for release. + p->argv_ref = (char **)malloc(sizeof(char*) * (p->argc + 1)); + for (int i = 0; i < p->argc; i++) p->argv_ref[i] = p->argv[i]; + pthread_cleanup_push(cleanup_function, parameters); + @try + { + int retval = p->function(p->argc, p->argv); + if (currentSession != nil) currentSession->global_errno = retval; + } + @catch (NSException *exception) + { + // Print exception information + NSLog( @"NSException caught" ); + NSLog( @"Name: %@", exception.name); + NSLog( @"Reason: %@", exception.reason ); + return NULL; + } + @finally + { + // Cleanup, in both success and fail cases + pthread_cleanup_pop(1); + return NULL; + } +} + +static NSString* miniRoot = nil; // limit operations to below a certain directory (~, usually). +static NSArray *allowedPaths = nil; +static NSDictionary *commandList = nil; +// do recompute directoriesInPath only if $PATH has changed +static NSString* fullCommandPath = @""; +static NSArray *directoriesInPath; + +void initializeEnvironment() { + // setup a few useful environment variables + // Initialize paths for application files, including history.txt and keys + NSString *docsPath; + if (miniRoot == nil) docsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; + else docsPath = miniRoot; + + // Where the executables are stored: $PATH + ~/Library/bin + ~/Documents/bin + // Add content of old PATH to this. PATH *is* defined in iOS, surprising as it may be. + // I'm not going to erase it, so we just add ourselves. + // Sometimes, we go through main several times, so make sure we only append to PATH once + NSString* checkingPath = [NSString stringWithCString:getenv("PATH") encoding:NSUTF8StringEncoding]; + if (! [fullCommandPath isEqualToString:checkingPath]) { + fullCommandPath = checkingPath; + } + if (![fullCommandPath containsString:@"Documents/bin"]) { + NSString *binPath = [docsPath stringByAppendingPathComponent:@"bin"]; + fullCommandPath = [[binPath stringByAppendingString:@":"] stringByAppendingString:fullCommandPath]; + setenv("PATH", fullCommandPath.UTF8String, 1); // 1 = override existing value + } + setenv("APPDIR", [[NSBundle mainBundle] resourcePath].UTF8String, 1); + setenv("PATH_LOCALE", docsPath.UTF8String, 0); // CURL config in ~/Documents/ or [Cloud Drive]/ + + setenv("TERM", "xterm", 1); // 1 = override existing value + setenv("TMPDIR", NSTemporaryDirectory().UTF8String, 0); // tmp directory + setenv("CLICOLOR", "1", 1); + setenv("LSCOLORS", "ExFxBxDxCxegedabagacad", 0); // colors for ls on black background + + // We can't write in $HOME so we need to set the position of config files: + setenv("SSH_HOME", docsPath.UTF8String, 0); // SSH keys in ~/Documents/.ssh/ or [Cloud Drive]/.ssh + setenv("DIG_HOME", docsPath.UTF8String, 0); // .digrc is in ~/Documents/.digrc or [Cloud Drive]/.digrc + setenv("CURL_HOME", docsPath.UTF8String, 0); // CURL config in ~/Documents/ or [Cloud Drive]/ + setenv("SSL_CERT_FILE", [docsPath stringByAppendingPathComponent:@"cacert.pem"].UTF8String, 0); // SLL cacert.pem in ~/Documents/cacert.pem or [Cloud Drive]/cacert.pem + // iOS already defines "HOME" as the home dir of the application + for (int i = 0; i < MaxPythonInterpreters; i++) PythonIsRunning[i] = false; + NSString *libPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject]; + // environment variables for Python: + setenv("PYTHONHOME", libPath.UTF8String, 0); // Python files are in ~/Library/lib/python[23].x/ + // XDG setup directories (~/Library/Caches, ~/Library/Preferences): + setenv("XDG_CACHE_HOME", [libPath stringByAppendingPathComponent:@"Caches"].UTF8String, 0); + setenv("XDG_CONFIG_HOME", [libPath stringByAppendingPathComponent:@"Preferences"].UTF8String, 0); + setenv("XDG_DATA_HOME", libPath.UTF8String, 0); + // if we use Python, we define a few more environment variables: + setenv("PYTHONEXECUTABLE", "python3", 0); // Python executable name for python3 + setenv("PYZMQ_BACKEND", "cffi", 0); + // Configuration files are in $HOME (and hidden) + setenv("JUPYTER_CONFIG_DIR", [docsPath stringByAppendingPathComponent:@".jupyter"].UTF8String, 0); + setenv("IPYTHONDIR", [docsPath stringByAppendingPathComponent:@".ipython"].UTF8String, 0); + setenv("MPLCONFIGDIR", [docsPath stringByAppendingPathComponent:@".config/matplotlib"].UTF8String, 0); + // hg config file in ~/Documents/.hgrc + setenv("HGRCPATH", [docsPath stringByAppendingPathComponent:@".hgrc"].UTF8String, 0); + if (![fullCommandPath containsString:@"Library/bin"]) { + NSString *binPath = [libPath stringByAppendingPathComponent:@"bin"]; + fullCommandPath = [[binPath stringByAppendingString:@":"] stringByAppendingString:fullCommandPath]; + } + if (!sideLoading) { + // If we're not sideloading, executeables will also be in the Application directory + NSString *mainBundlePath = [[NSBundle mainBundle] resourcePath]; + NSString *mainBundleLibPath = [mainBundlePath stringByAppendingPathComponent:@"Library"]; + // if we're not sideloading, all "executable" files are in the AppDir: + // $APPDIR/Library/bin3 + NSString *binPath = [mainBundleLibPath stringByAppendingPathComponent:@"bin3"]; + fullCommandPath = [[binPath stringByAppendingString:@":"] stringByAppendingString:fullCommandPath]; + // $APPDIR/Library/bin + binPath = [mainBundleLibPath stringByAppendingPathComponent:@"bin"]; + fullCommandPath = [[binPath stringByAppendingString:@":"] stringByAppendingString:fullCommandPath]; + // $APPDIR/bin + binPath = [mainBundlePath stringByAppendingPathComponent:@"bin"]; + fullCommandPath = [[binPath stringByAppendingString:@":"] stringByAppendingString:fullCommandPath]; + } + directoriesInPath = [fullCommandPath componentsSeparatedByString:@":"]; + setenv("PATH", fullCommandPath.UTF8String, 1); // 1 = override existing value + // Store the maximum number of file descriptors allowed: + getrlimit(RLIMIT_NOFILE, &limitFilesOpen); +} + +static char* parseArgument(char* argument, char* command) { + // expand all environment variables, convert "~" to $HOME (only if localFile) + // we also pass the shell command for some specific behaviour (don't do this for that command) + NSString* argumentString = [NSString stringWithCString:argument encoding:NSUTF8StringEncoding]; + // 1) expand environment variables, + "~" (not wildcards ? and *) + bool cannotExpand = false; + while ([argumentString containsString:@"$"] && !cannotExpand) { + // It has environment variables inside. Work on them one by one. + // position of first "$" sign: + NSRange r1 = [argumentString rangeOfString:@"$"]; + // position of first "/" after this $ sign: + NSRange r2 = [argumentString rangeOfString:@"/" options:NULL range:NSMakeRange(r1.location + r1.length, [argumentString length] - r1.location - r1.length)]; + // position of first ":" after this $ sign: + NSRange r3 = [argumentString rangeOfString:@":" options:NULL range:NSMakeRange(r1.location + r1.length, [argumentString length] - r1.location - r1.length)]; + if ((r2.location == NSNotFound) && (r3.location == NSNotFound)) r2.location = [argumentString length]; + else if ((r2.location == NSNotFound) || (r3.location < r2.location)) r2.location = r3.location; + + NSRange rSub = NSMakeRange(r1.location + r1.length, r2.location - r1.location - r1.length); + NSString *variable_string = [argumentString substringWithRange:rSub]; + const char* variable = getenv([variable_string UTF8String]); + if (variable) { + // Okay, so this one exists. + NSString* replacement_string = [NSString stringWithCString:variable encoding:NSUTF8StringEncoding]; + variable_string = [[NSString stringWithCString:"$" encoding:NSUTF8StringEncoding] stringByAppendingString:variable_string]; + argumentString = [argumentString stringByReplacingOccurrencesOfString:variable_string withString:replacement_string]; + } else cannotExpand = true; // found a variable we can't expand. stop trying for this argument + } + // 2) Tilde conversion: replace "~" with $HOME + // If there are multiple users on iOS, this code will need to be changed. + if([argumentString hasPrefix:@"~"]) { + // So it begins with "~". We can't use stringByExpandingTildeInPath because apps redefine HOME + NSString* replacement_string; + if (miniRoot == nil) + replacement_string = [NSString stringWithCString:(getenv("HOME")) encoding:NSUTF8StringEncoding]; + else replacement_string = miniRoot; + if (([argumentString hasPrefix:@"~/"]) || ([argumentString hasPrefix:@"~:"]) || ([argumentString length] == 1)) { + NSString* test_string = @"~"; + argumentString = [argumentString stringByReplacingOccurrencesOfString:test_string withString:replacement_string options:NULL range:NSMakeRange(0, 1)]; + } + } + // Also convert ":~something" in PATH style variables + // We don't use these yet, but we could. + // We do this expansion only for setenv + if (strcmp(command, "setenv") == 0) { + // This is something we need to avoid if the command is "scp" or "sftp" + if ([argumentString containsString:@":~"]) { + NSString* homeDir; + if (miniRoot == nil) homeDir = [NSString stringWithCString:(getenv("HOME")) encoding:NSUTF8StringEncoding]; + else homeDir = miniRoot; + // Only 1 possibility: ":~" (same as $HOME) + if (homeDir.length > 0) { + if ([argumentString containsString:@":~/"]) { + NSString* test_string = @":~/"; + NSString* replacement_string = [[NSString stringWithCString:":" encoding:NSUTF8StringEncoding] stringByAppendingString:homeDir]; + replacement_string = [replacement_string stringByAppendingString:[NSString stringWithCString:"/" encoding:NSUTF8StringEncoding]]; + argumentString = [argumentString stringByReplacingOccurrencesOfString:test_string withString:replacement_string]; + } else if ([argumentString hasSuffix:@":~"]) { + NSString* test_string = @":~"; + NSString* replacement_string = [[NSString stringWithCString:":" encoding:NSUTF8StringEncoding] stringByAppendingString:homeDir]; + argumentString = [argumentString stringByReplacingOccurrencesOfString:test_string withString:replacement_string options:NULL range:NSMakeRange([argumentString length] - 2, 2)]; + } else if ([argumentString hasSuffix:@":"]) { + NSString* test_string = @":"; + NSString* replacement_string = [[NSString stringWithCString:":" encoding:NSUTF8StringEncoding] stringByAppendingString:homeDir]; + argumentString = [argumentString stringByReplacingOccurrencesOfString:test_string withString:replacement_string options:NULL range:NSMakeRange([argumentString length] - 2, 2)]; + } + } + } + } + const char* newArgument = [argumentString UTF8String]; + if (strcmp(argument, newArgument) == 0) return argument; // nothing changed + // Make sure the argument is reallocated, so it can be free-ed + char* returnValue = realloc(argument, strlen(newArgument) + 1); + strcpy(returnValue, newArgument); + return returnValue; +} + + +static void initializeCommandList() +{ + // Loads command names and where to find them (digital library, function name) from plist dictionaries: + // + // Syntax for the dictionaris: + // key = command name, followed by an array of 4 components: + // 1st component: name of digital library (will be passed to dlopen(), can be SELF for RTLD_SELF or MAIN for RTLD_MAIN_ONLY) + // 2nd component: name of function to be called + // 3rd component: chain sent to getopt (for arguments in autocomplete) + // 4th component: takes a file/directory as argument + // + // Example: + // rlogin + // + // libnetwork_ios.dylib + // rlogin_main + // 468EKLNS:X:acde:fFk:l:n:rs:uxy + // no + // + + if (commandList != nil) return; + NSError *error; + NSString* applicationDirectory = [[NSBundle mainBundle] resourcePath]; + NSString* commandDictionary = [applicationDirectory stringByAppendingPathComponent:@"commandDictionary.plist"]; + NSURL *locationURL = [NSURL fileURLWithPath:commandDictionary isDirectory:NO]; + if ([locationURL checkResourceIsReachableAndReturnError:&error] == NO) { NSLog(@"%@", [error localizedDescription]); return; } + NSData* loadedFromFile = [NSData dataWithContentsOfFile:commandDictionary options:0 error:&error]; + if (!loadedFromFile) { NSLog(@"%@", [error localizedDescription]); return; } + commandList = [NSPropertyListSerialization propertyListWithData:loadedFromFile options:NSPropertyListImmutable format:NULL error:&error]; + if (!commandList) { NSLog(@"%@", [error localizedDescription]); return; } + // replaces the following command, marked as deprecated in the doc: + // commandList = [NSDictionary dictionaryWithContentsOfFile:commandDictionary]; + if (sideLoading) { + // more commands, for sideloaders (commands that won't pass AppStore rules, or with licensing issues): + NSString* extraCommandsDictionary = [applicationDirectory stringByAppendingPathComponent:@"extraCommandsDictionary.plist"]; + locationURL = [NSURL fileURLWithPath:extraCommandsDictionary isDirectory:NO]; + if ([locationURL checkResourceIsReachableAndReturnError:&error] == NO) { NSLog(@"%@", [error localizedDescription]); return; } + NSData* extraLoadedFromFile = [NSData dataWithContentsOfFile:extraCommandsDictionary options:0 error:&error]; + if (!extraLoadedFromFile) { NSLog(@"%@", [error localizedDescription]); return; } + NSDictionary* extraCommandList = [NSPropertyListSerialization propertyListWithData:extraLoadedFromFile options:NSPropertyListImmutable format:NULL error:&error]; + if (!extraCommandList) { NSLog(@"%@", [error localizedDescription]); return; } + // merge the two dictionaries: + NSMutableDictionary *mutableDict = [commandList mutableCopy]; + [mutableDict addEntriesFromDictionary:extraCommandList]; + commandList = [mutableDict copy]; + } +} + +int ios_setMiniRoot(NSString* mRoot) { + BOOL isDir; + NSFileManager *fileManager = [[NSFileManager alloc] init]; + + if (![fileManager fileExistsAtPath:mRoot isDirectory:&isDir]) { + return 0; + } + + if (!isDir) { + return 0; + } + + // fileManager has different ways of expressing the same directory. + // We need to actually change to the directory to get its "real name". + NSString* currentDir = [fileManager currentDirectoryPath]; + + if (![fileManager changeCurrentDirectoryPath:mRoot]) { + return 0; + } + // also don't set the miniRoot if we can't go in there + // get the real name for miniRoot: + miniRoot = [fileManager currentDirectoryPath]; + // Back to where we we before: + [fileManager changeCurrentDirectoryPath:currentDir]; + if (currentSession != nil) { + strcpy(currentSession->currentDir, [miniRoot UTF8String]); + strcpy(currentSession->previousDirectory, [miniRoot UTF8String]); + } + return 1; // mission accomplished +} + +// Called when +int ios_setMiniRootURL(NSURL* mRoot) { + NSFileManager *fileManager = [[NSFileManager alloc] init]; + if (currentSession == NULL) { + currentSession = malloc(sizeof(sessionParameters)); + initSessionParameters(currentSession); + } + strcpy(currentSession->localMiniRoot, [mRoot.path UTF8String]); + strcpy(currentSession->previousDirectory, currentSession->currentDir); + strcpy(currentSession->currentDir, [[mRoot path] UTF8String]); + [fileManager changeCurrentDirectoryPath:[mRoot path]]; + return 1; // mission accomplished +} + +int ios_setAllowedPaths(NSArray *paths) { + allowedPaths = paths; + return 1; +} + +BOOL __allowed_cd_to_path(NSString *path) { + if (miniRoot == nil || [path hasPrefix:miniRoot]) { + return YES; + } + + if (strlen(currentSession->localMiniRoot) != 0) { + NSString *localMiniRootPath = [NSString stringWithCString:currentSession->localMiniRoot encoding:NSUTF8StringEncoding]; + if (localMiniRootPath && [path hasPrefix:localMiniRootPath]) { + return YES; + } + } + + for (NSString *dir in allowedPaths) { + if ([path hasPrefix:dir]) { + return YES; + } + } + + return NO; +} + +void __cd_to_dir(NSString *newDir, NSFileManager *fileManager) { + BOOL isDir; + // Check for permission and existence: + if (![fileManager fileExistsAtPath:newDir isDirectory:&isDir]) { + fprintf(thread_stderr, "cd: %s: no such file or directory\n", [newDir UTF8String]); + return; + } + if (!isDir) { + fprintf(thread_stderr, "cd: %s: not a directory\n", [newDir UTF8String]); + return; + } + if (![fileManager isReadableFileAtPath:newDir] || + ![fileManager changeCurrentDirectoryPath:newDir]) { + fprintf(thread_stderr, "cd: %s: permission denied\n", [newDir UTF8String]); + return; + } + + // We managed to change the directory. + // Was that allowed? + // Allowed "cd" = below miniRoot *or* below localMiniRoot + NSString* resultDir = [fileManager currentDirectoryPath]; + + if (__allowed_cd_to_path(resultDir)) { + strcpy(currentSession->previousDirectory, currentSession->currentDir); + return; + } + + fprintf(thread_stderr, "cd: %s: permission denied\n", [newDir UTF8String]); + // If the user tried to go above the miniRoot, set it to miniRoot + if ([miniRoot hasPrefix:resultDir]) { + [fileManager changeCurrentDirectoryPath:miniRoot]; + strcpy(currentSession->currentDir, [miniRoot UTF8String]); + strcpy(currentSession->previousDirectory, currentSession->currentDir); + } else { + // go back to where we were before: + [fileManager changeCurrentDirectoryPath:[NSString stringWithCString:currentSession->currentDir encoding:NSUTF8StringEncoding]]; + } +} + +int cd_main(int argc, char** argv) { + if (currentSession == NULL) { + return 1; + } + NSFileManager *fileManager = [[NSFileManager alloc] init]; + + if (argc > 1) { + NSString* newDir = @(argv[1]); + if (strcmp(argv[1], "-") == 0) { + // "cd -" option to pop back to previous directory + newDir = [NSString stringWithCString:currentSession->previousDirectory encoding:NSUTF8StringEncoding]; + } + __cd_to_dir(newDir, fileManager); + } else { // [cd] Help, I'm lost, bring me back home + strcpy(currentSession->previousDirectory, [[fileManager currentDirectoryPath] UTF8String]); + + if (miniRoot != nil) { + [fileManager changeCurrentDirectoryPath:miniRoot]; + } else { + [fileManager changeCurrentDirectoryPath:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]]; + } + } + strcpy(currentSession->currentDir, [[fileManager currentDirectoryPath] UTF8String]); + return 0; +} + + +NSString* getoptString(NSString* commandName) { + if (commandList == nil) initializeCommandList(); + NSArray* commandStructure = [commandList objectForKey: commandName]; + if (commandStructure != nil) return commandStructure[2]; + else return @""; +} + +NSString* operatesOn(NSString* commandName) { + if (commandList == nil) initializeCommandList(); + NSArray* commandStructure = [commandList objectForKey: commandName]; + if (commandStructure != nil) return commandStructure[3]; + else return @""; +} + + +int ios_executable(const char* inputCmd) { + // returns 1 if this is one of the commands we define in ios_system, 0 otherwise + if (commandList == nil) initializeCommandList(); + // Take basename in case someone put a path before: + NSArray* valuesFromDict = [commandList objectForKey: [NSString stringWithCString:basename(inputCmd) encoding:NSUTF8StringEncoding]]; + // we could dlopen() here, but that would defeat the purpose + if (valuesFromDict == nil) return 0; + else return 1; +} + +// Where to direct input/output of the next thread: +static __thread FILE* child_stdin = NULL; +static __thread FILE* child_stdout = NULL; +static __thread FILE* child_stderr = NULL; + +FILE* ios_popen(const char* inputCmd, const char* type) { + // Save existing streams: + int fd[2] = {0}; + const char* command = inputCmd; + // skip past all spaces + while ((command[0] == ' ') && strlen(command) > 0) command++; + if (pipe(fd) < 0) { return NULL; } // Nothing we can do if pipe fails + // NOTES: fd[0] is set up for reading, fd[1] is set up for writing + // fpout = fdopen(fd[1], "w"); + // fpin = fdopen(fd[0], "r"); + if (type[0] == 'w') { + // open pipe for reading + child_stdin = fdopen(fd[0], "r"); + // launch command: if the command fails, return NULL. + int returnValue = ios_system(command); + if (returnValue == 0) + return fdopen(fd[1], "w"); + } else if (type[0] == 'r') { + // open pipe for writing + // set up streams for thread + child_stdout = fdopen(fd[1], "w"); + // launch command: if the command fails, return NULL. + int returnValue = ios_system(command); + if (returnValue == 0) + return fdopen(fd[0], "r"); + } + // pipe creation failed, command starting failed: + return NULL; +} + +// small function, behaves like strstr but skips quotes (Yury Korolev) +char *strstrquoted(char* str1, char* str2) { + + if (str1 == NULL || str2 == NULL) { + return NULL; + } + size_t len1 = strlen(str1); + size_t len2 = strlen(str2); + + if (len1 < len2) { + return NULL; + } + + if (strcmp(str1, str2) == 0) { + return str1; + } + + char quotechar = 0; + int esclen = 0; + int matchlen = 0; + + for (int i = 0; i < len1; i++) { + char ch = str1[i]; + if (quotechar) { + if (ch == '\\') { + esclen++; + continue; + } + + if (ch == quotechar) { + if (esclen % 2 == 1) { + esclen = 0; + continue; + } + quotechar = 0; + esclen = 0; + continue; + } + + esclen = 0; + continue; + } + + if (ch == '"' || ch == '\'') { + if (esclen % 2 == 0) { + quotechar = ch; + } + matchlen = 0; + esclen = 0; + continue; + } + + if (ch == '\\') { + esclen++; + } + + if (str2[matchlen] == ch) { + matchlen++; + if (matchlen == len2) { + return str1 + i - matchlen + 1; + } + continue; + } + + matchlen = 0; + } + return NULL; +} + +static char* concatenateArgv(char* const argv[]) { + int argc = 0; + int cmdLength = 0; + // concatenate all arguments into a big command. + // We need this because some programs call execv() with a single string: "ssh hg@bitbucket.org 'hg -R ... --stdio'" + // So we rely on ios_system to break them into chunks. + while(argv[argc] != NULL) { cmdLength += strlen(argv[argc]) + 1; argc++;} + if (argc == 0) return NULL; // safeguard check + char* cmd = malloc((cmdLength + 3 * argc) * sizeof(char)); // space for quotes + strcpy(cmd, argv[0]); + argc = 1; + while (argv[argc] != NULL) { + if (strstrquoted(argv[argc], " ")) { + // argument contains spaces. Enclose it into quotes: + if (strstrquoted(argv[argc], "\"") == NULL) { + // argument does not contain ". Enclose with " + strcat(cmd, " \""); + strcat(cmd, argv[argc]); + strcat(cmd, "\""); + argc++; + continue; + } + if (strstrquoted(argv[argc], "'") == NULL) { + // argument does not contain '. Enclose with ' + strcat(cmd, " '"); + strcat(cmd, argv[argc]); + strcat(cmd, "'"); + argc++; + continue; + } + fprintf(thread_stderr, "Don't know what to do with this argument, sorry: %s\n", argv[argc]); + } + strcat(cmd, " "); + strcat(cmd, argv[argc]); + argc++; + } + return cmd; +} + +int pbpaste(int argc, char** argv) { + // We can paste strings and URLs. + if ([UIPasteboard generalPasteboard].hasStrings) { + fprintf(thread_stdout, "%s", [[UIPasteboard generalPasteboard].string UTF8String]); + if (![[UIPasteboard generalPasteboard].string hasSuffix:@"\n"]) fprintf(thread_stdout, "\n"); + return 0; + } + if ([UIPasteboard generalPasteboard].hasURLs) { + fprintf(thread_stdout, "%s\n", [[[UIPasteboard generalPasteboard].URL absoluteString] UTF8String]); + return 0; + } + return 1; +} + + +int pbcopy(int argc, char** argv) { + if (argc == 1) { + // no arguments, listen to stdin + const int bufsize = 1024; + char buffer[bufsize]; + NSMutableData* data = [[NSMutableData alloc] init]; + + ssize_t count = 0; + while ((count = read(fileno(thread_stdin), buffer, bufsize-1))) { + [data appendBytes:buffer length:count]; + } + + NSString* result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + + if (!result) { + return 1; + } + + [UIPasteboard generalPasteboard].string = result; + } else { + // threre are arguments, concatenate and paste: + char* cmd = concatenateArgv(argv + 1); + [UIPasteboard generalPasteboard].string = @(cmd); + free(cmd); + } + return 0; +} + + +// Auxiliary function for sh_main. Given a string of characters (command1 && command2), +// split it into the sub commands and execute each of them in sequence: +static int splitCommandAndExecute(char* command) { + // Remember to use fork / waitpid to wait for the commands to finish + if (command == NULL) return 0; + char* maxPointer = command + strlen(command); + int returnValue = 0; + while (command[0] != 0) { + // NSLog(@"stdout %x \n", fileno(thread_stdout)); + // NSLog(@"stderr %x \n", fileno(thread_stderr)); + char* nextAnd = strstrquoted(command, "&&"); + char* nextOr = strstrquoted(command, "||"); + if ((nextAnd == NULL) && (nextOr == NULL)) { + // Only one command left + pid_t pid = ios_fork(); + returnValue = ios_system(command); + // NSLog(@"Started command, stored last_thread= %x", currentSession->lastThreadId); + ios_waitpid(pid); + break; + } + int nextCommandPosition = 0; + bool andNextCommand = false; + if (nextAnd != NULL) { + nextCommandPosition = nextAnd - command; + andNextCommand = true; + } + if (nextOr != NULL) { + if (nextOr - command < nextCommandPosition) { + nextCommandPosition = nextOr - command; + andNextCommand = false; + } + } + command[nextCommandPosition] = NULL; // terminate string + pid_t pid = ios_fork(); + returnValue = ios_system(command); + // NSLog(@"Started command (2), stored last_thread= %x", currentSession->lastThreadId); + ios_waitpid(pid); + if (andNextCommand && (returnValue != 0)) { + // && + the command returned error, we return: + break; + } else if (!andNextCommand && (returnValue == 0)) { + // || + the command worked, we return: + break; + } + command += (nextCommandPosition + 2); // char after "&&" or "||" + while ((command[0] == ' ') && (command < maxPointer)) command++; // skip spaces + if (command > maxPointer) return 0; // happens if the command ends with && or || + } + return returnValue; +} + +sessionParameters* parentSession = NULL; + +NSString* parentDir; +int sh_main(int argc, char** argv) { + // NOT an actual shell. + // for commands that call other commands as "sh -c command" or "sh -c command1 && command2" + // NSLog(@"sh_main, stdout %d \n", fileno(thread_stdout)); + // NSLog(@"sh_main, stderr %d \n", fileno(thread_stderr)); + if ((argc < 2) || (strncmp(argv[1], "-h", 2) == 0)) { + fprintf(thread_stderr, "Not an actual shell. sh is provided for compatibility with commands that call other commands.\n"); + fprintf(thread_stderr, "Usage: sh [-flags] command: executes command (all flags are ignored).\n"); + fprintf(thread_stderr, " sh [-flags] command1 && command2 [&& command3 && ...]: executes the commands, in order, until one returns error.\n"); + fprintf(thread_stderr, " sh [-flags] command1 || command2 [|| command3 || ...]: executes the commands, in order, until one returns OK.\n"); + return 0; + } + char** command = argv + 1; // skip past "sh" + while ((command[0][0] == '-') && (command[0] != NULL)) { command++; } // skip past all flags + if (command[0] == NULL) { + argv[0][0] = 'h'; // prevent termination in cleanup_function + return 0; + } + // If we reach this point, we have commands to execute. + // Store current sesssion, create a new session specific for this, execute commands + id sessionKey = @((NSUInteger)&sh_session); + if (sessionList != nil) { + sessionParameters* runningShellSession = (sessionParameters*)[[sessionList objectForKey: sessionKey] pointerValue]; + if (runningShellSession != NULL) { + if ((runningShellSession->lastThreadId != 0) && (runningShellSession->lastThreadId != pthread_self())){ + // NSLog(@"There is another sh session running: last_thread= %x", runningShellSession->lastThreadId); + argv[0][0] = 'h'; // prevent termination in cleanup_function + return 1; + } else { + // NSLog(@"There is another sh session running: last_thread= %x us= %x. Continuing.", runningShellSession->lastThreadId, pthread_self()); + } + } + } + NSFileManager *fileManager = [[NSFileManager alloc] init]; + // NSLog(@"parentSession = %x currentSession = %x currentDir = %s\n", parentSession, currentSession, [fileManager currentDirectoryPath].UTF8String); + if (currentSession->context == sh_session) { + return 1; // We cannot have a sh command starting a sh command. + } + if (parentSession == NULL) { + parentSession = currentSession; + parentDir = [fileManager currentDirectoryPath]; + } + ios_switchSession(&sh_session); // create a new session + // NSLog(@"after switchSession, currentDir = %s\n", [fileManager currentDirectoryPath].UTF8String); + currentSession->isMainThread = false; + currentSession->context = sh_session; + currentSession->stdin = thread_stdin; + currentSession->stdout = thread_stdout; + currentSession->stderr = thread_stderr; + currentSession->current_command_root_thread = NULL; + currentSession->lastThreadId = NULL; + // Need to loop twice: over each argument, and inside each argument. + // &&: keep computing until one command is in error + // ||: keep computing until one command is not in error + // Remember to use fork / waitpid to wait for the commands to finish + int returnValue = 0; + while (command[0] != NULL) { + int i = 0; + while ((command[i] != NULL) && (strcmp(command[i], "&&") != 0) && (strcmp(command[i], "||") != 0)) i++; + if (command[i] == NULL) { + char* lastCommand = concatenateArgv(command); + returnValue = splitCommandAndExecute(lastCommand); + free(lastCommand); + break; + } + bool andNextCommand = (strcmp(command[i], "&&") == 0); // should we continue? + command[i] = NULL; + char* newCommand = concatenateArgv(command); + returnValue = splitCommandAndExecute(newCommand); + free(newCommand); + if (andNextCommand && (returnValue != 0)) { + // && + the command returned error, we return: + break; + } else if (!andNextCommand && (returnValue == 0)) { + // || + the command worked, we return: + break; + } + command += (i+1); + } + // NSLog(@"Closing shell session; last_thread= %x root= %x", currentSession->lastThreadId, currentSession->current_command_root_thread); + if (![parentDir isEqualToString:[fileManager currentDirectoryPath]]) { + // NSLog(@"Reset current Dir to= %s instead of %s", parentDir.UTF8String, [fileManager currentDirectoryPath].UTF8String); + [fileManager changeCurrentDirectoryPath:parentDir]; + } + ios_closeSession(&sh_session); + currentSession = parentSession; + parentSession = NULL; + return returnValue; +} + + +int ios_execv(const char *path, char* const argv[]) { + // path and argv[0] are the same (not in theory, but in practice, since Python wrote the command) + // start "child" with the child streams: + char* cmd = concatenateArgv(argv); + int returnValue = ios_system(cmd); + free(cmd); + return returnValue; +} + +int ios_execve(const char *path, char* const argv[], char* envp[]) { + // TODO: save the environment (HOW?) and current dir + // TODO: replace environment with envp. envp looks a lot like current environment, though. + int returnValue = ios_execv(path, argv); + // TODO: restore the environment (HOW?) + return returnValue; +} + +pthread_t ios_getLastThreadId() { + if (!currentSession) return nil; + return (currentSession->lastThreadId); +} + +/* + * Public domain dup2() lookalike + * by Curtis Jackson @ AT&T Technologies, Burlington, NC + * electronic address: burl!rcj + * Edited for iOS by N. Holzschuch. + * The idea is that dup2(fd, [012]) is usually called between fork and exec. + * + * dup2 performs the following functions: + * + * Check to make sure that fd1 is a valid open file descriptor. + * Check to see if fd2 is already open; if so, close it. + * Duplicate fd1 onto fd2; checking to make sure fd2 is a valid fd. + * Return fd2 if all went well; return BADEXIT otherwise. + */ + +int ios_dup2(int fd1, int fd2) +{ + // iOS specifics: trying to access stdin/stdout/stderr? + if (fd1 < 3) { + // specific cases like dup2(STDOUT_FILENO, STDERR_FILENO) + FILE* stream1 = NULL; + switch (fd1) { + case 0: stream1 = child_stdin; break; + case 1: stream1 = child_stdout; break; + case 2: stream1 = child_stderr; break; + } + switch (fd2) { + case 0: child_stdin = stream1; return fd2; + case 1: child_stdout = stream1; return fd2; + case 2: child_stderr = stream1; return fd2; + } + } + if (fd2 == 0) { child_stdin = fdopen(fd1, "rb"); } + else if (fd2 == 1) { child_stdout = fdopen(fd1, "wb"); } + else if (fd2 == 2) { + if ((child_stdout != NULL) && (fileno(child_stdout) == fd1)) child_stderr = child_stdout; + else child_stderr = fdopen(fd1, "wb"); } + else if (fd1 != fd2) { + if (fcntl(fd1, F_GETFL) < 0) + return -1; + if (fcntl(fd2, F_GETFL) >= 0) + close(fd2); + if (fcntl(fd1, F_DUPFD, fd2) < 0) + return -1; + } + return fd2; +} + +int ios_kill() +{ + if (currentSession == NULL) return ESRCH; + if (currentSession->current_command_root_thread > 0) { + struct sigaction query_action; + if ((sigaction (SIGINT, NULL, &query_action) >= 0) && + (query_action.sa_handler != SIG_DFL) && + (query_action.sa_handler != SIG_IGN)) { + /* A programmer-defined signal handler is in effect. */ + // This might be problematic with multiple commands running at the same time that all define SIGINT + // ...such as ls. + query_action.sa_handler(SIGINT); + // kill(getpid(), SIGINT); // infinite loop? + } else { + // Send pthread_cancel with the given signal to the current main thread, if there is one. + return pthread_cancel(currentSession->current_command_root_thread); + } + } + // No process running + return ESRCH; +} + +int ios_killpid(pid_t pid, int sig) { + return pthread_cancel(ios_getThreadId(pid)); +} + +void ios_switchSession(const void* sessionId) { + char* sessionName = (char*) sessionId; + if ((currentSession != nil) && (parentSession != nil)) { + if ((currentSession->context == sh_session) && (parentSession->context == sessionName)) { + // If we are running a sh_session inside the requested sessionId, there is no need to change: + return; + } + } + // NSLog(@"ios_switchSession to %s\n", sessionName); + NSFileManager *fileManager = [[NSFileManager alloc] init]; + id sessionKey = @((NSUInteger)sessionId); + if (sessionList == nil) { + sessionList = [NSMutableDictionary new]; + if (currentSession != NULL) [sessionList setObject: [NSValue valueWithPointer:currentSession] forKey: sessionKey]; + } + currentSession = (sessionParameters*)[[sessionList objectForKey: sessionKey] pointerValue]; + + if (currentSession == NULL) { + sessionParameters* newSession = malloc(sizeof(sessionParameters)); + initSessionParameters(newSession); + [sessionList setObject: [NSValue valueWithPointer:newSession] forKey: sessionKey]; + currentSession = newSession; + } else { + NSString* currentSessionDir = [NSString stringWithCString:currentSession->currentDir encoding:NSUTF8StringEncoding]; + if (![currentSessionDir isEqualToString:[fileManager currentDirectoryPath]]) { + [fileManager changeCurrentDirectoryPath:currentSessionDir]; + } + currentSession->stdin = stdin; + currentSession->stdout = stdout; + currentSession->stderr = stderr; + } +} + +void ios_setDirectoryURL(NSURL* workingDirectoryURL) { + NSFileManager *fileManager = [[NSFileManager alloc] init]; + [fileManager changeCurrentDirectoryPath:[workingDirectoryURL path]]; + if (currentSession != NULL) { + NSString* currentSessionDir = [NSString stringWithCString:currentSession->currentDir encoding:NSUTF8StringEncoding]; + if ([currentSessionDir isEqualToString:[fileManager currentDirectoryPath]]) return; + strcpy(currentSession->previousDirectory, currentSession->currentDir); + strcpy(currentSession->currentDir, [[workingDirectoryURL path] UTF8String]); + } +} + +void ios_closeSession(const void* sessionId) { + // delete information associated with current session: + if (sessionList == nil) return; + id sessionKey = @((NSUInteger)sessionId); + [sessionList removeObjectForKey: sessionKey]; + currentSession = NULL; +} + +int ios_isatty(int fd) { + if (currentSession == NULL) return 0; + // 2 possibilities: 0, 1, 2 (classical) or fileno(thread_stdout) + if (thread_stdin != NULL) { + if ((fd == STDIN_FILENO) || (fd == fileno(currentSession->stdin)) || (fd == fileno(thread_stdin))) + return (fileno(thread_stdin) == fileno(currentSession->stdin)); + } + if (thread_stdout != NULL) { + if ((fd == STDOUT_FILENO) || (fd == fileno(currentSession->stdout)) || (fd == fileno(thread_stdout))) { + return (fileno(thread_stdout) == fileno(currentSession->stdout)); + } + } + if (thread_stderr != NULL) { + if ((fd == STDERR_FILENO) || (fd == fileno(currentSession->stderr)) || (fd == fileno(thread_stderr))) + return (fileno(thread_stderr) == fileno(currentSession->stderr)); + } + return 0; +} + +void ios_setStreams(FILE* _stdin, FILE* _stdout, FILE* _stderr) { + if (currentSession == NULL) return; + currentSession->stdin = _stdin; + currentSession->stdout = _stdout; + currentSession->stderr = _stderr; +} + +void ios_settty(FILE* _tty) { + if (currentSession == NULL) return; + currentSession->tty = _tty; +} + +int ios_gettty() { + if (currentSession == NULL) return NULL; + if (currentSession->tty == NULL) return -1; + return fileno(currentSession->tty); +} + +void ios_setContext(const void *context) { + if (currentSession == NULL) return; + currentSession->context = context; +} + +void* ios_getContext() { + if (currentSession == NULL) return NULL; + return currentSession->context; +} + + + +// For customization: +// replaces a function (e.g. ls_main) with another one, provided by the user (ls_mine_main) +// if the function does not exist, add it to the list +// if "allOccurences" is true, search for all commands that share the same function, replace them too. +// ("compress" and "uncompress" both point to compress_main. You probably want to replace both, but maybe +// you just happen to have a very fast uncompress, different from compress). +// We work with function names, not function pointers. +void replaceCommand(NSString* commandName, NSString* functionName, bool allOccurences) { + // Does that function exist / is reachable? We've had problems with stripping. + int (*function)(int ac, char** av) = NULL; + function = dlsym(RTLD_MAIN_ONLY, functionName.UTF8String); + if (!function) { + NSLog(@"replaceCommand: %@ does not exist", functionName); + return; // if not, we don't replace. + } + if (commandList == nil) initializeCommandList(); + NSArray* oldValues = [commandList objectForKey: commandName]; + NSString* oldFunctionName = nil; + if (oldValues != nil) oldFunctionName = oldValues[1]; + NSMutableDictionary *mutableDict = [commandList mutableCopy]; + mutableDict[commandName] = [NSArray arrayWithObjects: @"MAIN", functionName, @"", @"file", nil]; + + if ((oldFunctionName != nil) && allOccurences) { + // scan through all dictionary entries + for (NSString* existingCommand in mutableDict.allKeys) { + NSArray* currentPosition = [mutableDict objectForKey: existingCommand]; + if ([currentPosition[1] isEqualToString:oldFunctionName]) + [mutableDict setValue: [NSArray arrayWithObjects: @"MAIN", functionName, @"", @"file", nil] forKey: existingCommand]; + } + } + commandList = [mutableDict copy]; // back to non-mutable version +} + +// For customization: +// Add an entire plist file defining multiple commands. Commands follow the same syntax as initializeCommandList: +// +// key = command name, followed by an array of 4 components: +// 1st component: name of digital library (can be "MAIN" if command is defined inside program) +// 2nd component: name of function to be called +// 3rd component: chain sent to getopt (for arguments in autocomplete) +// 4th component: takes a file/directory as argument +// +// Example: +// rlogin +// +// libnetwork_ios.dylib +// rlogin_main +// 468EKLNS:X:acde:fFk:l:n:rs:uxy +// no +// +NSError* addCommandList(NSString* fileLocation) { + if (commandList == nil) initializeCommandList(); + NSError* error; + + NSURL *locationURL = [NSURL fileURLWithPath:fileLocation isDirectory:NO]; + if ([locationURL checkResourceIsReachableAndReturnError:&error] == NO) { + fprintf(stderr, "Resource dictionary %s not found", fileLocation.UTF8String); + return error; + } + + NSData* dataLoadedFromFile = [NSData dataWithContentsOfFile:fileLocation options:0 error:&error]; + if (!dataLoadedFromFile) return error; + NSDictionary* newCommandList = [NSPropertyListSerialization propertyListWithData:dataLoadedFromFile options:NSPropertyListImmutable format:NULL error:&error]; + if (!newCommandList) return error; + // merge the two dictionaries: + NSMutableDictionary *mutableDict = [commandList mutableCopy]; + [mutableDict addEntriesFromDictionary:newCommandList]; + commandList = [mutableDict copy]; + return NULL; +} + + +NSString* commandsAsString() { + + if (commandList == nil) initializeCommandList(); + + NSError * err; + NSData * jsonData = [NSJSONSerialization dataWithJSONObject:commandList.allKeys options:0 error:&err]; + NSString * myString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + + return myString; +} + +NSArray* commandsAsArray() { + if (commandList == nil) initializeCommandList(); + return commandList.allKeys; +} + +// for output file names, arguments: returns a pointer to +// immediately after the end of the argument, or NULL. +// Method: +// - if argument begins with ", go to next unescaped " +// - if argument begins with ', go to next unescaped ' +// - otherwise, move to next unescaped space +// +// Must be combined with another function to remove backslash. + +// Aux function: +static void* nextUnescapedCharacter(const char* str, const char c) { + char* nextOccurence = strchr(str, c); + while (nextOccurence != NULL) { + if ((nextOccurence > str + 1) && (*(nextOccurence - 1) == '\\')) { + // There is a backlash before the character. + int numBackslash = 0; + char* countBack = nextOccurence - 1; + while ((countBack > str) && (*countBack == '\\')) { numBackslash++; countBack--; } + if (numBackslash % 2 == 0) return nextOccurence; // even number of backslash + } else return nextOccurence; + nextOccurence = strchr(nextOccurence + 1, c); + } + return nextOccurence; +} + +static char* getLastCharacterOfArgument(const char* argument) { + if (strlen(argument) == 0) return NULL; // be safe + if (argument[0] == '"') { + char* endquote = nextUnescapedCharacter(argument + 1, '"'); + if (endquote != NULL) return endquote + 1; + else return NULL; + } else if (argument[0] == '\'') { + char* endquote = nextUnescapedCharacter(argument + 1, '\''); + if (endquote != NULL) return endquote + 1; + else return NULL; + } + else return nextUnescapedCharacter(argument + 1, ' '); +} + +// remove quotes at the beginning of argument if there's a balancing one at the end +static char* unquoteArgument(char* argument) { + if (argument[0] == '"') { + if (argument[strlen(argument) - 1] == '"') { + argument[strlen(argument) - 1] = 0x0; + return argument + 1; + } + } + if (argument[0] == '\'') { + if (argument[strlen(argument) - 1] == '\'') { + argument[strlen(argument) - 1] = 0x0; + return argument + 1; + } + } + // no quotes at the beginning: replace all escaped characters: + // '\x' -> x + char* nextOccurence = strchr(argument, '\\'); + while ((nextOccurence != NULL) && (strlen(nextOccurence) > 0)) { + memmove(nextOccurence, nextOccurence + 1, strlen(nextOccurence + 1) + 1); + // strcpy(nextOccurence, nextOccurence + 1); + nextOccurence = strchr(nextOccurence + 1, '\\'); + } + return argument; +} + + +int ios_system(const char* inputCmd) { + char* command; + // The names of the files for stdin, stdout, stderr + char* inputFileName = 0; + char* outputFileName = 0; + char* errorFileName = 0; + // Where the symbols "<", ">" or "2>" were. + // to be replaced by 0x0 later. + char* outputFileMarker = 0; + char* inputFileMarker = 0; + char* errorFileMarker = 0; + char* scriptName = 0; // interpreted commands + bool sharedErrorOutput = false; + NSFileManager *fileManager = [[NSFileManager alloc] init]; + // NSLog(@"command= %s\n", inputCmd); + // NSLog(@"ios_system, stdout %d \n", thread_stdout == NULL ? 0 : fileno(thread_stdout)); + // NSLog(@"ios_system, stderr %d \n", thread_stderr == NULL ? 0 : fileno(thread_stderr)); + if (currentSession == NULL) { + currentSession = malloc(sizeof(sessionParameters)); + initSessionParameters(currentSession); + } + currentSession->global_errno = 0; + + // initialize: + if (thread_stdin == 0) thread_stdin = currentSession->stdin; + if (thread_stdout == 0) thread_stdout = currentSession->stdout; + if (thread_stderr == 0) thread_stderr = currentSession->stderr; + if (thread_context == 0) thread_context = currentSession->context; + + char* cmd = strdup(inputCmd); + char* maxPointer = cmd + strlen(cmd); + char* originalCommand = cmd; + // fprintf(thread_stderr, "Command sent: %s \n", cmd); fflush(stderr); + if (cmd[0] == '"') { + // Command was enclosed in quotes (almost always with Vim) + char* endCmd = strstrquoted(cmd + 1, "\""); // find closing quote + if (endCmd) { + cmd = cmd + 1; // remove starting quote + endCmd[0] = 0x0; + assert(endCmd < maxPointer); + } + // assert(cmd + strlen(cmd) < maxPointer); + } + if (cmd[0] == '(') { + // Standard vim encoding: command between parentheses + command = cmd + 1; + char* endCmd = strstrquoted(command, ")"); // remove closing parenthesis + if (endCmd) { + endCmd[0] = 0x0; + assert(endCmd < maxPointer); + inputFileMarker = endCmd + 1; + } + } else command = cmd; + // fprintf(thread_stderr, "Command sent: %s \n", command); + // Search for input, output and error redirection + // They can be in any order, although the usual are: + // command < input > output 2> error, command < input > output 2>&1 or command < input >& output + // The last two are equivalent. Vim prefers the second. + // Search for input file "< " and output file " >" + if (!inputFileMarker) inputFileMarker = command; + outputFileMarker = inputFileMarker; + functionParameters *params = (functionParameters*) malloc(sizeof(functionParameters)); + // If child_streams have been defined (in dup2 or popen), the new thread takes them. + params->stdin = child_stdin; + params->stdout = child_stdout; + params->stderr = child_stderr; + params->session = currentSession; + + params->context = thread_context; + + child_stdin = child_stdout = child_stderr = NULL; + params->argc = 0; params->argv = 0; params->argv_ref = 0; + params->function = NULL; params->isPipeOut = false; params->isPipeErr = false; + // scan until first "<" (input file) + inputFileMarker = strstrquoted(inputFileMarker, "<"); + // scan until first non-space character: + if (inputFileMarker) { + inputFileName = inputFileMarker + 1; // skip past '<' + // skip past all spaces + while ((inputFileName[0] == ' ') && strlen(inputFileName) > 0) inputFileName++; + } + // is there a pipe ("|", "&|" or "|&") + // We assume here a logical command order: < before pipe, pipe before >. + // TODO: check what happens for unlogical commands. Refuse them, but gently. + // TODO: implement tee, because that has been removed + char* pipeMarker = strstrquoted(outputFileMarker,"&|"); + if (!pipeMarker) pipeMarker = strstrquoted(outputFileMarker,"|&"); // both seem to work + if (pipeMarker) { + bool pushMainThread = currentSession->isMainThread; + currentSession->isMainThread = false; + if (params->stdout != 0) thread_stdout = params->stdout; + if (params->stderr != 0) thread_stderr = params->stderr; + // if popen fails, don't start the command + params->stdout = ios_popen(pipeMarker+2, "w"); + params->stderr = params->stdout; + currentSession->isMainThread = pushMainThread; + pipeMarker[0] = 0x0; + sharedErrorOutput = true; + if (params->stdout == NULL) { // pipe open failed, return before we start a command + NSLog(@"Failed launching pipe for %s\n", pipeMarker+2); + ios_storeThreadId(0); + free(params); + free(originalCommand); // releases cmd, which was a strdup of inputCommand + return currentSession->global_errno; + } + } else { + pipeMarker = strstrquoted(outputFileMarker,"|"); + if (pipeMarker) { + bool pushMainThread = currentSession->isMainThread; + currentSession->isMainThread = false; + if (params->stdout != 0) thread_stdout = params->stdout; + if (params->stderr != 0) thread_stderr = params->stderr; // ????? + // if popen fails, don't start the command + params->stdout = ios_popen(pipeMarker+1, "w"); + currentSession->isMainThread = pushMainThread; + pipeMarker[0] = 0x0; + if (params->stdout == NULL) { // pipe open failed, return before we start a command + NSLog(@"Failed launching pipe for %s\n", pipeMarker+1); + ios_storeThreadId(0); + free(params); + free(originalCommand); // releases cmd, which was a strdup of inputCommand + return currentSession->global_errno; + } + } + } + // We have removed the pipe part. Still need to parse the rest of the command + // Must scan in strstr by reverse order of inclusion. So "2>&1" before "2>" before ">" + errorFileMarker = strstrquoted(outputFileMarker,"&>"); // both stderr/stdout sent to same file + // output file name will be after "&>" + if (errorFileMarker) { outputFileName = errorFileMarker + 2; outputFileMarker = errorFileMarker; } + if (!errorFileMarker) { + // TODO: 2>&1 before > means redirect stderr to (current) stdout, then redirects stdout + // ...except with a pipe. + // Currently, we don't check for that. + errorFileMarker = strstrquoted(outputFileMarker,"2>&1"); // both stderr/stdout sent to same file + if (errorFileMarker) { + if (params->stdout) params->stderr = params->stdout; + outputFileMarker = strstrquoted(outputFileMarker, ">"); + if (outputFileMarker) outputFileName = outputFileMarker + 1; // skip past '>' + } + } + if (errorFileMarker) { sharedErrorOutput = true; } + else { + // specific name for error file? + errorFileMarker = strstrquoted(outputFileMarker,"2>"); + if (errorFileMarker) { + errorFileName = errorFileMarker + 2; // skip past "2>" + // skip past all spaces: + while ((errorFileName[0] == ' ') && strlen(errorFileName) > 0) errorFileName++; + } + } + // scan until first ">" + if (!sharedErrorOutput) { + outputFileMarker = strstrquoted(outputFileMarker, ">"); + if (outputFileMarker) outputFileName = outputFileMarker + 1; // skip past '>' + } else { + outputFileMarker = NULL; + } + if (outputFileName) { + while ((outputFileName[0] == ' ') && strlen(outputFileName) > 0) outputFileName++; + } + if (errorFileName && (outputFileName == errorFileName)) { + // we got the same ">" twice, pick the next one ("2>" was before ">") + outputFileMarker = errorFileName; + outputFileMarker = strstrquoted(outputFileMarker, ">"); + if (outputFileMarker) { + outputFileName = outputFileMarker + 1; // skip past '>' + while ((outputFileName[0] == ' ') && strlen(outputFileName) > 0) outputFileName++; + } else outputFileName = NULL; // Only "2>", but no ">". It happens. + } + if (outputFileName) { + char* endFile = getLastCharacterOfArgument(outputFileName); + if (endFile) endFile[0] = 0x00; // end output file name at first space + assert(endFile <= maxPointer); + } + if (inputFileName) { + char* endFile = getLastCharacterOfArgument(inputFileName); + if (endFile) endFile[0] = 0x00; // end input file name at first space + assert(endFile <= maxPointer); + } + if (errorFileName) { + char* endFile = getLastCharacterOfArgument(errorFileName); + if (endFile) endFile[0] = 0x00; // end error file name at first space + assert(endFile <= maxPointer); + } + // insert chain termination elements at the beginning of each filename. + // Must be done after the parsing. + if (inputFileMarker) inputFileMarker[0] = 0x0; + // There was a test " && (params->stdout == NULL)" below. Why? + if (outputFileMarker) outputFileMarker[0] = 0x0; // There + if (errorFileMarker) errorFileMarker[0] = 0x0; + // strip filenames of quotes, if any: + if (outputFileName) outputFileName = unquoteArgument(outputFileName); + if (inputFileName) inputFileName = unquoteArgument(inputFileName); + if (errorFileName) errorFileName = unquoteArgument(errorFileName); + // + FILE* newStream; + if (inputFileName) { + newStream = fopen(inputFileName, "r"); + if (newStream) params->stdin = newStream; + } + if (params->stdin == NULL) params->stdin = thread_stdin; + if (outputFileName) { + newStream = fopen(outputFileName, "w"); + if (newStream) { + if (params->stdout != NULL) { + if (fileno(params->stdout) != fileno(currentSession->stdout)) fclose(params->stdout); + } + params->stdout = newStream; + } + } + if (params->stdout == NULL) params->stdout = thread_stdout; + if (sharedErrorOutput && (params->stderr != params->stdout)) { + if (params->stderr != NULL) { + if (fileno(params->stderr) != fileno(currentSession->stderr)) fclose(params->stderr); + } + params->stderr = params->stdout; + } + else if (errorFileName) { + newStream = NULL; + newStream = fopen(errorFileName, "w"); + if (newStream) { + if (params->stderr != NULL) { + if (fileno(params->stderr) != fileno(currentSession->stderr)) fclose(params->stderr); + } + params->stderr = newStream; + } + } + if (params->stderr == NULL) params->stderr = thread_stderr; + int argc = 0; + size_t numSpaces = 0; + // the number of arguments is *at most* the number of spaces plus one + char* str = command; + while(*str) if (*str++ == ' ') ++numSpaces; + char** argv = (char **)malloc(sizeof(char*) * (numSpaces + 2)); + bool* dontExpand = malloc(sizeof(bool) * (numSpaces + 2)); + // n spaces = n+1 arguments, plus null at the end + str = command; + while (*str) { + argv[argc] = str; + dontExpand[argc] = false; + argc += 1; + char* end = getLastCharacterOfArgument(str); + bool mustBreak = (end == NULL) || (strlen(end) == 0); + if (!mustBreak) end[0] = 0x0; + if ((str[0] == '\'') || (str[0] == '"')) { + dontExpand[argc-1] = true; // don't expand arguments in quotes + } + argv[argc-1] = unquoteArgument(argv[argc-1]); + if (mustBreak) break; + str = end + 1; + assert(argc < numSpaces + 2); + while (str && (str[0] == ' ')) str++; // skip multiple spaces + } + argv[argc] = NULL; + if (argc != 0) { + // So far, all arguments are pointers into originalCommand. + // We need to change them (environment variables expansion, ~ expansion, etc) + // Duplicate everything so we can realloc: + char** argv_copy = (char **)malloc(sizeof(char*) * (argc + 1)); + for (int i = 0; i < argc; i++) argv_copy[i] = strdup(argv[i]); + argv_copy[argc] = NULL; + free(argv); + argv = argv_copy; + // We have the arguments. Parse them for environment variables, ~, etc. + for (int i = 1; i < argc; i++) if (!dontExpand[i]) { argv[i] = parseArgument(argv[i], argv[0]); } + // wildcard expansion (*, ?, []...) Has to be after $ and ~ expansion, results in larger arguments + for (int i = 1; i < argc; i++) if (!dontExpand[i]) { + if (strstrquoted (argv[i],"*") || strstrquoted (argv[i],"?") || strstrquoted (argv[i],"[")) { + glob_t gt; + if (glob(argv[i], 0, NULL, >) == 0) { + argc += gt.gl_matchc - 1; + argv = (char **)realloc(argv, sizeof(char*) * (argc + 1)); + dontExpand = (bool *)realloc(dontExpand, sizeof(bool) * (argc + 1)); + // Move everything after i by gt.gl_matchc - 1 steps up: + for (int j = argc; j - gt.gl_matchc + 1 > i; j--) { + argv[j] = argv[j - gt.gl_matchc + 1]; + dontExpand[j] = dontExpand[j - gt.gl_matchc + 1]; + } + for (int j = 0; j < gt.gl_matchc; j++) { + argv[i + j] = strdup(gt.gl_pathv[j]); + } + i += gt.gl_matchc - 1; + globfree(>); + } else { + // If there is no match, leave parameter as is, continue with command. + // Not exactly Unix behaviour, but more convenient on Phones. + fprintf(params->stderr, "%s: %s: No match\n", argv[0], argv[i]); + fflush(params->stderr); + globfree(>); + } + } + } + free(dontExpand); + // Now call the actual command: + // - is argv[0] a command that refers to a file? (either absolute path, or in $PATH) + // if so, does it exist, does it have +x bit set, does it have #! python or #! lua on the first line? + // if yes to all, call the relevant interpreter. Works for hg, for example. + if (argv[0][0] == '\\') { + // Just remove the \ at the beginning + // There can be several versions of a command (e.g. ls as precompiled and ls written in Python) + // The executable file has precedence, unless the user has specified they want the original + // version, by prefixing it with \. So "\ls" == always "our" ls. "ls" == maybe ~/Library/bin/ls + // (if it exists). + size_t len_with_terminator = strlen(argv[0] + 1) + 1; + memmove(argv[0], argv[0] + 1, len_with_terminator); + } else { + NSString* commandName = [NSString stringWithCString:argv[0] encoding:NSUTF8StringEncoding]; + strcpy(currentSession->commandName, argv[0]); + BOOL isDir = false; + bool cmdIsAFile = false; + bool cmdIsAPath = false; + if ([commandName hasPrefix:@"~/"]) { + NSString* replacement_string = [NSString stringWithCString:(getenv("HOME")) encoding:NSUTF8StringEncoding]; + NSString* test_string = @"~"; + commandName = [commandName stringByReplacingOccurrencesOfString:test_string withString:replacement_string options:NULL range:NSMakeRange(0, 1)]; + } + if ([fileManager fileExistsAtPath:commandName isDirectory:&isDir] && (!isDir)) { + // File exists, is a file. + struct stat sb; + if (stat(commandName.UTF8String, &sb) == 0) { + // File exists, is executable, not a directory. + cmdIsAFile = true; + } + } + // if commandName contains "/", then it's a path, and we don't search for it in PATH. + cmdIsAPath = ([commandName rangeOfString:@"/"].location != NSNotFound) && !cmdIsAFile; + if (!cmdIsAPath) { + // We go through the path, because that command may be a file in the path + NSString* checkingPath = [NSString stringWithCString:getenv("PATH") encoding:NSUTF8StringEncoding]; + if (! [fullCommandPath isEqualToString:checkingPath]) { + fullCommandPath = checkingPath; + directoriesInPath = [fullCommandPath componentsSeparatedByString:@":"]; + } + for (NSString* path in directoriesInPath) { + // If we don't have access to the path component, there's no point in continuing: + if (![fileManager fileExistsAtPath:path isDirectory:&isDir]) continue; + if (!isDir) continue; // same in the (unlikely) event the path component is not a directory + NSString* locationName; + if (!cmdIsAFile) { + // search for 3 possibilities: name, name.bc and name.ll + locationName = [path stringByAppendingPathComponent:commandName]; + bool fileFound = [fileManager fileExistsAtPath:locationName isDirectory:&isDir]; + if (fileFound && isDir) continue; // file exists, but is a directory + if (!fileFound) { + locationName = [[path stringByAppendingPathComponent:commandName] stringByAppendingString:@".bc"]; + fileFound = [fileManager fileExistsAtPath:locationName isDirectory:&isDir]; + if (fileFound && isDir) continue; // file exists, but is a directory + } + if (!fileFound) { + locationName = [[path stringByAppendingPathComponent:commandName] stringByAppendingString:@".ll"]; + fileFound = [fileManager fileExistsAtPath:locationName isDirectory:&isDir]; + if (fileFound && isDir) continue; // file exists, but is a directory + } + if (!fileFound) { + locationName = [[path stringByAppendingPathComponent:commandName] stringByAppendingString:@".wasm"]; + fileFound = [fileManager fileExistsAtPath:locationName isDirectory:&isDir]; + if (fileFound && isDir) continue; // file exists, but is a directory + } + if (!fileFound) continue; + // isExecutableFileAtPath replies "NO" even if file has x-bit set. + // if (![fileManager isExecutableFileAtPath:cmdname]) continue; + struct stat sb; + // Files inside the Application Bundle will always have "x" removed. Don't check. + if (!([path containsString: [[NSBundle mainBundle] resourcePath]]) // Not inside the App Bundle + && !((stat(locationName.UTF8String, &sb) == 0))) // file exists, is not a directory + continue; + } else + // if (cmdIsAFile) we are now ready to execute this file: + locationName = commandName; + if (([locationName hasSuffix:@".bc"]) || ([locationName hasSuffix:@".ll"])) { + // CLANG bitcode. insert lli in front of argument list: + argc += 1; + argv = (char **)realloc(argv, sizeof(char*) * argc); + // Move everything one step up + for (int i = argc; i >= 1; i--) { argv[i] = argv[i-1]; } + argv[1] = realloc(argv[1], locationName.length + 1); + strcpy(argv[1], locationName.UTF8String); + argv[0] = strdup("lli"); // this argument is new + break; + } else if ([locationName hasSuffix:@".wasm"]) { + // insert wasm in front of argument list: + argc += 1; + argv = (char **)realloc(argv, sizeof(char*) * argc); + // Move everything one step up + for (int i = argc; i >= 1; i--) { argv[i] = argv[i-1]; } + argv[1] = realloc(argv[1], locationName.length + 1); + strcpy(argv[1], locationName.UTF8String); + argv[0] = strdup("wasm"); // this argument is new + break; + } else { + NSData *data = [NSData dataWithContentsOfFile:locationName]; + NSString *fileContent = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; + NSRange firstLineRange = [fileContent rangeOfString:@"\n"]; + if (firstLineRange.location == NSNotFound) firstLineRange.location = 0; + firstLineRange.length = firstLineRange.location; + firstLineRange.location = 0; + NSString* firstLine = [fileContent substringWithRange:firstLineRange]; + if ([firstLine hasPrefix:@"#!"]) { + // So long as the 1st line begins with "#!" and contains "python" we accept it as a python script + // "#! /usr/bin/python", "#! /usr/local/bin/python" and "#! /usr/bin/myStrangePath/python" are all OK. + // We also accept "#! /usr/bin/env python" because it is used. + // executable scripts files. Python and lua: + // 1) get script language name + if ([firstLine containsString:@"python3"]) { + scriptName = "python3"; + } else if ([firstLine containsString:@"python2"]) { + scriptName = "python"; + } else if ([firstLine containsString:@"python"]) { + // the default for python is now python3. + scriptName = "python3"; + } else if ([firstLine containsString:@"texlua"]) { + scriptName = "texlua"; + } else if ([firstLine containsString:@"lua"]) { + scriptName = "lua"; + } + if (scriptName) { + // 2) insert script language at beginning of argument list + argc += 1; + argv = (char **)realloc(argv, sizeof(char*) * (argc + 1)); + // Move everything one step up + for (int i = argc; i >= 1; i--) { argv[i] = argv[i-1]; } + argv[1] = realloc(argv[1], locationName.length + 1); + strcpy(argv[1], locationName.UTF8String); + argv[0] = strdup(scriptName); // this one is new + break; + } + } + } + if (cmdIsAFile) break; // else keep going through the path elements. + } + } else { + if (!cmdIsAFile) { + // argv[0] is a file that doesn't exist. Probably one of our commands. + // Replace with its name: + char* newName = basename(argv[0]); + argv[0] = realloc(argv[0], strlen(newName) + 1); + strcpy(argv[0], newName); + } + } + } + // NSLog(@"After command parsing, stdout %d stderr %d \n", fileno(params->stdout), fileno(params->stderr)); + // fprintf(thread_stderr, "Command after parsing: "); + // for (int i = 0; i < argc; i++) + // fprintf(thread_stderr, "[%s] ", argv[i]); + // We've reached this point: either the command is a file, from a script we support, + // and we have inserted the name of the script at the beginning, or it is a builtin command + int (*function)(int ac, char** av) = NULL; + if (commandList == nil) initializeCommandList(); + NSString* commandName = [NSString stringWithCString:argv[0] encoding:NSUTF8StringEncoding]; + // Make sure python3 and python2 coexist peacefully: + if ([commandName isEqualToString: @"python3"]) setenv("PYTHONEXECUTABLE", "python3", 1); + else if ([commandName isEqualToString: @"python2"]) setenv("PYTHONEXECUTABLE", "python", 1); + else if ([commandName isEqualToString: @"python"]) setenv("PYTHONEXECUTABLE", "python", 1); + // Ability to start multiple python3 scripts (required for Jupyter notebooks): + if ([commandName isEqualToString: @"python3"]) { + // start by increasing the number of the interpreter, until we're out. + int numInterpreter = 0; + if (currentPythonInterpreter < numPythonInterpreters) { + numInterpreter = currentPythonInterpreter; + currentPythonInterpreter++; + } else { + while (numInterpreter < numPythonInterpreters) { + if (PythonIsRunning[numInterpreter] == false) break; + numInterpreter++; + } + if (numInterpreter >= numPythonInterpreters) { + NSLog(@"%@", @"Too many python scripts running simultaneously. Try closing some notebooks.\n"); + commandName = @"notAValidCommand"; + } + } + if ((numInterpreter >= 0) && (numInterpreter < numPythonInterpreters)) { + PythonIsRunning[numInterpreter] = true; + if (numInterpreter > 0) { + char suffix[2]; + suffix[0] = 'A' + (numInterpreter - 1); + suffix[1] = 0; + argv[0][6] = suffix[0]; + commandName = [@"python" stringByAppendingString: [NSString stringWithCString: suffix encoding:NSUTF8StringEncoding]]; + } + } + } + // + NSArray* commandStructure = [commandList objectForKey: commandName]; + void* handle = NULL; + if (commandStructure != nil) { + NSString* libraryName = commandStructure[0]; + if ([libraryName isEqualToString: @"SELF"]) handle = RTLD_SELF; // commands defined in ios_system.framework + else if ([libraryName isEqualToString: @"MAIN"]) handle = RTLD_MAIN_ONLY; // commands defined in main program + else handle = dlopen(libraryName.UTF8String, RTLD_LAZY | RTLD_GLOBAL); // commands defined in dynamic library + if (handle == NULL) { + NSLog(@"Failed loading %s from %s, cause = %s\n", commandName.UTF8String, libraryName.UTF8String, dlerror()); + if (sideLoading) fprintf(thread_stderr, "Failed loading %s from %s, cause = %s\n", commandName.UTF8String, libraryName.UTF8String, dlerror()); + NSString* fileLocation = [[NSBundle mainBundle] pathForResource:libraryName ofType:nil]; + } + NSString* functionName = commandStructure[1]; + function = dlsym(handle, functionName.UTF8String); + if (function == NULL) { + NSLog(@"Failed loading %s from %s, cause = %s\n", commandName.UTF8String, libraryName.UTF8String, dlerror()); + if (sideLoading) fprintf(thread_stderr, "Failed loading %s from %s, cause = %s\n", commandName.UTF8String, libraryName.UTF8String, dlerror()); + } + } + if (function) { + // We run the function in a thread because there are several + // points where we can exit from a shell function. + // Commands call pthread_exit instead of exit + // thread is attached, could also be un-attached + params->argc = argc; + params->argv = argv; + params->function = function; + params->dlHandle = handle; + params->isPipeOut = (params->stdout != thread_stdout); + params->isPipeErr = (params->stderr != thread_stderr) && (params->stderr != params->stdout); + // params->session = currentSession; + // Before starting, do we have enough file descriptors available? + int numFileDescriptorsOpen = 0; + for (int fd = 0; fd < limitFilesOpen.rlim_cur; fd++) { + errno = 0; + int flags = fcntl(fd, F_GETFD, 0); + if (flags == -1 && errno) { + continue; + } + ++numFileDescriptorsOpen ; + } + // fprintf(stderr, "Num file descriptor = %d\n", numFileDescriptorsOpen); + // We assume 128 file descriptors will be enough for a single command. + if (numFileDescriptorsOpen + 128 > limitFilesOpen.rlim_cur) { + limitFilesOpen.rlim_cur += 1024; + int res = setrlimit(RLIMIT_NOFILE, &limitFilesOpen); + if (res == 0) NSLog(@"[Info] Increased file descriptor limit to = %llu\n", limitFilesOpen.rlim_cur); + else NSLog(@"[Warning] Failed to increased file descriptor limit to = %llu\n", limitFilesOpen.rlim_cur); + } + if (currentSession->isMainThread) { + bool commandOperatesOnFiles = ([commandStructure[3] isEqualToString:@"file"] || + [commandStructure[3] isEqualToString:@"directory"] || + params->isPipeOut || params->isPipeErr); + NSString* currentPath = [fileManager currentDirectoryPath]; + commandOperatesOnFiles &= (currentPath != nil); + if (commandOperatesOnFiles) { + // Send a signal to the system that we're going to change the current directory: + // TODO: only do this if the command actually accesses files: either outputFile exists, + // or errorFile exists, or the command uses files. + NSURL* currentURL = [NSURL fileURLWithPath:currentPath]; + NSFileCoordinator *fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil]; + [fileCoordinator coordinateWritingItemAtURL:currentURL options:0 error:NULL byAccessor:^(NSURL *currentURL) { + currentSession->isMainThread = false; + volatile pthread_t _tid = NULL; + pthread_create(&_tid, NULL, run_function, params); + while (_tid == NULL) { } + // ios_storeThreadId(_tid); + currentSession->current_command_root_thread = _tid; + // Wait for this process to finish: + if (joinMainThread) { + pthread_join(_tid, NULL); + // If there are auxiliary process, also wait for them: + if (currentSession->lastThreadId > 0) pthread_join(currentSession->lastThreadId, NULL); + currentSession->lastThreadId = 0; + currentSession->current_command_root_thread = 0; + } else { + pthread_detach(_tid); // a thread must be either joined or detached + } + currentSession->isMainThread = true; + }]; + } else { + currentSession->isMainThread = false; + volatile pthread_t _tid = NULL; + pthread_create(&_tid, NULL, run_function, params); + while (_tid == NULL) { } + // ios_storeThreadId(_tid); + currentSession->current_command_root_thread = _tid; + // Wait for this process to finish: + if (joinMainThread) { + pthread_join(_tid, NULL); + // If there are auxiliary process, also wait for them: + if (currentSession->lastThreadId > 0) pthread_join(currentSession->lastThreadId, NULL); + currentSession->lastThreadId = 0; + currentSession->current_command_root_thread = 0; + } else { + pthread_detach(_tid); // a thread must be either joined or detached + } + currentSession->isMainThread = true; + } + } else { + // NSLog(@"Starting command %s, global_errno= %d\n", command, currentSession->global_errno); + // Don't send signal if not in main thread. Also, don't join threads. + volatile pthread_t _tid_local = NULL; + pthread_create(&_tid_local, NULL, run_function, params); + // The last command on the command line (with multiple pipes) will be created first + while (_tid_local == NULL) { }; // Wait until thread has actually started + // fprintf(stderr, "Started thread = %x\n", _tid_local); + if (currentSession->lastThreadId == 0) currentSession->lastThreadId = _tid_local; // will be joined later + else pthread_detach(_tid_local); // a thread must be either joined or detached. + } + } else { + fprintf(params->stderr, "%s: command not found\n", argv[0]); + // NSLog(@"%s: command not found\n", argv[0]); + free(argv); + // If command output was redirected to a pipe, we still need to close it. + // (to warn the other command that it can stop waiting) + // We still need this step because there can be multiple pipes. + if (params->stdout != currentSession->stdout) { + fclose(params->stdout); + } + if ((params->stderr != currentSession->stderr) && (params->stderr != params->stdout)) { + fclose(params->stderr); + } + if ((handle != NULL) && (handle != RTLD_SELF) + && (handle != RTLD_MAIN_ONLY) + && (handle != RTLD_DEFAULT) && (handle != RTLD_NEXT)) + dlclose(handle); + free(params); // This was malloc'ed in ios_system + ios_storeThreadId(0); + currentSession->global_errno = 127; + // TODO: this should also raise an exception, for python scripts + } // if (function) + } else { // argc != 0 + ios_storeThreadId(0); + free(argv); // argv is otherwise freed in cleanup_function + free(dontExpand); + free(params); + } + // NSLog(@"returning from ios_system, global_errno= %d\n", currentSession->global_errno); + free(originalCommand); // releases cmd, which was a strdup of inputCommand + fflush(thread_stdin); + fflush(thread_stdout); + fflush(thread_stderr); + return currentSession->global_errno; +} diff --git a/ios_system/Sources/ios_system/libc_replacement.c b/ios_system/Sources/ios_system/libc_replacement.c new file mode 100644 index 00000000..6846c397 --- /dev/null +++ b/ios_system/Sources/ios_system/libc_replacement.c @@ -0,0 +1,307 @@ +// +// libc_replacement.c +// ios_system +// +// Created by Nicolas Holzschuch on 30/04/2018. +// Copyright © 2018 Nicolas Holzschuch. All rights reserved. +// +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ios_error.h" +#undef write +#undef fwrite +#undef puts +#undef fputs +#undef fputc +#undef putw +#undef fflush + +int printf (const char *format, ...) { + va_list arg; + int done; + + va_start (arg, format); + done = vfprintf (thread_stdout, format, arg); + va_end (arg); + + return done; +} +int fprintf(FILE * restrict stream, const char * restrict format, ...) { + va_list arg; + int done; + + va_start (arg, format); + if (fileno(stream) == STDOUT_FILENO) { + if (thread_stdout == NULL) thread_stdout = stdout; + done = vfprintf (thread_stdout, format, arg); + } + else if (fileno(stream) == STDERR_FILENO) { + if (thread_stderr == NULL) thread_stderr = stderr; + done = vfprintf (thread_stderr, format, arg); + } + // iOS, debug: + // else if (fileno(stream) == STDERR_FILENO) done = vfprintf (stderr, format, arg); + else done = vfprintf (stream, format, arg); + va_end (arg); + + return done; +} +int scanf (const char *format, ...) { + int count; + va_list ap; + + if (thread_stderr == NULL) thread_stderr = stderr; + if (thread_stdout == NULL) thread_stdout = stdout; + if (thread_stdin == NULL) thread_stdin = stdin; + + fflush(thread_stdout); + fflush(thread_stderr); + va_start (ap, format); + count = vfscanf (thread_stdin, format, ap); + va_end (ap); + return (count); +} +int ios_fflush(FILE *stream) { + if (thread_stdout == NULL) thread_stdout = stdout; + if (thread_stderr == NULL) thread_stderr = stderr; + + if (fileno(stream) == STDOUT_FILENO) return fflush(thread_stdout); + if (fileno(stream) == STDERR_FILENO) return fflush(thread_stderr); + return fflush(stream); +} +ssize_t ios_write(int fildes, const void *buf, size_t nbyte) { + if (thread_stdout == NULL) thread_stdout = stdout; + if (thread_stderr == NULL) thread_stderr = stderr; + if (fildes == STDOUT_FILENO) return write(fileno(thread_stdout), buf, nbyte); + if (fildes == STDERR_FILENO) return write(fileno(thread_stderr), buf, nbyte); + return write(fildes, buf, nbyte); +} +size_t ios_fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream) { + if (thread_stdout == NULL) thread_stdout = stdout; + if (thread_stderr == NULL) thread_stderr = stderr; + if (fileno(stream) == STDOUT_FILENO) return fwrite(ptr, size, nitems, thread_stdout); + if (fileno(stream) == STDERR_FILENO) return fwrite(ptr, size, nitems, thread_stderr); + return fwrite(ptr, size, nitems, stream); +} +int ios_puts(const char *s) { + if (thread_stdout == NULL) thread_stdout = stdout; + // puts adds a newline at the end. + int returnValue = fputs(s, thread_stdout); + fputc('\n', thread_stdout); + return returnValue; +} +int ios_fputs(const char* s, FILE *stream) { + if (thread_stdout == NULL) thread_stdout = stdout; + if (thread_stderr == NULL) thread_stderr = stderr; + if (fileno(stream) == STDOUT_FILENO) return fputs(s, thread_stdout); + if (fileno(stream) == STDERR_FILENO) return fputs(s, thread_stderr); + return fputs(s, stream); +} +int ios_fputc(int c, FILE *stream) { + if (thread_stdout == NULL) thread_stdout = stdout; + if (thread_stderr == NULL) thread_stderr = stderr; + if (fileno(stream) == STDOUT_FILENO) return fputc(c, thread_stdout); + if (fileno(stream) == STDERR_FILENO) return fputc(c, thread_stderr); + return fputc(c, stream); +} + +#include + +int ios_putw(int w, FILE *stream) { + if (thread_stdout == NULL) thread_stdout = stdout; + if (thread_stderr == NULL) thread_stderr = stderr; + if (fileno(stream) == STDOUT_FILENO) return putw(w, thread_stdout); + if (fileno(stream) == STDERR_FILENO) return putw(w, thread_stderr); + return putw(w, stream); +} + +// Fake process IDs to go with fake forking: +// You will still need to edit your code to make sure you go through both branches. +#define IOS_MAX_THREADS 128 +static pthread_t thread_ids[IOS_MAX_THREADS]; +static int pid_overflow = 0; +static pid_t current_pid = 0; + +inline pthread_t ios_getThreadId(pid_t pid) { + if (pid >= IOS_MAX_THREADS) return NULL; + // return ios_getLastThreadId(); // previous behaviour + return thread_ids[pid]; +} + +// We do not recycle process ids too quickly to avoid collisions. + +static inline const pid_t ios_nextAvailablePid() { + if (!pid_overflow && (current_pid < IOS_MAX_THREADS - 1)) { + current_pid += 1; + thread_ids[current_pid] = -1; // Not yet started + return current_pid; + } + // We've already started more than IOS_MAX_THREADS threads. + if (!pid_overflow) current_pid = 0; // first time over the limit + pid_overflow = 1; + while (1) { + current_pid += 1; + if (current_pid >= IOS_MAX_THREADS) current_pid = 1; + pthread_t thread_pid = ios_getThreadId(current_pid); + if (thread_pid == 0) { + return current_pid; // already released + } + // Dangerous: if the process is already killed, this wil crash + /* + if (pthread_kill(thread_pid, 0) != 0) { + thread_ids[current_pid] = 0; + return current_pid; // not running anymore + } + */ + } +} + +inline void ios_storeThreadId(pthread_t thread) { + // To avoid issues when a command starts a command without forking, + // we only store thread IDs for the first thread of the "process". + // fprintf(stderr, "Storing thread %x to pid %d current value: %x\n", thread, current_pid, thread_ids[current_pid]); + if (thread_ids[current_pid] == -1) { + thread_ids[current_pid] = thread; + return; + } + // The fuck is this line doing here? + // if (pthread_kill(ios_getThreadId(current_pid), 0) != 0) thread_ids[current_pid] = thread; +} + +void ios_releaseThread(pthread_t thread) { + // TODO: this is inefficient. Replace with NSMutableArray? + // fprintf(stderr, "Releasing thread %x\n", thread); + for (int p = 0; p < IOS_MAX_THREADS; p++) { + if (thread_ids[p] == thread) { + // fprintf(stderr, "Found Id %x\n", p); + thread_ids[p] = 0; + return; + } + } + // fprintf(stderr, "Not found\n"); +} + + +void ios_releaseThreadId(pid_t pid) { + thread_ids[pid] = 0; +} + +pid_t ios_currentPid() { + return current_pid; +} + +pid_t fork(void) { return ios_nextAvailablePid(); } // increases current_pid by 1. +pid_t ios_fork(void) { return ios_nextAvailablePid(); } // increases current_pid by 1. +pid_t vfork(void) { return ios_nextAvailablePid(); } + +// simple replacement of waitpid for swift programs +// We use "optnone" to prevent optimization, otherwise the while loops never end. +__attribute__ ((optnone)) void ios_waitpid(pid_t pid) { + pthread_t threadToWaitFor; + // Old system: no explicit pid, just store last thread Id. + if ((pid == -1) || (pid == 0)) { + threadToWaitFor = ios_getLastThreadId(); + while (threadToWaitFor != 0) { + threadToWaitFor = ios_getLastThreadId(); + } + return; + } + // New system: thread Id is store with pid: + threadToWaitFor = ios_getThreadId(pid); + while (threadToWaitFor != 0) { + // -1: not started, >0 started, not finished, 0: finished + threadToWaitFor = ios_getThreadId(pid); + } + return; +} + +__attribute__ ((optnone)) pid_t waitpid(pid_t pid, int *stat_loc, int options) { + // pthread_join won't work, because the thread might have been detached. + // (and you can't re-join a detached thread). + // -1 = the call waits for any child process (not good yet) + // 0 = the call waits for any child process in the process group of the caller + + if (options && WNOHANG) { + // WNOHANG: just check that the process is still running: + pthread_t threadToWaitFor; + if ((pid == -1) || (pid == 0)) threadToWaitFor = ios_getLastThreadId(); + else threadToWaitFor = ios_getThreadId(pid); + if (threadToWaitFor != 0) // the process is still running + return 0; + else { + if (stat_loc) *stat_loc = W_EXITCODE(ios_getCommandStatus(), 0); + fflush(thread_stdout); + fflush(thread_stderr); + return pid; // was "-1". See man page and https://github.com/holzschu/ios_system/issues/89 + } + } else { + // Wait until the process is terminated: + ios_waitpid(pid); + if (stat_loc) *stat_loc = W_EXITCODE(ios_getCommandStatus(), 0); + return pid; + } +} + + + +// +void vwarn(const char *fmt, va_list args) +{ + if (thread_stderr == NULL) thread_stderr = stderr; + fputs(ios_progname(), thread_stderr); + if (fmt != NULL) + { + fputs(": ", thread_stderr); + vfprintf(thread_stderr, fmt, args); + } + fputs(": ", thread_stderr); + fputs(strerror(errno), thread_stderr); + putc('\n', thread_stderr); +} + +void vwarnx(const char *fmt, va_list args) +{ + if (thread_stderr == NULL) thread_stderr = stderr; + fputs(ios_progname(), thread_stderr); + fputs(": ", thread_stderr); + if (fmt != NULL) + vfprintf(thread_stderr, fmt, args); + putc('\n', thread_stderr); +} +// void err(int eval, const char *fmt, ...); +void err(int eval, const char *fmt, ...) { + va_list argptr; + va_start(argptr, fmt); + vwarn(fmt, argptr); + va_end(argptr); + ios_exit(eval); +} +// void errx(int eval, const char *fmt, ...); +void errx(int eval, const char *fmt, ...) { + va_list argptr; + va_start(argptr, fmt); + vwarnx(fmt, argptr); + va_end(argptr); + ios_exit(eval); +} +// void warn(const char *fmt, ...); +void warn(const char *fmt, ...) { + va_list argptr; + va_start(argptr, fmt); + vwarn(fmt, argptr); + va_end(argptr); +} +// void warnx(const char *fmt, ...); +void warnx(const char *fmt, ...) { + va_list argptr; + va_start(argptr, fmt); + vwarnx(fmt, argptr); + va_end(argptr); +} diff --git a/ios_system/Tests/LinuxMain.swift b/ios_system/Tests/LinuxMain.swift new file mode 100644 index 00000000..4c384941 --- /dev/null +++ b/ios_system/Tests/LinuxMain.swift @@ -0,0 +1,7 @@ +import XCTest + +import ios_systemTests + +var tests = [XCTestCaseEntry]() +tests += ios_systemTests.allTests() +XCTMain(tests) diff --git a/ios_system/Tests/ios_systemTests/XCTestManifests.swift b/ios_system/Tests/ios_systemTests/XCTestManifests.swift new file mode 100644 index 00000000..16765403 --- /dev/null +++ b/ios_system/Tests/ios_systemTests/XCTestManifests.swift @@ -0,0 +1,9 @@ +import XCTest + +#if !canImport(ObjectiveC) +public func allTests() -> [XCTestCaseEntry] { + return [ + testCase(ios_systemTests.allTests), + ] +} +#endif diff --git a/ios_system/Tests/ios_systemTests/ios_systemTests.swift b/ios_system/Tests/ios_systemTests/ios_systemTests.swift new file mode 100644 index 00000000..aa24433c --- /dev/null +++ b/ios_system/Tests/ios_systemTests/ios_systemTests.swift @@ -0,0 +1,15 @@ +import XCTest +@testable import ios_system + +final class ios_systemTests: XCTestCase { + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct + // results. + XCTAssertEqual(ios_system().text, "Hello, World!") + } + + static var allTests = [ + ("testExample", testExample), + ] +} diff --git a/ios_system.xcodeproj/project.pbxproj b/ios_system_before.xcodeproj/project.pbxproj similarity index 99% rename from ios_system.xcodeproj/project.pbxproj rename to ios_system_before.xcodeproj/project.pbxproj index 7588dd4d..ec21f72e 100644 --- a/ios_system.xcodeproj/project.pbxproj +++ b/ios_system_before.xcodeproj/project.pbxproj @@ -2469,7 +2469,7 @@ }; }; }; - buildConfigurationList = 223496A51FD5FC71007ED1A9 /* Build configuration list for PBXProject "ios_system" */; + buildConfigurationList = 223496A51FD5FC71007ED1A9 /* Build configuration list for PBXProject "ios_system_before" */; compatibilityVersion = "Xcode 8.0"; developmentRegion = en; hasScannedForEncodings = 0; @@ -3669,7 +3669,7 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 223496A51FD5FC71007ED1A9 /* Build configuration list for PBXProject "ios_system" */ = { + 223496A51FD5FC71007ED1A9 /* Build configuration list for PBXProject "ios_system_before" */ = { isa = XCConfigurationList; buildConfigurations = ( 223496B11FD5FC71007ED1A9 /* Debug */, diff --git a/ios_system.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios_system_before.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from ios_system.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to ios_system_before.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/ios_system_before.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios_system_before.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/ios_system_before.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/awk/Info.plist b/ios_system_before/Info.plist similarity index 100% rename from awk/Info.plist rename to ios_system_before/Info.plist diff --git a/ios_system_before/ios_system.h b/ios_system_before/ios_system.h new file mode 100644 index 00000000..bc187f5c --- /dev/null +++ b/ios_system_before/ios_system.h @@ -0,0 +1,67 @@ +// +// ios_system.h +// ios_system +// +// Created by Nicolas Holzschuch on 04/12/2017. +// Copyright © 2017 Nicolas Holzschuch. All rights reserved. +// + +#import +#import +#import + +//! Project version number for ios_system. +FOUNDATION_EXPORT double ios_systemVersionNumber; + +//! Project version string for ios_system. +FOUNDATION_EXPORT const unsigned char ios_systemVersionString[]; + +// Thread-local input and output streams +extern __thread FILE* thread_stdin; +extern __thread FILE* thread_stdout; +extern __thread FILE* thread_stderr; +extern __thread void* thread_context; +// set to true to have more commands available, more debugging information. +extern bool sideLoading; +// set to false to have the main thread run in detached mode (non blocking) +extern bool joinMainThread; + +extern int ios_executable(const char* inputCmd); // does this command exist? (executable file or builtin command) +extern int ios_system(const char* inputCmd); // execute this command (executable file or builtin command) +extern FILE *ios_popen(const char *command, const char *type); // Execute this command and pipe the result +extern int ios_kill(void); // kill the current running command +extern int ios_isatty(int fd); // test whether a file descriptor refers to a terminal +extern pthread_t ios_getLastThreadId(void); +extern pthread_t ios_getThreadId(pid_t pid); +extern void ios_storeThreadId(pthread_t thread); +extern void ios_releaseThread(pthread_t thread); +extern void ios_releaseThreadId(pid_t pid); +extern pid_t ios_currentPid(void); +extern int ios_getCommandStatus(void); +extern const char* ios_progname(void); +extern char * ios_getenv(const char *name); +extern pid_t ios_fork(void); +extern void ios_waitpid(pid_t pid); +extern void ios_signal(int signal); +void ios_setWindowSize(int width, int height, const void* sessionId); + +extern NSString* commandsAsString(void); +extern NSArray* commandsAsArray(void); // set of all commands, in an NSArrays +extern NSString* getoptString(NSString* command); +extern NSString* operatesOn(NSString* command); +extern void initializeEnvironment(void); +extern int ios_setMiniRoot(NSString*); // restricts operations to a certain hierarchy +extern int ios_setMiniRootURL(NSURL*); // restricts operations to a certain hierarchy +extern int ios_setAllowedPaths(NSArray *paths); // restricts operations to a certain hierarchy +extern void ios_switchSession(const void* sessionid); +extern void ios_closeSession(const void* sessionid); +extern void ios_setStreams(FILE* _stdin, FILE* _stdout, FILE* _stderr); +extern void ios_settty(FILE* _tty); +extern int ios_gettty(void); +extern void ios_setContext(const void *context); +extern void* ios_getContext(void); +extern void ios_setDirectoryURL(NSURL* workingDirectoryURL); +extern void replaceCommand(NSString* commandName, NSString* functionName, bool allOccurences); +extern NSError* addCommandList(NSString* fileLocation); +extern int numPythonInterpreters; +extern int cd_main(int argc, char** argv); diff --git a/network_cmds-543/.gitignore b/network_cmds-543/.gitignore new file mode 100644 index 00000000..919696ea --- /dev/null +++ b/network_cmds-543/.gitignore @@ -0,0 +1,11 @@ +.DS_Store +*.xcodeproj/project.xcworkspace +*.xcodeproj/xcuserdata +.svn +build +*~.m +*~.c +*~.h +cscope.* +tags +TAGS diff --git a/network_cmds-543/APPLE_LICENSE b/network_cmds-543/APPLE_LICENSE new file mode 100644 index 00000000..e7aa7d07 --- /dev/null +++ b/network_cmds-543/APPLE_LICENSE @@ -0,0 +1,370 @@ + APPLE PUBLIC SOURCE LICENSE + Version 1.0 - March 16, 1999 + +Please read this License carefully before downloading this software. +By downloading and using this software, you are agreeing to be bound +by the terms of this License. If you do not or cannot agree to the +terms of this License, please do not download or use the software. + +1. General; Definitions. This License applies to any program or other + work which Apple Computer, Inc. ("Apple") publicly announces as + subject to this Apple Public Source License and which contains a + notice placed by Apple identifying such program or work as "Original + Code" and stating that it is subject to the terms of this Apple + Public Source License version 1.0 (or subsequent version thereof), + as it may be revised from time to time by Apple ("License"). As + used in this License: + +1.1 "Applicable Patents" mean: (a) in the case where Apple is the + grantor of rights, (i) patents or patent applications that are now + or hereafter acquired, owned by or assigned to Apple and (ii) whose + claims cover subject matter contained in the Original Code, but only + to the extent necessary to use, reproduce and/or distribute the + Original Code without infringement; and (b) in the case where You + are the grantor of rights, (i) patents and patent applications that + are now or hereafter acquired, owned by or assigned to You and (ii) + whose claims cover subject matter in Your Modifications, taken alone + or in combination with Original Code. + +1.2 "Covered Code" means the Original Code, Modifications, the + combination of Original Code and any Modifications, and/or any + respective portions thereof. + +1.3 "Deploy" means to use, sublicense or distribute Covered Code other + than for Your internal research and development (R&D), and includes + without limitation, any and all internal use or distribution of + Covered Code within Your business or organization except for R&D + use, as well as direct or indirect sublicensing or distribution of + Covered Code by You to any third party in any form or manner. + +1.4 "Larger Work" means a work which combines Covered Code or portions + thereof with code not governed by the terms of this License. + +1.5 "Modifications" mean any addition to, deletion from, and/or change + to, the substance and/or structure of Covered Code. When code is + released as a series of files, a Modification is: (a) any addition + to or deletion from the contents of a file containing Covered Code; + and/or (b) any new file or other representation of computer program + statements that contains any part of Covered Code. + +1.6 "Original Code" means the Source Code of a program or other work + as originally made available by Apple under this License, including + the Source Code of any updates or upgrades to such programs or works + made available by Apple under this License, and that has been + expressly identified by Apple as such in the header file(s) of such + work. + +1.7 "Source Code" means the human readable form of a program or other + work that is suitable for making modifications to it, including all + modules it contains, plus any associated interface definition files, + scripts used to control compilation and installation of an + executable (object code). + +1.8 "You" or "Your" means an individual or a legal entity exercising + rights under this License. For legal entities, "You" or "Your" + includes any entity which controls, is controlled by, or is under + common control with, You, where "control" means (a) the power, + direct or indirect, to cause the direction or management of such + entity, whether by contract or otherwise, or (b) ownership of fifty + percent (50%) or more of the outstanding shares or beneficial + ownership of such entity. + +2. Permitted Uses; Conditions & Restrictions. Subject to the terms + and conditions of this License, Apple hereby grants You, effective + on the date You accept this License and download the Original Code, + a world-wide, royalty-free, non-exclusive license, to the extent of + Apple's Applicable Patents and copyrights covering the Original + Code, to do the following: + +2.1 You may use, copy, modify and distribute Original Code, with or + without Modifications, solely for Your internal research and + development, provided that You must in each instance: + +(a) retain and reproduce in all copies of Original Code the copyright +and other proprietary notices and disclaimers of Apple as they appear +in the Original Code, and keep intact all notices in the Original Code +that refer to this License; + +(b) include a copy of this License with every copy of Source Code of +Covered Code and documentation You distribute, and You may not offer +or impose any terms on such Source Code that alter or restrict this +License or the recipients' rights hereunder, except as permitted under +Section 6; and + +(c) completely and accurately document all Modifications that you have +made and the date of each such Modification, designate the version of +the Original Code you used, prominently include a file carrying such +information with the Modifications, and duplicate the notice in +Exhibit A in each file of the Source Code of all such Modifications. + +2.2 You may Deploy Covered Code, provided that You must in each + instance: + +(a) satisfy all the conditions of Section 2.1 with respect to the +Source Code of the Covered Code; + +(b) make all Your Deployed Modifications publicly available in Source +Code form via electronic distribution (e.g. download from a web site) +under the terms of this License and subject to the license grants set +forth in Section 3 below, and any additional terms You may choose to +offer under Section 6. You must continue to make the Source Code of +Your Deployed Modifications available for as long as you Deploy the +Covered Code or twelve (12) months from the date of initial +Deployment, whichever is longer; + +(c) must notify Apple and other third parties of how to obtain Your +Deployed Modifications by filling out and submitting the required +information found at +http://www.apple.com/publicsource/modifications.html; and + +(d) if you Deploy Covered Code in object code, executable form only, +include a prominent notice, in the code itself as well as in related +documentation, stating that Source Code of the Covered Code is +available under the terms of this License with information on how and +where to obtain such Source Code. + +3. Your Grants. In consideration of, and as a condition to, the + licenses granted to You under this License: + +(a) You hereby grant to Apple and all third parties a non-exclusive, +royalty-free license, under Your Applicable Patents and other +intellectual property rights owned or controlled by You, to use, +reproduce, modify, distribute and Deploy Your Modifications of the +same scope and extent as Apple's licenses under Sections 2.1 and 2.2; +and + +(b) You hereby grant to Apple and its subsidiaries a non-exclusive, +worldwide, royalty-free, perpetual and irrevocable license, under Your +Applicable Patents and other intellectual property rights owned or +controlled by You, to use, reproduce, execute, compile, display, +perform, modify or have modified (for Apple and/or its subsidiaries), +sublicense and distribute Your Modifications, in any form, through +multiple tiers of distribution. + +4. Larger Works. You may create a Larger Work by combining Covered + Code with other code not governed by the terms of this License and + distribute the Larger Work as a single product. In each such + instance, You must make sure the requirements of this License are + fulfilled for the Covered Code or any portion thereof. + +5. Limitations on Patent License. Except as expressly stated in + Section 2, no other patent rights, express or implied, are granted + by Apple herein. Modifications and/or Larger Works may require + additional patent licenses from Apple which Apple may grant in its + sole discretion. + +6. Additional Terms. You may choose to offer, and to charge a fee + for, warranty, support, indemnity or liability obligations and/or + other rights consistent with the scope of the license granted herein + ("Additional Terms") to one or more recipients of Covered + Code. However, You may do so only on Your own behalf and as Your + sole responsibility, and not on behalf of Apple. You must obtain the + recipient's agreement that any such Additional Terms are offered by + You alone, and You hereby agree to indemnify, defend and hold Apple + harmless for any liability incurred by or claims asserted against + Apple by reason of any such Additional Terms. + +7. Versions of the License. Apple may publish revised and/or new + versions of this License from time to time. Each version will be + given a distinguishing version number. Once Original Code has been + published under a particular version of this License, You may + continue to use it under the terms of that version. You may also + choose to use such Original Code under the terms of any subsequent + version of this License published by Apple. No one other than Apple + has the right to modify the terms applicable to Covered Code created + under this License. + +8. NO WARRANTY OR SUPPORT. The Original Code may contain in whole or + in part pre-release, untested, or not fully tested works. The + Original Code may contain errors that could cause failures or loss + of data, and may be incomplete or contain inaccuracies. You + expressly acknowledge and agree that use of the Original Code, or + any portion thereof, is at Your sole and entire risk. THE ORIGINAL + CODE IS PROVIDED "AS IS" AND WITHOUT WARRANTY, UPGRADES OR SUPPORT + OF ANY KIND AND APPLE AND APPLE'S LICENSOR(S) (FOR THE PURPOSES OF + SECTIONS 8 AND 9, APPLE AND APPLE'S LICENSOR(S) ARE COLLECTIVELY + REFERRED TO AS "APPLE") EXPRESSLY DISCLAIM ALL WARRANTIES AND/OR + CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES AND/OR CONDITIONS OF MERCHANTABILITY OR + SATISFACTORY QUALITY AND FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT OF THIRD PARTY RIGHTS. APPLE DOES NOT WARRANT THAT + THE FUNCTIONS CONTAINED IN THE ORIGINAL CODE WILL MEET YOUR + REQUIREMENTS, OR THAT THE OPERATION OF THE ORIGINAL CODE WILL BE + UNINTERRUPTED OR ERROR-FREE, OR THAT DEFECTS IN THE ORIGINAL CODE + WILL BE CORRECTED. NO ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN + BY APPLE OR AN APPLE AUTHORIZED REPRESENTATIVE SHALL CREATE A + WARRANTY OR IN ANY WAY INCREASE THE SCOPE OF THIS WARRANTY. You + acknowledge that the Original Code is not intended for use in the + operation of nuclear facilities, aircraft navigation, communication + systems, or air traffic control machines in which case the failure + of the Original Code could lead to death, personal injury, or severe + physical or environmental damage. + +9. Liability. + +9.1 Infringement. If any of the Original Code becomes the subject of + a claim of infringement ("Affected Original Code"), Apple may, at + its sole discretion and option: (a) attempt to procure the rights + necessary for You to continue using the Affected Original Code; (b) + modify the Affected Original Code so that it is no longer + infringing; or (c) terminate Your rights to use the Affected + Original Code, effective immediately upon Apple's posting of a + notice to such effect on the Apple web site that is used for + implementation of this License. + +9.2 LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES SHALL APPLE BE + LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL + DAMAGES ARISING OUT OF OR RELATING TO THIS LICENSE OR YOUR USE OR + INABILITY TO USE THE ORIGINAL CODE, OR ANY PORTION THEREOF, WHETHER + UNDER A THEORY OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), + PRODUCTS LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF + THE POSSIBILITY OF SUCH DAMAGES AND NOTWITHSTANDING THE FAILURE OF + ESSENTIAL PURPOSE OF ANY REMEDY. In no event shall Apple's total + liability to You for all damages under this License exceed the + amount of fifty dollars ($50.00). + +10. Trademarks. This License does not grant any rights to use the + trademarks or trade names "Apple", "Apple Computer", "Mac OS X", + "Mac OS X Server" or any other trademarks or trade names belonging + to Apple (collectively "Apple Marks") and no Apple Marks may be + used to endorse or promote products derived from the Original Code + other than as permitted by and in strict compliance at all times + with Apple's third party trademark usage guidelines which are + posted at http://www.apple.com/legal/guidelinesfor3rdparties.html. + +11. Ownership. Apple retains all rights, title and interest in and to + the Original Code and any Modifications made by or on behalf of + Apple ("Apple Modifications"), and such Apple Modifications will + not be automatically subject to this License. Apple may, at its + sole discretion, choose to license such Apple Modifications under + this License, or on different terms from those contained in this + License or may choose not to license them at all. Apple's + development, use, reproduction, modification, sublicensing and + distribution of Covered Code will not be subject to this License. + +12. Termination. + +12.1 Termination. This License and the rights granted hereunder will + terminate: + +(a) automatically without notice from Apple if You fail to comply with +any term(s) of this License and fail to cure such breach within 30 +days of becoming aware of such breach; (b) immediately in the event of +the circumstances described in Sections 9.1 and/or 13.6(b); or (c) +automatically without notice from Apple if You, at any time during the +term of this License, commence an action for patent infringement +against Apple. + +12.2 Effect of Termination. Upon termination, You agree to + immediately stop any further use, reproduction, modification and + distribution of the Covered Code, or Affected Original Code in the + case of termination under Section 9.1, and to destroy all copies of + the Covered Code or Affected Original Code (in the case of + termination under Section 9.1) that are in your possession or + control. All sublicenses to the Covered Code which have been + properly granted prior to termination shall survive any termination + of this License. Provisions which, by their nature, should remain + in effect beyond the termination of this License shall survive, + including but not limited to Sections 3, 5, 8, 9, 10, 11, 12.2 and + 13. Neither party will be liable to the other for compensation, + indemnity or damages of any sort solely as a result of terminating + this License in accordance with its terms, and termination of this + License will be without prejudice to any other right or remedy of + either party. + +13. Miscellaneous. + +13.1 Export Law Assurances. You may not use or otherwise export or + re-export the Original Code except as authorized by United States + law and the laws of the jurisdiction in which the Original Code was + obtained. In particular, but without limitation, the Original Code + may not be exported or re-exported (a) into (or to a national or + resident of) any U.S. embargoed country or (b) to anyone on the + U.S. Treasury Department's list of Specially Designated Nationals + or the U.S. Department of Commerce's Table of Denial Orders. By + using the Original Code, You represent and warrant that You are not + located in, under control of, or a national or resident of any such + country or on any such list. + +13.2 Government End Users. The Covered Code is a "commercial item" as + defined in FAR 2.101. Government software and technical data + rights in the Covered Code include only those rights customarily + provided to the public as defined in this License. This customary + commercial license in technical data and software is provided in + accordance with FAR 12.211 (Technical Data) and 12.212 (Computer + Software) and, for Department of Defense purchases, DFAR + 252.227-7015 (Technical Data -- Commercial Items) and 227.7202-3 + (Rights in Commercial Computer Software or Computer Software + Documentation). Accordingly, all U.S. Government End Users acquire + Covered Code with only those rights set forth herein. + +13.3 Relationship of Parties. This License will not be construed as + creating an agency, partnership, joint venture or any other form of + legal association between You and Apple, and You will not represent + to the contrary, whether expressly, by implication, appearance or + otherwise. + +13.4 Independent Development. Nothing in this License will impair + Apple's right to acquire, license, develop, have others develop for + it, market and/or distribute technology or products that perform + the same or similar functions as, or otherwise compete with, + Modifications, Larger Works, technology or products that You may + develop, produce, market or distribute. + +13.5 Waiver; Construction. Failure by Apple to enforce any provision + of this License will not be deemed a waiver of future enforcement + of that or any other provision. Any law or regulation which + provides that the language of a contract shall be construed against + the drafter will not apply to this License. + +13.6 Severability. (a) If for any reason a court of competent + jurisdiction finds any provision of this License, or portion + thereof, to be unenforceable, that provision of the License will be + enforced to the maximum extent permissible so as to effect the + economic benefits and intent of the parties, and the remainder of + this License will continue in full force and effect. (b) + Notwithstanding the foregoing, if applicable law prohibits or + restricts You from fully and/or specifically complying with + Sections 2 and/or 3 or prevents the enforceability of either of + those Sections, this License will immediately terminate and You + must immediately discontinue any use of the Covered Code and + destroy all copies of it that are in your possession or control. + +13.7 Dispute Resolution. Any litigation or other dispute resolution + between You and Apple relating to this License shall take place in + the Northern District of California, and You and Apple hereby + consent to the personal jurisdiction of, and venue in, the state + and federal courts within that District with respect to this + License. The application of the United Nations Convention on + Contracts for the International Sale of Goods is expressly + excluded. + +13.8 Entire Agreement; Governing Law. This License constitutes the + entire agreement between the parties with respect to the subject + matter hereof. This License shall be governed by the laws of the + United States and the State of California, except that body of + California law concerning conflicts of law. + +Where You are located in the province of Quebec, Canada, the following +clause applies: The parties hereby confirm that they have requested +that this License and all related documents be drafted in English. Les +parties ont exige que le present contrat et tous les documents +connexes soient rediges en anglais. + +EXHIBIT A. + +"Portions Copyright (c) 1999 Apple Computer, Inc. All Rights +Reserved. This file contains Original Code and/or Modifications of +Original Code as defined in and that are subject to the Apple Public +Source License Version 1.0 (the 'License'). You may not use this file +except in compliance with the License. Please obtain a copy of the +License at http://www.apple.com/publicsource and read it before using +this file. + +The Original Code and all software distributed under the License are +distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the +License for the specific language governing rights and limitations +under the License." diff --git a/network_cmds-543/arp.tproj/IMPORT_NOTES b/network_cmds-543/arp.tproj/IMPORT_NOTES new file mode 100644 index 00000000..231c5cae --- /dev/null +++ b/network_cmds-543/arp.tproj/IMPORT_NOTES @@ -0,0 +1,3 @@ +arp.c - FreeBSD file, included types.h, socket.h, ethernet.h, removed + token ring header, removed __unused__ attributes, hid token ring code + with #ifndef __APPLE__ diff --git a/network_cmds-543/arp.tproj/arp.8 b/network_cmds-543/arp.tproj/arp.8 new file mode 100644 index 00000000..b586c267 --- /dev/null +++ b/network_cmds-543/arp.tproj/arp.8 @@ -0,0 +1,251 @@ +.\" Copyright (c) 2012 Apple Inc. All rights reserved. +.\" +.\" @APPLE_OSREFERENCE_LICENSE_HEADER_START@ +.\" +.\" This file contains Original Code and/or Modifications of Original Code +.\" as defined in and that are subject to the Apple Public Source License +.\" Version 2.0 (the 'License'). You may not use this file except in +.\" compliance with the License. The rights granted to you under the License +.\" may not be used to create, or enable the creation or redistribution of, +.\" unlawful or unlicensed copies of an Apple operating system, or to +.\" circumvent, violate, or enable the circumvention or violation of, any +.\" terms of an Apple operating system software license agreement. +.\" +.\" Please obtain a copy of the License at +.\" http://www.opensource.apple.com/apsl/ and read it before using this file. +.\" +.\" The Original Code and all software distributed under the License are +.\" distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +.\" EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +.\" INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +.\" FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +.\" Please see the License for the specific language governing rights and +.\" limitations under the License. +.\" +.\" @APPLE_OSREFERENCE_LICENSE_HEADER_END@ +.\" +.\" Copyright (c) 1985, 1991, 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. +.\" +.\" @(#)arp.8 8.1 (Berkeley) 6/6/93 +.\" $FreeBSD: src/usr.sbin/arp/arp.8,v 1.25.2.1 2008/04/25 16:38:14 sam Exp $ +.\" +.Dd March 18, 2008 +.Dt ARP 8 +.Os +.Sh NAME +.Nm arp +.Nd address resolution display and control +.Sh SYNOPSIS +.Nm +.Op Fl n +.Op Fl i Ar interface +.Ar hostname +.Nm +.Op Fl n +.Op Fl i Ar interface +.Op Fl l +.Fl a +.Nm +.Fl d Ar hostname +.Op Cm pub +.Op Cm ifscope interface +.Nm +.Fl d +.Op Fl i Ar interface +.Fl a +.Nm +.Fl s Ar hostname ether_addr +.Op Cm temp +.Op Cm reject +.Op Cm blackhole +.Op Cm pub Op Cm only +.Op Cm ifscope interface +.Nm +.Fl S Ar hostname ether_addr +.Op Cm temp +.Op Cm reject +.Op Cm blackhole +.Op Cm pub Op Cm only +.Op Cm ifscope interface +.Nm +.Fl f Ar filename +.Sh DESCRIPTION +The +.Nm +utility displays and modifies the Internet-to-Ethernet address translation +tables used by the address resolution protocol +.Pq Xr arp 4 . +With no flags, the program displays the current +.Tn ARP +entry for +.Ar hostname . +The host may be specified by name or by number, +using Internet dot notation. +.Pp +Available options: +.Bl -tag -width indent +.It Fl a +The program displays or deletes all of the current +.Tn ARP +entries. +.It Fl d +A super-user may delete an entry for the host called +.Ar hostname +with the +.Fl d +flag. +If the +.Cm pub +keyword is specified, only the +.Dq published +.Tn ARP +entry +for this host will be deleted. +If the +.Cm ifscope +keyword is specified, the entry specific to the interface will be deleted. +.Pp +Alternatively, the +.Fl d +flag may be combined with the +.Fl a +flag to delete all entries. +.It Fl i Ar interface +Limit the operation scope to the +.Tn ARP +entries on +.Ar interface . +Applicable only to the following operations: +display one, display all, delete all. +.It Fl l +Show link-layer reachability information. +.It Fl n +Show network addresses as numbers (normally +.Nm +attempts to display addresses symbolically). +.It Fl s Ar hostname ether_addr +Create an +.Tn ARP +entry for the host called +.Ar hostname +with the Ethernet address +.Ar ether_addr . +The Ethernet address is given as six hex bytes separated by colons. +The entry will be permanent unless the word +.Cm temp +is given in the command. +If the word +.Cm pub +is given, the entry will be +.Dq published ; +i.e., this system will +act as an +.Tn ARP +server, +responding to requests for +.Ar hostname +even though the host address is not its own. +In this case the +.Ar ether_addr +can be given as +.Cm auto +in which case the interfaces on this host will be examined, +and if one of them is found to occupy the same subnet, its +Ethernet address will be used. +If the +.Cm only +keyword is also specified, this will create a +.Dq "published (proxy only)" +entry. +This type of entry is created automatically if +.Nm +detects that a routing table entry for +.Ar hostname +already exists. +.Pp +If the +.Cm reject +keyword is specified the entry will be marked so that traffic to +the host will be discarded and the sender will be notified the +host is unreachable. +The +.Cm blackhole +keyword is similar in that traffic is discarded but the sender is +not notified. +These can be used to block external traffic to a host without +using a firewall. +.Pp +If the +.Cm ifscope +keyword is specified, the entry will set with an additional property that +strictly associate the entry to the interface. This allows +for the presence of mutiple entries with the same destination +on different interfaces. +.It Fl S Ar hostname ether_addr +Is just like +.Fl s +except any existing +.Tn ARP +entry for this host will be deleted first. +.It Fl f Ar filename +Cause the file +.Ar filename +to be read and multiple entries to be set in the +.Tn ARP +tables. +Entries +in the file should be of the form +.Pp +.Bd -ragged -offset indent -compact +.Ar hostname ether_addr +.Op Cm temp +.Op Cm pub Op Cm only +.Op Cm ifscope interface +.Ed +.Pp +with argument meanings as given above. +Leading whitespace and empty lines are ignored. +A +.Ql # +character will mark the rest of the line as a comment. +.It Fl x +Show extended link-layer reachability information in addition to that shown by +the +.Fl l +flag. +.El +.Sh SEE ALSO +.Xr inet 3 , +.Xr arp 4 , +.Xr ifconfig 8 , +.Xr ndp 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Bx 4.3 . diff --git a/network_cmds-543/arp.tproj/arp.c b/network_cmds-543/arp.tproj/arp.c new file mode 100644 index 00000000..85796eb8 --- /dev/null +++ b/network_cmds-543/arp.tproj/arp.c @@ -0,0 +1,1163 @@ +/* + * Copyright (c) 2003-2012 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Copyright (c) 1984, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Sun Microsystems, Inc. + * + * 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. + */ + +#if 0 +#ifndef lint +static char const copyright[] = +"@(#) Copyright (c) 1984, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ +#endif + +/* + * arp - display, set, and delete arp table entries + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#if 0 +#include +#endif + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef void (action_fn)(struct sockaddr_dl *sdl, + struct sockaddr_inarp *s_in, struct rt_msghdr *rtm); +typedef void (action_ext_fn)(struct sockaddr_dl *sdl, + struct sockaddr_inarp *s_in, struct rt_msghdr_ext *rtm); + +static int search(in_addr_t addr, action_fn *action); +static int search_ext(in_addr_t addr, action_ext_fn *action); +static action_fn print_entry; +static action_fn nuke_entry; +static action_ext_fn print_entry_ext; + +static char *print_lladdr(struct sockaddr_dl *); +static int delete(char *host, int do_proxy); +static void usage(void); +static int set(int argc, char **argv); +static int get(char *host); +static int file(char *name); +static struct rt_msghdr *rtmsg(int cmd, + struct sockaddr_inarp *dst, struct sockaddr_dl *sdl); +static int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr); +static struct sockaddr_inarp *getaddr(char *host); +static int valid_type(int type); +static char *sec2str(time_t); + +static int nflag; /* no reverse dns lookups */ +static int xflag; /* extended link-layer reachability information */ +static char *rifname; + +static int expire_time, flags, doing_proxy, proxy_only; + +static char *boundif = NULL; +static unsigned int ifscope = 0; + +/* which function we're supposed to do */ +#define F_GET 1 +#define F_SET 2 +#define F_FILESET 3 +#define F_REPLACE 4 +#define F_DELETE 5 + +#ifndef SA_SIZE +#define SA_SIZE(sa) \ + ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \ + sizeof(uint32_t) : \ + 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(uint32_t) - 1) ) ) +#endif + +#define SETFUNC(f) { if (func) usage(); func = (f); } + + +int +main(int argc, char *argv[]) +{ + int ch, func = 0; + int rtn = 0; + int aflag = 0; /* do it for all entries */ + int lflag = 0; + uint32_t ifindex = 0; + + while ((ch = getopt(argc, argv, "andflsSi:x")) != -1) + switch((char)ch) { + case 'a': + aflag = 1; + break; + case 'd': + SETFUNC(F_DELETE); + break; + case 'n': + nflag = 1; + break; + case 'l': + lflag = 1; + break; + case 'S': + SETFUNC(F_REPLACE); + break; + case 's': + SETFUNC(F_SET); + break; + case 'f' : + SETFUNC(F_FILESET); + break; + case 'i': + rifname = optarg; + break; + case 'x': + xflag = 1; + lflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (!func) + func = F_GET; + if (rifname) { + if (func != F_GET && !(func == F_DELETE && aflag)) + errx(1, "-i not applicable to this operation"); + if ((ifindex = if_nametoindex(rifname)) == 0) { + if (errno == ENXIO) + errx(1, "interface %s does not exist", rifname); + else + err(1, "if_nametoindex(%s)", rifname); + } + } + switch (func) { + case F_GET: + if (aflag) { + if (argc != 0) + usage(); + if (lflag) { + printf("%-23s %-17s %-9.9s %-9.9s %8.8s %4s " + "%4s", "Neighbor", + "Linklayer Address", "Expire(O)", + "Expire(I)", "Netif", "Refs", "Prbs"); + if (xflag) + printf(" %-7.7s %-7.7s %-7.7s", + "RSSI", "LQM", "NPM"); + printf("\n"); + search_ext(0, print_entry_ext); + } else { + search(0, print_entry); + } + } else { + if (argc != 1) + usage(); + rtn = get(argv[0]); + } + break; + case F_SET: + case F_REPLACE: + if (argc < 2 || argc > 6) + usage(); + if (func == F_REPLACE) + (void)delete(argv[0], 0); + rtn = set(argc, argv) ? 1 : 0; + break; + case F_DELETE: + if (aflag) { + if (argc != 0) + usage(); + search(0, nuke_entry); + } else { + int do_proxy = 0; + int i; + + for (i = 1; i < argc; i++) { + if (strncmp(argv[i], "pub", sizeof("pub")) == 0) { + do_proxy = SIN_PROXY; + } else if (strncmp(argv[i], "ifscope", sizeof("ifscope")) == 0) { + if (i + 1 >= argc) { + printf("ifscope needs an interface parameter\n"); + return (1); + } + boundif = argv[++i]; + if ((ifscope = if_nametoindex(boundif)) == 0) + errx(1, "ifscope has bad interface name: %s", boundif); + } else { + usage(); + } + } + if (i > argc) + usage(); + rtn = delete(argv[0], do_proxy); + } + break; + case F_FILESET: + if (argc != 1) + usage(); + rtn = file(argv[0]); + break; + } + + return (rtn); +} + +/* + * Process a file to set standard arp entries + */ +static int +file(char *name) +{ + FILE *fp; + int i, retval; + char line[128], arg[7][50], *args[7], *p; + + if ((fp = fopen(name, "r")) == NULL) + err(1, "cannot open %s", name); + args[0] = &arg[0][0]; + args[1] = &arg[1][0]; + args[2] = &arg[2][0]; + args[3] = &arg[3][0]; + args[4] = &arg[4][0]; + args[5] = &arg[5][0]; + args[6] = &arg[6][0]; + retval = 0; + while(fgets(line, sizeof(line), fp) != NULL) { + if ((p = strchr(line, '#')) != NULL) + *p = '\0'; + for (p = line; isblank(*p); p++); + if (*p == '\n' || *p == '\0') + continue; + i = sscanf(p, "%49s %49s %49s %49s %49s %49s %49s", arg[0], arg[1], + arg[2], arg[3], arg[4], arg[5], arg[6]); + if (i < 2) { + warnx("bad line: %s", line); + retval = 1; + continue; + } + if (set(i, args)) + retval = 1; + } + fclose(fp); + return (retval); +} + +/* + * Given a hostname, fills up a (static) struct sockaddr_inarp with + * the address of the host and returns a pointer to the + * structure. + */ +static struct sockaddr_inarp * +getaddr(char *host) +{ + struct hostent *hp; + static struct sockaddr_inarp reply; + + bzero(&reply, sizeof(reply)); + reply.sin_len = sizeof(reply); + reply.sin_family = AF_INET; + reply.sin_addr.s_addr = inet_addr(host); + if (reply.sin_addr.s_addr == INADDR_NONE) { + if (!(hp = gethostbyname(host))) { + warnx("%s: %s", host, hstrerror(h_errno)); + return (NULL); + } + bcopy((char *)hp->h_addr, (char *)&reply.sin_addr, + sizeof reply.sin_addr); + } + return (&reply); +} + +/* + * Returns true if the type is a valid one for ARP. + */ +static int +valid_type(int type) +{ + + switch (type) { + case IFT_ETHER: + case IFT_FDDI: + case IFT_ISO88023: + case IFT_ISO88024: +#if 0 + case IFT_ISO88025: +#endif + case IFT_L2VLAN: +#ifdef IFT_BRIDGE + case IFT_BRIDGE: +#endif + return (1); + default: + return (0); + } +} + +/* + * Set an individual arp entry + */ +static int +set(int argc, char **argv) +{ + struct sockaddr_inarp *addr; + struct sockaddr_inarp *dst; /* what are we looking for */ + struct sockaddr_dl *sdl; + struct rt_msghdr *rtm; + struct ether_addr *ea; + char *host = argv[0], *eaddr = argv[1]; + struct sockaddr_dl sdl_m; + + argc -= 2; + argv += 2; + + bzero(&sdl_m, sizeof(sdl_m)); + sdl_m.sdl_len = sizeof(sdl_m); + sdl_m.sdl_family = AF_LINK; + + dst = getaddr(host); + if (dst == NULL) + return (1); + doing_proxy = flags = proxy_only = expire_time = 0; + boundif = NULL; + ifscope = 0; + while (argc-- > 0) { + if (strncmp(argv[0], "temp", sizeof("temp")) == 0) { + struct timeval tv; + gettimeofday(&tv, 0); + expire_time = tv.tv_sec + 20 * 60; + } else if (strncmp(argv[0], "pub", sizeof("pub")) == 0) { + flags |= RTF_ANNOUNCE; + doing_proxy = 1; + if (argc && strncmp(argv[1], "only", sizeof("only")) == 0) { + proxy_only = 1; + dst->sin_other = SIN_PROXY; + argc--; argv++; + } + } else if (strncmp(argv[0], "blackhole", sizeof("blackhole")) == 0) { + flags |= RTF_BLACKHOLE; + } else if (strncmp(argv[0], "reject", sizeof("reject")) == 0) { + flags |= RTF_REJECT; + } else if (strncmp(argv[0], "trail", sizeof("trail")) == 0) { + /* XXX deprecated and undocumented feature */ + printf("%s: Sending trailers is no longer supported\n", + host); + } else if (strncmp(argv[0], "ifscope", sizeof("ifscope")) == 0) { + if (argc < 1) { + printf("ifscope needs an interface parameter\n"); + return (1); + } + boundif = argv[1]; + if ((ifscope = if_nametoindex(boundif)) == 0) + errx(1, "ifscope has bad interface name: %s", boundif); + argc--; argv++; + } + argv++; + } + ea = (struct ether_addr *)LLADDR(&sdl_m); + if (doing_proxy && !strcmp(eaddr, "auto")) { + if (!get_ether_addr(dst->sin_addr.s_addr, ea)) { + printf("no interface found for %s\n", + inet_ntoa(dst->sin_addr)); + return (1); + } + sdl_m.sdl_alen = ETHER_ADDR_LEN; + } else { + struct ether_addr *ea1 = ether_aton(eaddr); + + if (ea1 == NULL) { + warnx("invalid Ethernet address '%s'", eaddr); + return (1); + } else { + *ea = *ea1; + sdl_m.sdl_alen = ETHER_ADDR_LEN; + } + } + for (;;) { /* try at most twice */ + rtm = rtmsg(RTM_GET, dst, &sdl_m); + if (rtm == NULL) { + warn("%s", host); + return (1); + } + addr = (struct sockaddr_inarp *)(rtm + 1); + sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); + if (addr->sin_addr.s_addr != dst->sin_addr.s_addr) + break; + if (sdl->sdl_family == AF_LINK && + (rtm->rtm_flags & RTF_LLINFO) && + !(rtm->rtm_flags & RTF_GATEWAY) && + valid_type(sdl->sdl_type) ) + break; + /* + * If we asked for a scope entry and did not get one or + * did not asked for a scope entry and got one, we can + * proceed. + */ + if ((ifscope != 0) != (rtm->rtm_flags & RTF_IFSCOPE)) + break; + if (doing_proxy == 0) { + printf("set: can only proxy for %s\n", host); + return (1); + } + if (dst->sin_other & SIN_PROXY) { + printf("set: proxy entry exists for non 802 device\n"); + return (1); + } + dst->sin_other = SIN_PROXY; + proxy_only = 1; + } + + if (sdl->sdl_family != AF_LINK) { + printf("cannot intuit interface index and type for %s\n", host); + return (1); + } + sdl_m.sdl_type = sdl->sdl_type; + sdl_m.sdl_index = sdl->sdl_index; + return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL); +} + +/* + * Display an individual arp entry + */ +static int +get(char *host) +{ + struct sockaddr_inarp *addr; + + addr = getaddr(host); + if (addr == NULL) + return (1); + if (0 == search(addr->sin_addr.s_addr, print_entry)) { + printf("%s (%s) -- no entry", + host, inet_ntoa(addr->sin_addr)); + if (rifname) + printf(" on %s", rifname); + printf("\n"); + return (1); + } + return (0); +} + +/* + * Delete an arp entry + */ +static int +delete(char *host, int do_proxy) +{ + struct sockaddr_inarp *addr, *dst; + struct rt_msghdr *rtm; + struct sockaddr_dl *sdl; + + dst = getaddr(host); + if (dst == NULL) + return (1); + dst->sin_other = do_proxy; + for (;;) { /* try twice */ + rtm = rtmsg(RTM_GET, dst, NULL); + if (rtm == NULL) { + warn("%s", host); + return (1); + } + addr = (struct sockaddr_inarp *)(rtm + 1); + sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); + if (addr->sin_addr.s_addr == dst->sin_addr.s_addr && + sdl->sdl_family == AF_LINK && + (rtm->rtm_flags & RTF_LLINFO) && + !(rtm->rtm_flags & RTF_GATEWAY) && + valid_type(sdl->sdl_type) ) + break; /* found it */ + if (dst->sin_other & SIN_PROXY) { + fprintf(stderr, "delete: cannot locate %s\n",host); + return (1); + } + dst->sin_other = SIN_PROXY; + } + if (rtmsg(RTM_DELETE, dst, NULL) != NULL) { + printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr)); + return (0); + } + return (1); +} + +/* + * Search the arp table and do some action on matching entries + */ +static int +search(in_addr_t addr, action_fn *action) +{ + int mib[6]; + size_t needed; + char *lim, *buf, *newbuf, *next; + struct rt_msghdr *rtm; + struct sockaddr_inarp *sin2; + struct sockaddr_dl *sdl; + char ifname[IF_NAMESIZE]; + int st, found_entry = 0; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET; + mib[4] = NET_RT_FLAGS; + mib[5] = RTF_LLINFO; + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + err(1, "route-sysctl-estimate"); + if (needed == 0) /* empty table */ + return 0; + buf = NULL; + for (;;) { + newbuf = realloc(buf, needed); + if (newbuf == NULL) { + if (buf != NULL) + free(buf); + errx(1, "could not reallocate memory"); + } + buf = newbuf; + st = sysctl(mib, 6, buf, &needed, NULL, 0); + if (st == 0 || errno != ENOMEM) + break; + needed += needed / 8; + } + if (st == -1) + err(1, "actual retrieval of routing table"); + lim = buf + needed; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + sin2 = (struct sockaddr_inarp *)(rtm + 1); + sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2)); + if (rifname && if_indextoname(sdl->sdl_index, ifname) && + strcmp(ifname, rifname)) + continue; + if (addr) { + if (addr != sin2->sin_addr.s_addr) + continue; + found_entry = 1; + } + (*action)(sdl, sin2, rtm); + } + free(buf); + return (found_entry); +} + +/* + * Stolen and adapted from ifconfig + */ +static char * +print_lladdr(struct sockaddr_dl *sdl) +{ + static char buf[256]; + char *cp; + int n, bufsize = sizeof (buf), p = 0; + + bzero(buf, sizeof (buf)); + cp = (char *)LLADDR(sdl); + if ((n = sdl->sdl_alen) > 0) { + while (--n >= 0) + p += snprintf(buf + p, bufsize - p, "%x%s", + *cp++ & 0xff, n > 0 ? ":" : ""); + } + return (buf); +} + +/* + * Display an arp entry + */ +static void +print_entry(struct sockaddr_dl *sdl, + struct sockaddr_inarp *addr, struct rt_msghdr *rtm) +{ + const char *host; + struct hostent *hp; + char ifname[IF_NAMESIZE]; +#if 0 + struct iso88025_sockaddr_dl_data *trld; + int seg; +#endif + + if (nflag == 0) + hp = gethostbyaddr((caddr_t)&(addr->sin_addr), + sizeof addr->sin_addr, AF_INET); + else + hp = 0; + if (hp) + host = hp->h_name; + else { + host = "?"; + if (h_errno == TRY_AGAIN) + nflag = 1; + } + printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr)); + if (sdl->sdl_alen) { +#if 1 + printf("%s", print_lladdr(sdl)); +#else + if ((sdl->sdl_type == IFT_ETHER || + sdl->sdl_type == IFT_L2VLAN || + sdl->sdl_type == IFT_BRIDGE) && + sdl->sdl_alen == ETHER_ADDR_LEN) + printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl))); + else { + int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; + + printf("%s", link_ntoa(sdl) + n); + } +#endif + } else + printf("(incomplete)"); + if (if_indextoname(sdl->sdl_index, ifname) != NULL) + printf(" on %s", ifname); + if ((rtm->rtm_flags & RTF_IFSCOPE)) + printf(" ifscope"); + if (rtm->rtm_rmx.rmx_expire == 0) + printf(" permanent"); + if (addr->sin_other & SIN_PROXY) + printf(" published (proxy only)"); + if (rtm->rtm_addrs & RTA_NETMASK) { + addr = (struct sockaddr_inarp *) + (SA_SIZE(sdl) + (char *)sdl); + if (addr->sin_addr.s_addr == 0xffffffff) + printf(" published"); + if (addr->sin_len != 8) + printf("(weird)"); + } + switch(sdl->sdl_type) { + case IFT_ETHER: + printf(" [ethernet]"); + break; +#if 0 + case IFT_ISO88025: + printf(" [token-ring]"); + trld = SDL_ISO88025(sdl); + if (trld->trld_rcf != 0) { + printf(" rt=%x", ntohs(trld->trld_rcf)); + for (seg = 0; + seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2); + seg++) + printf(":%x", ntohs(*(trld->trld_route[seg]))); + } + break; +#endif + case IFT_FDDI: + printf(" [fddi]"); + break; + case IFT_ATM: + printf(" [atm]"); + break; + case IFT_L2VLAN: + printf(" [vlan]"); + break; + case IFT_IEEE1394: + printf(" [firewire]"); + break; +#ifdef IFT_BRIDGE + case IFT_BRIDGE: + printf(" [bridge]"); + break; +#endif + default: + break; + } + + printf("\n"); + +} + +/* + * Nuke an arp entry + */ +static void +nuke_entry(struct sockaddr_dl *sdl __unused, + struct sockaddr_inarp *addr, struct rt_msghdr *rtm) +{ + char ip[20]; + + snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr)); + /* + * When deleting all entries, specify the interface scope of each entry + */ + if ((rtm->rtm_flags & RTF_IFSCOPE)) + ifscope = rtm->rtm_index; + (void)delete(ip, 0); + ifscope = 0; +} + +static void +usage(void) +{ + fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n", + "usage: arp [-n] [-i interface] hostname", + " arp [-n] [-i interface] [-l] -a", + " arp -d hostname [pub] [ifscope interface]", + " arp -d [-i interface] -a", + " arp -s hostname ether_addr [temp] [reject] [blackhole] [pub [only]] [ifscope interface]", + " arp -S hostname ether_addr [temp] [reject] [blackhole] [pub [only]] [ifscope interface]", + " arp -f filename"); + exit(1); +} + +static struct rt_msghdr * +rtmsg(int cmd, struct sockaddr_inarp *dst, struct sockaddr_dl *sdl) +{ + static int seq; + int rlen; + int l; + struct sockaddr_in so_mask, *so_mask_ptr = &so_mask; + static int s = -1; + static pid_t pid; + + static struct { + struct rt_msghdr m_rtm; + char m_space[512]; + } m_rtmsg; + + struct rt_msghdr *rtm = &m_rtmsg.m_rtm; + char *cp = m_rtmsg.m_space; + + if (s < 0) { /* first time: open socket, get pid */ + s = socket(PF_ROUTE, SOCK_RAW, 0); + if (s < 0) + err(1, "socket"); + pid = getpid(); + } + bzero(&so_mask, sizeof(so_mask)); + so_mask.sin_len = 8; + so_mask.sin_addr.s_addr = 0xffffffff; + + errno = 0; + /* + * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer + * appropriately (except for the mask set just above). + */ + if (cmd == RTM_DELETE) + goto doit; + bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); + rtm->rtm_flags = flags; + rtm->rtm_version = RTM_VERSION; + + /* + * Note: On RTM_GET the kernel will return a scoped route when both a scoped route and + * a unscoped route exist. That means we cannot delete a unscoped route if there is + * also a matching scope route + */ + if (ifscope) { + rtm->rtm_index = ifscope; + rtm->rtm_flags |= RTF_IFSCOPE; + } + + switch (cmd) { + default: + errx(1, "internal wrong cmd"); + case RTM_ADD: + rtm->rtm_addrs |= RTA_GATEWAY; + rtm->rtm_rmx.rmx_expire = expire_time; + rtm->rtm_inits = RTV_EXPIRE; + rtm->rtm_flags |= (RTF_HOST | RTF_STATIC); + dst->sin_other = 0; + if (doing_proxy) { + if (proxy_only) + dst->sin_other = SIN_PROXY; + else { + rtm->rtm_addrs |= RTA_NETMASK; + rtm->rtm_flags &= ~RTF_HOST; + } + } + /* FALLTHROUGH */ + case RTM_GET: + rtm->rtm_addrs |= RTA_DST; + } +#define NEXTADDR(w, s) \ + if ((s) != NULL && rtm->rtm_addrs & (w)) { \ + bcopy((s), cp, sizeof(*(s))); cp += SA_SIZE(s);} + + NEXTADDR(RTA_DST, dst); + NEXTADDR(RTA_GATEWAY, sdl); + NEXTADDR(RTA_NETMASK, so_mask_ptr); + + rtm->rtm_msglen = cp - (char *)&m_rtmsg; +doit: + l = rtm->rtm_msglen; + rtm->rtm_seq = ++seq; + rtm->rtm_type = cmd; + if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { + if (errno != ESRCH || cmd != RTM_DELETE) { + warn("writing to routing socket"); + return (NULL); + } + } + do { + l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); + } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); + if (l < 0) + warn("read from routing socket"); + return (rtm); +} + +/* + * get_ether_addr - get the hardware address of an interface on the + * the same subnet as ipaddr. + */ +#define MAX_IFS 32 + +static int +get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr) +{ + struct ifreq *ifr, *ifend, *ifp; + in_addr_t ina, mask; + struct sockaddr_dl *dla; + struct ifreq ifreq; + struct ifconf ifc; + struct ifreq ifs[MAX_IFS]; + int sock; + int retval = 0; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) + err(1, "socket"); + + ifc.ifc_len = sizeof(ifs); + ifc.ifc_req = ifs; + if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { + warnx("ioctl(SIOCGIFCONF)"); + goto done; + } + +#define NEXTIFR(i) \ + ((struct ifreq *)((char *)&(i)->ifr_addr \ + + MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) ) + + /* + * Scan through looking for an interface with an Internet + * address on the same subnet as `ipaddr'. + */ + ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len); + for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) { + if (ifr->ifr_addr.sa_family != AF_INET) + continue; + strncpy(ifreq.ifr_name, ifr->ifr_name, + sizeof(ifreq.ifr_name)); + ifreq.ifr_addr = ifr->ifr_addr; + /* + * Check that the interface is up, + * and not point-to-point or loopback. + */ + if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0) + continue; + if ((ifreq.ifr_flags & + (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT| + IFF_LOOPBACK|IFF_NOARP)) + != (IFF_UP|IFF_BROADCAST)) + continue; + /* + * Get its netmask and check that it's on + * the right subnet. + */ + if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0) + continue; + mask = ((struct sockaddr_in *) + &ifreq.ifr_addr)->sin_addr.s_addr; + ina = ((struct sockaddr_in *) + &ifr->ifr_addr)->sin_addr.s_addr; + if ((ipaddr & mask) == (ina & mask)) + break; /* ok, we got it! */ + } + + if (ifr >= ifend) + goto done; + + /* + * Now scan through again looking for a link-level address + * for this interface. + */ + ifp = ifr; + for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr)) + if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 && + ifr->ifr_addr.sa_family == AF_LINK) + break; + if (ifr >= ifend) + goto done; + /* + * Found the link-level address - copy it out + */ + dla = (struct sockaddr_dl *) &ifr->ifr_addr; + memcpy(hwaddr, LLADDR(dla), dla->sdl_alen); + printf("using interface %s for proxy with address ", + ifp->ifr_name); + printf("%s\n", ether_ntoa(hwaddr)); + retval = dla->sdl_alen; +done: + close(sock); + return (retval); +} + +static char * +sec2str(total) + time_t total; +{ + static char result[256]; + int days, hours, mins, secs; + int first = 1; + char *p = result; + + days = total / 3600 / 24; + hours = (total / 3600) % 24; + mins = (total / 60) % 60; + secs = total % 60; + + if (days) { + first = 0; + p += snprintf(p, sizeof(result) - (p - result), "%dd", days); + } + if (!first || hours) { + first = 0; + p += snprintf(p, sizeof(result) - (p - result), "%dh", hours); + } + if (!first || mins) { + first = 0; + p += snprintf(p, sizeof(result) - (p - result), "%dm", mins); + } + snprintf(p, sizeof(result) - (p - result), "%ds", secs); + + return(result); +} + +static int +search_ext(in_addr_t addr, action_ext_fn *action) +{ + int mib[6]; + size_t needed; + char *lim, *buf, *newbuf, *next; + struct rt_msghdr_ext *ertm; + struct sockaddr_inarp *sin2; + struct sockaddr_dl *sdl; + char ifname[IF_NAMESIZE]; + int st, found_entry = 0; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET; + mib[4] = NET_RT_DUMPX_FLAGS; + mib[5] = RTF_LLINFO; + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + err(1, "route-sysctl-estimate"); + if (needed == 0) /* empty table */ + return 0; + buf = NULL; + for (;;) { + newbuf = realloc(buf, needed); + if (newbuf == NULL) { + if (buf != NULL) + free(buf); + errx(1, "could not reallocate memory"); + } + buf = newbuf; + st = sysctl(mib, 6, buf, &needed, NULL, 0); + if (st == 0 || errno != ENOMEM) + break; + needed += needed / 8; + } + if (st == -1) + err(1, "actual retrieval of routing table"); + lim = buf + needed; + for (next = buf; next < lim; next += ertm->rtm_msglen) { + ertm = (struct rt_msghdr_ext *)next; + sin2 = (struct sockaddr_inarp *)(ertm + 1); + sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2)); + if (rifname && if_indextoname(sdl->sdl_index, ifname) && + strcmp(ifname, rifname)) + continue; + if (addr) { + if (addr != sin2->sin_addr.s_addr) + continue; + found_entry = 1; + } + (*action)(sdl, sin2, ertm); + } + free(buf); + return (found_entry); +} + +static void +print_entry_ext(struct sockaddr_dl *sdl, struct sockaddr_inarp *addr, + struct rt_msghdr_ext *ertm) +{ + const char *host; + struct hostent *hp; + char ifname[IF_NAMESIZE]; + struct timeval time; + + if (nflag == 0) + hp = gethostbyaddr((caddr_t)&(addr->sin_addr), + sizeof (addr->sin_addr), AF_INET); + else + hp = 0; + + if (hp) + host = hp->h_name; + else + host = inet_ntoa(addr->sin_addr); + + printf("%-23s ", host); + + if (sdl->sdl_alen) + printf("%-17s ", print_lladdr(sdl)); + else + printf("%-17s ", "(incomplete)"); + + gettimeofday(&time, 0); + + if (ertm->rtm_ri.ri_refcnt == 0 || ertm->rtm_ri.ri_snd_expire == 0) + printf("%-9.9s ", "(none)"); + else if (ertm->rtm_ri.ri_snd_expire > time.tv_sec) + printf("%-9.9s ", + sec2str(ertm->rtm_ri.ri_snd_expire - time.tv_sec)); + else + printf("%-9.9s ", "expired"); + + if (ertm->rtm_ri.ri_refcnt == 0 || ertm->rtm_ri.ri_rcv_expire == 0) + printf("%-9.9s", "(none)"); + else if (ertm->rtm_ri.ri_rcv_expire > time.tv_sec) + printf("%-9.9s", + sec2str(ertm->rtm_ri.ri_rcv_expire - time.tv_sec)); + else + printf("%-9.9s", "expired"); + + if (if_indextoname(sdl->sdl_index, ifname) == NULL) + snprintf(ifname, sizeof (ifname), "%s", "?"); + printf(" %8.8s", ifname); + + if (ertm->rtm_ri.ri_refcnt) { + printf(" %4d", ertm->rtm_ri.ri_refcnt); + if (ertm->rtm_ri.ri_probes) + printf(" %4d", ertm->rtm_ri.ri_probes); + + if (xflag) { + if (!ertm->rtm_ri.ri_probes) + printf(" %-4.4s", "none"); + + if (ertm->rtm_ri.ri_rssi != IFNET_RSSI_UNKNOWN) + printf(" %7d", ertm->rtm_ri.ri_rssi); + else + printf(" %-7.7s", "unknown"); + + switch (ertm->rtm_ri.ri_lqm) + { + case IFNET_LQM_THRESH_OFF: + printf(" %-7.7s", "off"); + break; + case IFNET_LQM_THRESH_UNKNOWN: + printf(" %-7.7s", "unknown"); + break; + case IFNET_LQM_THRESH_POOR: + printf(" %-7.7s", "poor"); + break; + case IFNET_LQM_THRESH_GOOD: + printf(" %-7.7s", "good"); + break; + default: + printf(" %7d", ertm->rtm_ri.ri_lqm); + break; + } + + switch (ertm->rtm_ri.ri_npm) + { + case IFNET_NPM_THRESH_UNKNOWN: + printf(" %-7.7s", "unknown"); + break; + case IFNET_NPM_THRESH_NEAR: + printf(" %-7.7s", "near"); + break; + case IFNET_NPM_THRESH_GENERAL: + printf(" %-7.7s", "general"); + break; + case IFNET_NPM_THRESH_FAR: + printf(" %-7.7s", "far"); + break; + default: + printf(" %7d", ertm->rtm_ri.ri_npm); + break; + } + } + } + printf("\n"); +} diff --git a/network_cmds-543/arp.tproj/arp4.4 b/network_cmds-543/arp.tproj/arp4.4 new file mode 100644 index 00000000..a8bb6e7d --- /dev/null +++ b/network_cmds-543/arp.tproj/arp4.4 @@ -0,0 +1,123 @@ +.\" Copyright (c) 1985, 1986, 1988, 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 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. +.\" +.\" @(#)arp4.4 6.5 (Berkeley) 4/18/94 +.\" +.Dd April 18, 1994 +.Dt ARP 4 +.Os BSD 4 +.Sh NAME +.Nm arp +.Nd Address Resolution Protocol +.Sh SYNOPSIS +.Em "pseudo-device ether" +.Sh DESCRIPTION +The Address Resolution Protocol (ARP) is a protocol used to dynamically +map between Internet host addresses and 10Mb/s Ethernet addresses. +It is used by all the 10Mb/s Ethernet interface drivers. +It is not specific to Internet protocols or to 10Mb/s Ethernet, +but this implementation currently supports only that combination. +.Pp +ARP caches Internet-Ethernet address mappings. +When an interface requests a mapping for an address not in the cache, +ARP queues the message which requires the mapping and broadcasts +a message on the associated network requesting the address mapping. +If a response is provided, the new mapping is cached and any pending +message is transmitted. +ARP will queue at most one packet while waiting for a response to a +mapping request; +only the most recently ``transmitted'' packet is kept. +If the target host does not respond after several requests, +the host is considered to be down for a short period (normally 20 seconds), +allowing an error to be returned to transmission attempts during this +interval. +The error is +.Li EHOSTDOWN +for a non-responding destination host, and +.Li EHOSTUNREACH +for a non-responding router. +.Pp +The ARP cache is stored in the system routing table as +dynamically-created host routes. +The route to a directly-attached Ethernet network is installed as a +.Dq cloning +route (one with the +.Li RTF_CLONING +flag set), +causing routes to individual hosts on that network to be created on +demand. +These routes time out periodically (normally 20 minutes after validated; +entries are not validated when not in use). +An entry for a host which is not responding is a +.Dq reject +route (one with the +.Li RTF_REJECT +flag set). +.Pp +ARP entries may be added, deleted or changed with the +.Xr arp 8 +utility. +Manually-added entries may be temporary or permanent, +and may be +.Dq published , +in which case the system will respond to ARP requests for that host +as if it were the target of the request. +.Pp +In the past, +ARP was used to negotiate the use of a trailer encapsulation. +This is no longer supported. +.Pp +ARP watches passively for hosts impersonating the local host (i.e. a host +which responds to an ARP mapping request for the local host's address). +.Sh DIAGNOSTICS +.Em "duplicate IP address %x!! sent from ethernet address: %x:%x:%x:%x:%x:%x." +ARP has discovered another host on the local network which responds to +mapping requests for its own Internet address with a different Ethernet +address, generally indicating that two hosts are attempting to use the +same Internet address. +.Sh SEE ALSO +.Xr inet 4 , +.Xr route 4 , +.Xr arp 8 , +.Xr ifconfig 8 , +.Xr route 8 +.sp +.Rs +.%A Plummer, D. +.%B "An Ethernet Address Resolution Protocol" +.%T RFC826 +.Re +.Rs +.%A Leffler, S.J. +.%A Karels, M.J. +.%B "Trailer Encapsulations +.%T RFC893 +.Re diff --git a/network_cmds-543/cfilutil/cfilstat.c b/network_cmds-543/cfilutil/cfilstat.c new file mode 100644 index 00000000..ac4b1974 --- /dev/null +++ b/network_cmds-543/cfilutil/cfilstat.c @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2013-2014 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +void +print_filter_list() +{ + size_t total_len, curr_len; + void *buffer = NULL; + void *ptr; + uint32_t line = 0; + + if (sysctlbyname("net.cfil.filter_list", NULL, &total_len, NULL, 0) == -1) + err(1, "sysctlbyname(net.cfil.filter_list)"); + + buffer = malloc(total_len); + if (buffer == NULL) + err(1, "malloc()"); + if (sysctlbyname("net.cfil.filter_list", buffer, &total_len, NULL, 0) == -1) + err(1, "sysctlbyname(net.cfil.filter_list)"); + + ptr = buffer; + curr_len = 0; + do { + struct cfil_filter_stat *filter_stat; + + filter_stat = (struct cfil_filter_stat *)ptr; + + if (curr_len + filter_stat->cfs_len > total_len || + filter_stat->cfs_len < sizeof(struct cfil_filter_stat)) + break; + + if (line % 16 == 0) + printf("%10s %10s %10s %10s\n", + "filter", "flags", "count", "necpunit"); + + printf("%10u 0x%08x %10u %10u\n", + filter_stat->cfs_filter_id, + filter_stat->cfs_flags, + filter_stat->cfs_sock_count, + filter_stat->cfs_necp_control_unit); + + ptr += filter_stat->cfs_len; + curr_len += filter_stat->cfs_len; + } while (1); + + free(buffer); +} + +void +sprint_offset(char *str, size_t len, const char *fmt, uint64_t offset) +{ + if (offset == CFM_MAX_OFFSET) + snprintf(str, len, "%s", "MAX"); + else + snprintf(str, len, fmt, offset); +} + +void +print_socket_list() +{ + size_t total_len, curr_len; + void *buffer = NULL; + void *ptr; + int i; + + if (sysctlbyname("net.cfil.sock_list", NULL, &total_len, NULL, 0) == -1) + err(1, "sysctlbyname(net.cfil.sock_list)"); + + buffer = malloc(total_len); + if (buffer == NULL) + err(1, "malloc()"); + if (sysctlbyname("net.cfil.sock_list", buffer, &total_len, NULL, 0) == -1) + err(1, "sysctlbyname(net.cfil.sock_list)"); + + ptr = buffer; + curr_len = 0; + do { + struct cfil_sock_stat *sock_stat; + char opass[32]; + char ipass[32]; + + sock_stat = (struct cfil_sock_stat *)ptr; + + if (curr_len + sock_stat->cfs_len > total_len || + sock_stat->cfs_len < sizeof(struct cfil_sock_stat)) + break; + + sprint_offset(opass, 32, "%8llu", sock_stat->cfs_snd.cbs_pass_offset); + sprint_offset(ipass, 32, "%8llu", sock_stat->cfs_rcv.cbs_pass_offset); + + printf("%18s %10s " + "%8s %8s %8s %8s %8s %8s %8s " + "%8s %8s %8s %8s %8s %8s %8s " + "%8s %8s\n", + "sockid", "flags", + "ofirst", "olast", "oqlen", " ", "opass", " ", " ", + "ifirst", "ilast", "iqlen", " ", "ipass", " ", " ", + "pid", "epid"); + + printf("0x%016llx 0x%08llx " + "%8llu %8llu %8llu %8s %8s %8s %8s " + "%8llu %8llu %8llu %8s %8s %8s %8s " + "%8u %8u\n", + + sock_stat->cfs_sock_id, + sock_stat->cfs_flags, + + sock_stat->cfs_snd.cbs_pending_first, + sock_stat->cfs_snd.cbs_pending_last, + sock_stat->cfs_snd.cbs_inject_q_len, + " ", + opass, + " ", + " ", + + sock_stat->cfs_rcv.cbs_pending_first, + sock_stat->cfs_rcv.cbs_pending_last, + sock_stat->cfs_rcv.cbs_inject_q_len, + " ", + ipass, + " ", + " ", + sock_stat->cfs_pid, + sock_stat->cfs_e_pid); + + printf("%7s %10s %10s " + "%8s %8s %8s %8s %8s %8s %8s " + "%8s %8s %8s %8s %8s %8s %8s\n", + " ", + "filter", "flags", + "octlfrst", "octllast", "opndfrst", "opndlast", "opass", "opked", "opeek", + "ictlfrst", "ictllast", "ipndfrst", "ipndlast", "ipass", "ipked", "ipeek"); + for (i = 0; i < CFIL_MAX_FILTER_COUNT; i++) { + struct cfil_entry_stat *estat; + char spass[32]; + char speek[32]; + char spked[32]; + char rpass[32]; + char rpeek[32]; + char rpked[32]; + + estat = &sock_stat->ces_entries[i]; + + sprint_offset(spass, 32, "%8llu", estat->ces_snd.cbs_pass_offset); + sprint_offset(speek, 32, "%8llu", estat->ces_snd.cbs_peek_offset); + sprint_offset(spked, 32, "%8llu", estat->ces_snd.cbs_peeked); + + sprint_offset(rpass, 32, "%8llu", estat->ces_rcv.cbs_pass_offset); + sprint_offset(rpeek, 32, "%8llu", estat->ces_rcv.cbs_peek_offset); + sprint_offset(rpked, 32, "%8llu", estat->ces_rcv.cbs_peeked); + + printf("%7s %10u 0x%08x " + "%8llu %8llu %8llu %8llu %8s %8s %8s " + "%8llu %8llu %8llu %8llu %8s %8s %8s\n", + + " ", + estat->ces_filter_id, + estat->ces_flags, + + estat->ces_snd.cbs_ctl_first, + estat->ces_snd.cbs_ctl_last, + estat->ces_snd.cbs_pending_first, + estat->ces_snd.cbs_pending_last, + spass, + spked, + speek, + + estat->ces_rcv.cbs_ctl_first, + estat->ces_rcv.cbs_ctl_last, + estat->ces_rcv.cbs_pending_first, + estat->ces_rcv.cbs_pending_last, + rpass, + rpked, + rpeek); + } + + + ptr += sock_stat->cfs_len; + curr_len += sock_stat->cfs_len; + } while (1); + + free(buffer); +} + + +#define PR32(x) printf(#x " %u\n", stats-> x) +#define PR64(x) printf(#x " %llu\n", stats-> x) +void +print_cfil_stats() +{ + size_t len, alloc_len; + void *buffer = NULL; + struct cfil_stats *stats; + + if (sysctlbyname("net.cfil.stats", NULL, &len, NULL, 0) == -1) + err(1, "sysctlbyname(net.cfil.stats)"); + + if (len < sizeof(struct cfil_stats)) + alloc_len = sizeof(struct cfil_stats); + else + alloc_len = len; + + buffer = malloc(alloc_len); + if (buffer == NULL) + err(1, "malloc()"); + if (sysctlbyname("net.cfil.stats", buffer, &len, NULL, 0) == -1) + err(1, "sysctlbyname(net.cfil.stats)"); + stats = (struct cfil_stats *)buffer; + + PR32(cfs_ctl_connect_ok); + PR32(cfs_ctl_connect_fail); + PR32(cfs_ctl_connect_ok); + PR32(cfs_ctl_connect_fail); + PR32(cfs_ctl_disconnect_ok); + PR32(cfs_ctl_disconnect_fail); + PR32(cfs_ctl_send_ok); + PR32(cfs_ctl_send_bad); + PR32(cfs_ctl_rcvd_ok); + PR32(cfs_ctl_rcvd_bad); + PR32(cfs_ctl_rcvd_flow_lift); + PR32(cfs_ctl_action_data_update); + PR32(cfs_ctl_action_drop); + PR32(cfs_ctl_action_bad_op); + PR32(cfs_ctl_action_bad_len); + + PR32(cfs_sock_id_not_found); + + PR32(cfs_cfi_alloc_ok); + PR32(cfs_cfi_alloc_fail); + + PR32(cfs_sock_userspace_only); + PR32(cfs_sock_attach_in_vain); + PR32(cfs_sock_attach_already); + PR32(cfs_sock_attach_no_mem); + PR32(cfs_sock_attach_failed); + PR32(cfs_sock_attached); + PR32(cfs_sock_detached); + + PR32(cfs_attach_event_ok); + PR32(cfs_attach_event_flow_control); + PR32(cfs_attach_event_fail); + + PR32(cfs_closed_event_ok); + PR32(cfs_closed_event_flow_control); + PR32(cfs_closed_event_fail); + + PR32(cfs_data_event_ok); + PR32(cfs_data_event_flow_control); + PR32(cfs_data_event_fail); + + PR32(cfs_disconnect_in_event_ok); + PR32(cfs_disconnect_out_event_ok); + PR32(cfs_disconnect_event_flow_control); + PR32(cfs_disconnect_event_fail); + + PR32(cfs_ctl_q_not_started); + + PR32(cfs_close_wait); + PR32(cfs_close_wait_timeout); + + PR32(cfs_flush_in_drop); + PR32(cfs_flush_out_drop); + PR32(cfs_flush_in_close); + PR32(cfs_flush_out_close); + PR32(cfs_flush_in_free); + PR32(cfs_flush_out_free); + + PR32(cfs_inject_q_nomem); + PR32(cfs_inject_q_nobufs); + PR32(cfs_inject_q_detached); + PR32(cfs_inject_q_in_fail); + PR32(cfs_inject_q_out_fail); + + PR32(cfs_inject_q_in_retry); + PR32(cfs_inject_q_out_retry); + + PR32(cfs_data_in_control); + PR32(cfs_data_in_oob); + PR32(cfs_data_out_control); + PR32(cfs_data_out_oob); + + PR64(cfs_ctl_q_in_enqueued); + PR64(cfs_ctl_q_out_enqueued); + PR64(cfs_ctl_q_in_peeked); + PR64(cfs_ctl_q_out_peeked); + + PR64(cfs_pending_q_in_enqueued); + PR64(cfs_pending_q_out_enqueued); + + PR64(cfs_inject_q_in_enqueued); + PR64(cfs_inject_q_out_enqueued); + PR64(cfs_inject_q_in_passed); + PR64(cfs_inject_q_out_passed); +} diff --git a/network_cmds-543/cfilutil/cfilutil.1 b/network_cmds-543/cfilutil/cfilutil.1 new file mode 100644 index 00000000..0d97adf1 --- /dev/null +++ b/network_cmds-543/cfilutil/cfilutil.1 @@ -0,0 +1,56 @@ +.Dd 2/10/14 +.Dt cfilutil 1 +.Os Darwin +.Sh NAME +.Nm cfilutil +.Nd Tool to exercise the content filter subsystem. +.Sh SYNOPSIS +.Nm +.Op Fl hilqsv +.Fl u Ar unit +.Op Fl a Ar offset +.Op Fl d Ar offset value +.Op Fl k Ar increment +.Op Fl m Ar length +.Op Fl p Ar offset +.Op Fl r Ar random +.Op Fl t Ar delay +.Sh DESCRIPTION +Use +.Nm +to exercise the content filter subsystem. +.Pp +The flags have the following meaning: +.Bl -tag -width -indent +.It Fl a Ar offset +Auto start filtering with given offset. +.It Fl a Ar offset value +Default values for offset passin, peekin, passout, peekout, pass or peek. +.It Fl h +Display this help. +.It Fl i +Interactive mode. +.It Fl k Ar increment +Peek mode with increment. +.It Fl l +Pass loopback traffic. +.It Fl m Ar length +Maximum dump length. +.It Fl p Ar offset +Pass mode (all or after given offset if it is > 0). +.It Fl q +Decrease verbosity. +.It Fl r Ar rate +Random drop rate. +.It Fl s +display content filter statistics (all, sock, filt, cfil). +.It Fl t Ar delay +Pass delay in microseconds. +.It Fl u Ar unit +NECP filter control unit. +.It Fl v +Increase verbosity. +.El +.Pp +.Sh SEE ALSO +.Xr neutil 1 \" rdar://16115914 diff --git a/network_cmds-543/cfilutil/cfilutil.c b/network_cmds-543/cfilutil/cfilutil.c new file mode 100644 index 00000000..4aaa7193 --- /dev/null +++ b/network_cmds-543/cfilutil/cfilutil.c @@ -0,0 +1,987 @@ +/* + * Copyright (c) 2013-2016 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern void print_filter_list(void); +extern void print_socket_list(void); +extern void print_cfil_stats(void); + +#define MAX_BUFFER (65536 + 1024) + +#define MAXHEXDUMPCOL 16 + + +enum { + MODE_NONE = 0, + MODE_INTERACTIVE = 0x01, + MODE_PEEK = 0x02, + MODE_PASS = 0x04, + MODE_DELAY = 0x08 +}; +int mode = MODE_NONE; + +unsigned long delay_ms = 0; +struct timeval delay_tv = { 0, 0 }; +long verbosity = 0; +uint32_t necp_control_unit = 0; +unsigned long auto_start = 0; +uint64_t peek_inc = 0; +uint64_t pass_offset = 0; +struct timeval now, deadline; +int sf = -1; +int pass_loopback = 0; +uint32_t random_drop = 0; +uint32_t event_total = 0; +uint32_t event_dropped = 0; + +uint64_t default_in_pass = 0; +uint64_t default_in_peek = 0; +uint64_t default_out_pass = 0; +uint64_t default_out_peek = 0; + +unsigned long max_dump_len = 32; + +TAILQ_HEAD(sock_info_head, sock_info) sock_info_head = TAILQ_HEAD_INITIALIZER(sock_info_head); + + +struct sock_info { + TAILQ_ENTRY(sock_info) si_link; + cfil_sock_id_t si_sock_id; + struct timeval si_deadline; + uint64_t si_in_pass; + uint64_t si_in_peek; + uint64_t si_out_pass; + uint64_t si_out_peek; +}; + +static void +HexDump(void *data, size_t len) +{ + size_t i, j, k; + unsigned char *ptr = (unsigned char *)data; + unsigned char buf[32 + 3 * MAXHEXDUMPCOL + 2 + MAXHEXDUMPCOL + 1]; + + for (i = 0; i < len; i += MAXHEXDUMPCOL) { + k = snprintf((char *)buf, sizeof(buf), "\t0x%04lx: ", i); + for (j = i; j < i + MAXHEXDUMPCOL; j++) { + if (j < len) { + unsigned char msnbl = ptr[j] >> 4; + unsigned char lsnbl = ptr[j] & 0x0f; + + buf[k++] = msnbl < 10 ? msnbl + '0' : msnbl + 'a' - 10; + buf[k++] = lsnbl < 10 ? lsnbl + '0' : lsnbl + 'a' - 10; + } else { + buf[k++] = ' '; + buf[k++] = ' '; + } + if ((j % 2) == 1) + buf[k++] = ' '; + if ((j % MAXHEXDUMPCOL) == MAXHEXDUMPCOL - 1) + buf[k++] = ' '; + } + + buf[k++] = ' '; + buf[k++] = ' '; + + for (j = i; j < i + MAXHEXDUMPCOL && j < len; j++) { + if (isprint(ptr[j])) + buf[k++] = ptr[j]; + else + buf[k++] = '.'; + } + buf[k] = 0; + printf("%s\n", buf); + } +} + +void +print_hdr(struct cfil_msg_hdr *hdr) +{ + const char *typestr = "unknown"; + const char *opstr = "unknown"; + + if (hdr->cfm_type == CFM_TYPE_EVENT) { + typestr = "event"; + switch (hdr->cfm_op) { + case CFM_OP_SOCKET_ATTACHED: + opstr = "attached"; + break; + case CFM_OP_SOCKET_CLOSED: + opstr = "closed"; + break; + case CFM_OP_DATA_OUT: + opstr = "dataout"; + break; + case CFM_OP_DATA_IN: + opstr = "datain"; + break; + case CFM_OP_DISCONNECT_OUT: + opstr = "disconnectout"; + break; + case CFM_OP_DISCONNECT_IN: + opstr = "disconnectin"; + break; + + default: + break; + } + } else if (hdr->cfm_type == CFM_TYPE_ACTION) { + typestr = "action"; + switch (hdr->cfm_op) { + case CFM_OP_DATA_UPDATE: + opstr = "update"; + break; + case CFM_OP_DROP: + opstr = "drop"; + break; + + default: + break; + } + + } + printf("%s %s len %u version %u type %u op %u sock_id 0x%llx\n", + typestr, opstr, + hdr->cfm_len, hdr->cfm_version, hdr->cfm_type, + hdr->cfm_op, hdr->cfm_sock_id); +} + +void +print_data_req(struct cfil_msg_data_event *data_req) +{ + size_t datalen; + void *databuf; + + if (verbosity <= 0) + return; + + print_hdr(&data_req->cfd_msghdr); + + printf(" start %llu end %llu\n", + data_req->cfd_start_offset, data_req->cfd_end_offset); + + datalen = (size_t)(data_req->cfd_end_offset - data_req->cfd_start_offset); + + databuf = (void *)(data_req + 1); + + if (verbosity > 1) + HexDump(databuf, MIN(datalen, max_dump_len)); +} + +void +print_action_msg(struct cfil_msg_action *action) +{ + if (verbosity <= 0) + return; + + print_hdr(&action->cfa_msghdr); + + if (action->cfa_msghdr.cfm_op == CFM_OP_DATA_UPDATE) + printf(" out pass %llu peek %llu in pass %llu peek %llu\n", + action->cfa_out_pass_offset, action->cfa_out_peek_offset, + action->cfa_in_pass_offset, action->cfa_in_peek_offset); +} + +struct sock_info * +find_sock_info(cfil_sock_id_t sockid) +{ + struct sock_info *sock_info; + + TAILQ_FOREACH(sock_info, &sock_info_head, si_link) { + if (sock_info->si_sock_id == sockid) + return (sock_info); + } + return (NULL); +} + +struct sock_info * +add_sock_info(cfil_sock_id_t sockid) +{ + struct sock_info *sock_info; + + if (find_sock_info(sockid) != NULL) + return (NULL); + + sock_info = calloc(1, sizeof(struct sock_info)); + if (sock_info == NULL) + err(EX_OSERR, "calloc()"); + sock_info->si_sock_id = sockid; + TAILQ_INSERT_TAIL(&sock_info_head, sock_info, si_link); + + return (sock_info); +} + +void +remove_sock_info(cfil_sock_id_t sockid) +{ + struct sock_info *sock_info = find_sock_info(sockid); + + if (sock_info != NULL) { + TAILQ_REMOVE(&sock_info_head, sock_info, si_link); + free(sock_info); + } +} + +/* return 0 if timer is already set */ +int +set_sock_info_deadline(struct sock_info *sock_info) +{ + if (timerisset(&sock_info->si_deadline)) + return (0); + + timeradd(&now, &sock_info->si_deadline, &sock_info->si_deadline); + + if (!timerisset(&deadline)) { + timeradd(&now, &delay_tv, &deadline); + } + + return (1); +} + +void +send_action_message(uint32_t op, struct sock_info *sock_info, int nodelay) +{ + struct cfil_msg_action action; + + if (!nodelay && delay_ms) { + set_sock_info_deadline(sock_info); + return; + } + bzero(&action, sizeof(struct cfil_msg_action)); + action.cfa_msghdr.cfm_len = sizeof(struct cfil_msg_action); + action.cfa_msghdr.cfm_version = CFM_VERSION_CURRENT; + action.cfa_msghdr.cfm_type = CFM_TYPE_ACTION; + action.cfa_msghdr.cfm_op = op; + action.cfa_msghdr.cfm_sock_id = sock_info->si_sock_id; + switch (op) { + case CFM_OP_DATA_UPDATE: + action.cfa_out_pass_offset = sock_info->si_out_pass; + action.cfa_out_peek_offset = sock_info->si_out_peek; + action.cfa_in_pass_offset = sock_info->si_in_pass; + action.cfa_in_peek_offset = sock_info->si_in_peek; + break; + + default: + break; + } + + if (verbosity > -1) + print_action_msg(&action); + + if (send(sf, &action, sizeof(struct cfil_msg_action), 0) == -1) + warn("send()"); + + timerclear(&sock_info->si_deadline); +} + +void +process_delayed_actions() +{ + struct sock_info *sock_info; + + TAILQ_FOREACH(sock_info, &sock_info_head, si_link) { + if (timerisset(&sock_info->si_deadline) && + timercmp(&sock_info->si_deadline, &now, >=)) + send_action_message(CFM_OP_DATA_UPDATE, sock_info, 1); + } +} + +int +set_non_blocking(int fd) +{ + int flags; + + flags = fcntl(fd, F_GETFL); + if (flags == -1) { + warn("fcntl(F_GETFL)"); + return (-1); + } + flags |= O_NONBLOCK; + if (fcntl(fd, F_SETFL, flags) == -1) { + warn("fcntl(F_SETFL)"); + return (-1); + } + return (0); +} + +int +offset_from_str(const char *str, uint64_t *ret_val) +{ + char *endptr; + uint64_t offset; + int success = 1; + + if (strcasecmp(str, "max") == 0 || strcasecmp(str, "all") == 0) + offset = CFM_MAX_OFFSET; + else { + offset = strtoull(str, &endptr, 0); + if (*str == '\0' || *endptr != '\0') + success = 0; + } + if (success) + *ret_val = offset; + return (success); +} + +#define IN6_IS_ADDR_V4MAPPED_LOOPBACK(a) \ + ((*(const __uint32_t *)(const void *)(&(a)->s6_addr[0]) == 0) && \ + (*(const __uint32_t *)(const void *)(&(a)->s6_addr[4]) == 0) && \ + (*(const __uint32_t *)(const void *)(&(a)->s6_addr[8]) == ntohl(0x0000ffff)) && \ + (*(const __uint32_t *)(const void *)(&(a)->s6_addr[12]) == ntohl(INADDR_LOOPBACK))) + + +int +is_loopback(struct cfil_msg_data_event *data_req) +{ + if (data_req->cfc_dst.sa.sa_family == AF_INET && + ntohl(data_req->cfc_dst.sin.sin_addr.s_addr) == INADDR_LOOPBACK) + return (1); + if (data_req->cfc_dst.sa.sa_family == AF_INET6 && + IN6_IS_ADDR_LOOPBACK(&data_req->cfc_dst.sin6.sin6_addr)) + return (1); + if (data_req->cfc_dst.sa.sa_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED_LOOPBACK(&data_req->cfc_dst.sin6.sin6_addr)) + return (1); + + if (data_req->cfc_src.sa.sa_family == AF_INET && + ntohl(data_req->cfc_src.sin.sin_addr.s_addr) == INADDR_LOOPBACK) + return (1); + if (data_req->cfc_src.sa.sa_family == AF_INET6 && + IN6_IS_ADDR_LOOPBACK(&data_req->cfc_src.sin6.sin6_addr)) + return (1); + if (data_req->cfc_src.sa.sa_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED_LOOPBACK(&data_req->cfc_src.sin6.sin6_addr)) + return (1); + + return (0); +} + +int +drop(struct sock_info *sock_info) +{ + event_total++; + if (random_drop > 0) { + uint32_t r = arc4random(); + if (r <= random_drop) { + event_dropped++; + printf("dropping 0x%llx dropped %u total %u rate %f\n", + sock_info->si_sock_id, + event_dropped, event_total, + (double)event_dropped/(double)event_total * 100); + send_action_message(CFM_OP_DROP, sock_info, 0); + return (1); + } + } + return (0); +} + +int +doit() +{ + struct sockaddr_ctl sac; + struct ctl_info ctl_info; + void *buffer = NULL; + struct cfil_msg_hdr *hdr; + int kq = -1; + struct kevent kv; + int fdin = fileno(stdin); + char *linep = NULL; + size_t linecap = 0; + char *cmdptr = NULL; + char *argptr = NULL; + size_t cmdlen = 0; + struct cfil_msg_action action; + cfil_sock_id_t last_sock_id = 0; + struct sock_info *sock_info = NULL; + struct timeval last_time, elapsed, delta; + struct timespec interval, *timeout = NULL; + + kq = kqueue(); + if (kq == -1) + err(1, "kqueue()"); + + sf = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); + if (sf == -1) + err(1, "socket()"); + + bzero(&ctl_info, sizeof(struct ctl_info)); + strlcpy(ctl_info.ctl_name, CONTENT_FILTER_CONTROL_NAME, sizeof(ctl_info.ctl_name)); + if (ioctl(sf, CTLIOCGINFO, &ctl_info) == -1) + err(1, "ioctl(CTLIOCGINFO)"); + + if (fcntl(sf, F_SETNOSIGPIPE, 1) == -1) + err(1, "fcntl(F_SETNOSIGPIPE)"); + + bzero(&sac, sizeof(struct sockaddr_ctl)); + sac.sc_len = sizeof(struct sockaddr_ctl); + sac.sc_family = AF_SYSTEM; + sac.ss_sysaddr = AF_SYS_CONTROL; + sac.sc_id = ctl_info.ctl_id; + + if (connect(sf, (struct sockaddr *)&sac, sizeof(struct sockaddr_ctl)) == -1) + err(1, "connect()"); + + if (set_non_blocking(sf) == -1) + err(1, "set_non_blocking(sf)"); + + if (setsockopt(sf, SYSPROTO_CONTROL, CFIL_OPT_NECP_CONTROL_UNIT, + &necp_control_unit, sizeof(uint32_t)) == -1) + err(1, "setsockopt(CFIL_OPT_NECP_CONTROL_UNIT, %u)", necp_control_unit); + + bzero(&kv, sizeof(struct kevent)); + kv.ident = sf; + kv.filter = EVFILT_READ; + kv.flags = EV_ADD; + if (kevent(kq, &kv, 1, NULL, 0, NULL) == -1) + err(1, "kevent(sf %d)", sf); + + /* + * We can only read from an interactive terminal + */ + if (isatty(fdin)) { + bzero(&kv, sizeof(struct kevent)); + kv.ident = fdin; + kv.filter = EVFILT_READ; + kv.flags = EV_ADD; + if (kevent(kq, &kv, 1, NULL, 0, NULL) == -1) + err(1, "kevent(fdin %d)", fdin); + } + + buffer = malloc(MAX_BUFFER); + if (buffer == NULL) + err(1, "malloc()"); + + gettimeofday(&now, NULL); + + while (1) { + last_time = now; + if (delay_ms && timerisset(&deadline)) { + timersub(&deadline, &now, &delta); + TIMEVAL_TO_TIMESPEC(&delta, &interval); + timeout = &interval; + } else { + timeout = NULL; + } + + if (kevent(kq, NULL, 0, &kv, 1, timeout) == -1) { + if (errno == EINTR) + continue; + err(1, "kevent()"); + } + gettimeofday(&now, NULL); + timersub(&now, &last_time, &elapsed); + if (delay_ms && timerisset(&deadline)) { + if (timercmp(&now, &deadline, >=)) { + process_delayed_actions(); + interval.tv_sec = 0; + interval.tv_nsec = 0; + } + } + + if (kv.ident == sf && kv.filter == EVFILT_READ) { + while (1) { + ssize_t nread; + + nread = recv(sf, buffer, MAX_BUFFER, 0); + if (nread == 0) { + warnx("recv(sf) returned 0, connection closed"); + break; + } + if (nread == -1) { + if (errno == EINTR) + continue; + if (errno == EWOULDBLOCK) + break; + err(1, "recv()"); + + } + if (nread < sizeof(struct cfil_msg_hdr)) + errx(1, "too small"); + hdr = (struct cfil_msg_hdr *)buffer; + + + if (hdr->cfm_type != CFM_TYPE_EVENT) { + warnx("not a content filter event type %u", hdr->cfm_type); + continue; + } + switch (hdr->cfm_op) { + case CFM_OP_SOCKET_ATTACHED: { + struct cfil_msg_sock_attached *msg_attached = (struct cfil_msg_sock_attached *)hdr; + + if (verbosity > -2) + print_hdr(hdr); + if (verbosity > -1) + printf(" fam %d type %d proto %d pid %u epid %u\n", + msg_attached->cfs_sock_family, + msg_attached->cfs_sock_type, + msg_attached->cfs_sock_protocol, + msg_attached->cfs_pid, + msg_attached->cfs_e_pid); + break; + } + case CFM_OP_SOCKET_CLOSED: + case CFM_OP_DISCONNECT_IN: + case CFM_OP_DISCONNECT_OUT: + if (verbosity > -2) + print_hdr(hdr); + break; + case CFM_OP_DATA_OUT: + case CFM_OP_DATA_IN: + if (verbosity > -3) + print_data_req((struct cfil_msg_data_event *)hdr); + break; + default: + warnx("unknown content filter event op %u", hdr->cfm_op); + continue; + } + switch (hdr->cfm_op) { + case CFM_OP_SOCKET_ATTACHED: + sock_info = add_sock_info(hdr->cfm_sock_id); + if (sock_info == NULL) { + warnx("sock_id %llx already exists", hdr->cfm_sock_id); + continue; + } + break; + case CFM_OP_DATA_OUT: + case CFM_OP_DATA_IN: + case CFM_OP_DISCONNECT_IN: + case CFM_OP_DISCONNECT_OUT: + case CFM_OP_SOCKET_CLOSED: + sock_info = find_sock_info(hdr->cfm_sock_id); + + if (sock_info == NULL) { + warnx("unexpected data message, sock_info is NULL"); + continue; + } + break; + default: + warnx("unknown content filter event op %u", hdr->cfm_op); + continue; + } + + + switch (hdr->cfm_op) { + case CFM_OP_SOCKET_ATTACHED: { + if ((mode & MODE_PASS) || (mode & MODE_PEEK) || auto_start) { + sock_info->si_out_pass = default_out_pass; + sock_info->si_out_peek = (mode & MODE_PEEK) ? peek_inc : (mode & MODE_PASS) ? CFM_MAX_OFFSET : default_out_peek; + sock_info->si_in_pass = default_in_pass; + sock_info->si_in_peek = (mode & MODE_PEEK) ? peek_inc : (mode & MODE_PASS) ? CFM_MAX_OFFSET : default_in_peek; + + send_action_message(CFM_OP_DATA_UPDATE, sock_info, 0); + } + break; + } + case CFM_OP_SOCKET_CLOSED: { + remove_sock_info(hdr->cfm_sock_id); + sock_info = NULL; + break; + } + case CFM_OP_DATA_OUT: + case CFM_OP_DATA_IN: { + struct cfil_msg_data_event *data_req = (struct cfil_msg_data_event *)hdr; + + if (pass_loopback && is_loopback(data_req)) { + sock_info->si_out_pass = CFM_MAX_OFFSET; + sock_info->si_in_pass = CFM_MAX_OFFSET; + } else { + if (drop(sock_info)) + continue; + + if ((mode & MODE_PASS)) { + if (data_req->cfd_msghdr.cfm_op == CFM_OP_DATA_OUT) { + if (pass_offset == 0 || pass_offset == CFM_MAX_OFFSET) + sock_info->si_out_pass = data_req->cfd_end_offset; + else if (data_req->cfd_end_offset > pass_offset) { + sock_info->si_out_pass = CFM_MAX_OFFSET; + sock_info->si_in_pass = CFM_MAX_OFFSET; + } + sock_info->si_out_peek = (mode & MODE_PEEK) ? + data_req->cfd_end_offset + peek_inc : 0; + } else { + if (pass_offset == 0 || pass_offset == CFM_MAX_OFFSET) + sock_info->si_in_pass = data_req->cfd_end_offset; + else if (data_req->cfd_end_offset > pass_offset) { + sock_info->si_out_pass = CFM_MAX_OFFSET; + sock_info->si_in_pass = CFM_MAX_OFFSET; + } + sock_info->si_in_peek = (mode & MODE_PEEK) ? + data_req->cfd_end_offset + peek_inc : 0; + } + } else { + break; + } + } + send_action_message(CFM_OP_DATA_UPDATE, sock_info, 0); + + break; + } + case CFM_OP_DISCONNECT_IN: + case CFM_OP_DISCONNECT_OUT: { + if (drop(sock_info)) + continue; + + if ((mode & MODE_PASS)) { + sock_info->si_out_pass = CFM_MAX_OFFSET; + sock_info->si_in_pass = CFM_MAX_OFFSET; + + send_action_message(CFM_OP_DATA_UPDATE, sock_info, 0); + } + break; + } + default: + warnx("unkown message op %u", hdr->cfm_op); + break; + } + if (sock_info) + last_sock_id = sock_info->si_sock_id; + } + } + if (kv.ident == fdin && kv.filter == EVFILT_READ) { + ssize_t nread; + uint64_t offset = 0; + int nitems; + int op = 0; + + nread = getline(&linep, &linecap, stdin); + if (nread == -1) + errx(1, "getline()"); + + if (verbosity > 2) + printf("linecap %lu nread %lu\n", linecap, nread); + if (nread > 0) + linep[nread - 1] = '\0'; + + if (verbosity > 2) + HexDump(linep, linecap); + + if (*linep == 0) + continue; + + if (cmdptr == NULL || argptr == NULL || linecap > cmdlen) { + cmdlen = linecap; + cmdptr = realloc(cmdptr, cmdlen); + argptr = realloc(argptr, cmdlen); + } + + /* + * Trick to support unisgned and hexadecimal arguments + * as I can't figure out sscanf() conversions + */ + nitems = sscanf(linep, "%s %s", cmdptr, argptr); + if (nitems == 0) { + warnx("I didn't get that..."); + continue; + } else if (nitems > 1) { + if (offset_from_str(argptr, &offset) == 0) { + warnx("I didn't get that either..."); + continue; + } + } + if (verbosity > 2) + printf("nitems %d %s %s\n", nitems, cmdptr, argptr); + + bzero(&action, sizeof(struct cfil_msg_action)); + action.cfa_msghdr.cfm_len = sizeof(struct cfil_msg_action); + action.cfa_msghdr.cfm_version = CFM_VERSION_CURRENT; + action.cfa_msghdr.cfm_type = CFM_TYPE_ACTION; + + if (strcasecmp(cmdptr, "passout") == 0 && nitems > 1) { + op = CFM_OP_DATA_UPDATE; + action.cfa_out_pass_offset = offset; + } else if (strcasecmp(cmdptr, "passin") == 0 && nitems > 1) { + op = CFM_OP_DATA_UPDATE; + action.cfa_in_pass_offset = offset; + } else if (strcasecmp(cmdptr, "pass") == 0 && nitems > 1) { + op = CFM_OP_DATA_UPDATE; + action.cfa_out_pass_offset = offset; + action.cfa_in_pass_offset = offset; + } else if (strcasecmp(cmdptr, "peekout") == 0 && nitems > 1) { + op = CFM_OP_DATA_UPDATE; + action.cfa_out_peek_offset = offset; + } else if (strcasecmp(cmdptr, "peekin") == 0 && nitems > 1) { + op = CFM_OP_DATA_UPDATE; + action.cfa_in_peek_offset = offset; + } else if (strcasecmp(cmdptr, "peek") == 0 && nitems > 1) { + op = CFM_OP_DATA_UPDATE; + action.cfa_out_peek_offset = offset; + action.cfa_in_peek_offset = offset; + } else if (strcasecmp(cmdptr, "start") == 0) { + op = CFM_OP_DATA_UPDATE; + action.cfa_out_pass_offset = 0; + action.cfa_out_peek_offset = CFM_MAX_OFFSET; + action.cfa_in_pass_offset = 0; + action.cfa_in_peek_offset = CFM_MAX_OFFSET; + } else if (strcasecmp(cmdptr, "peekall") == 0) { + op = CFM_OP_DATA_UPDATE; + action.cfa_out_peek_offset = CFM_MAX_OFFSET; + action.cfa_in_peek_offset = CFM_MAX_OFFSET; + } else if (strcasecmp(cmdptr, "passall") == 0) { + op = CFM_OP_DATA_UPDATE; + action.cfa_out_pass_offset = CFM_MAX_OFFSET; + action.cfa_out_peek_offset = CFM_MAX_OFFSET; + action.cfa_in_pass_offset = CFM_MAX_OFFSET; + action.cfa_in_peek_offset = CFM_MAX_OFFSET; + } else if (strcasecmp(cmdptr, "drop") == 0) + op = CFM_OP_DROP; + else if (strcasecmp(cmdptr, "sock") == 0) { + last_sock_id = offset; + printf("last_sock_id 0x%llx\n", last_sock_id); + } else + warnx("syntax error"); + + if (op == CFM_OP_DATA_UPDATE || op == CFM_OP_DROP) { + action.cfa_msghdr.cfm_op = op; + action.cfa_msghdr.cfm_sock_id = last_sock_id; + print_action_msg(&action); + + if (send(sf, &action, sizeof(struct cfil_msg_action), 0) == -1) + warn("send()"); + } + } + } + + return 0; +} + +static const char * +basename(const char * str) +{ + const char *last_slash = strrchr(str, '/'); + + if (last_slash == NULL) + return (str); + else + return (last_slash + 1); +} + +struct option_desc { + const char *option; + const char *description; + int required; +}; + +struct option_desc option_desc_list[] = { + { "-a offset", "auto start with offset", 0 }, + { "-d offset value", "default offset value for passin, peekin, passout, peekout, pass, peek", 0 }, + { "-h", "dsiplay this help", 0 }, + { "-i", "interactive mode", 0 }, + { "-k increment", "peek mode with increment", 0 }, + {"-l", "pass loopback", 0 }, + { "-m length", "max dump length", 0 }, + { "-p offset", "pass mode (all or after given offset if > 0)", 0 }, + { "-q", "decrease verbose level", 0 }, + { "-r random", "random drop rate", 0 }, + { "-s ", "display content filter statistics (all, sock, filt, cfil)", 0 }, + { "-t delay", "pass delay in microseconds", 0 }, + { "-u unit", "NECP filter control unit", 1 }, + { "-v", "increase verbose level", 0 }, + { NULL, NULL, 0 } /* Mark end of list */ +}; + +static void +usage(const char *cmd) +{ + struct option_desc *option_desc; + char *usage_str = malloc(LINE_MAX); + size_t usage_len; + + if (usage_str == NULL) + err(1, "%s: malloc(%d)", __func__, LINE_MAX); + + usage_len = snprintf(usage_str, LINE_MAX, "# usage: %s ", basename(cmd)); + + for (option_desc = option_desc_list; option_desc->option != NULL; option_desc++) { + int len; + + if (option_desc->required) + len = snprintf(usage_str + usage_len, LINE_MAX - usage_len, "%s ", option_desc->option); + else + len = snprintf(usage_str + usage_len, LINE_MAX - usage_len, "[%s] ", option_desc->option); + if (len < 0) + err(1, "%s: snprintf(", __func__); + + usage_len += len; + if (usage_len > LINE_MAX) + break; + } + printf("%s\n", usage_str); + printf("options:\n"); + + for (option_desc = option_desc_list; option_desc->option != NULL; option_desc++) { + printf(" %-20s # %s\n", option_desc->option, option_desc->description); + } + +} + +int +main(int argc, char * const argv[]) +{ + int ch; + double d; + int stats_sock_list = 0; + int stats_filt_list = 0; + int stats_cfil_stats = 0; + + while ((ch = getopt(argc, argv, "a:d:hik:lm:p:qr:s:t:u:v")) != -1) { + switch (ch) { + case 'a': + auto_start = strtoul(optarg, NULL, 0); + break; + case 'd': { + if (optind >= argc) + errx(1, "'-d' needs 2 parameters"); + if (strcasecmp(optarg, "passout") == 0) { + if (offset_from_str(argv[optind], &default_out_pass) == 0) + errx(1, "bad %s offset: %s", optarg, argv[optind + 1]); + } else if (strcasecmp(optarg, "passin") == 0) { + if (offset_from_str(argv[optind], &default_in_pass) == 0) + errx(1, "bad %s offset: %s", optarg, argv[optind + 1]); + } else if (strcasecmp(optarg, "pass") == 0) { + if (offset_from_str(argv[optind], &default_out_pass) == 0) + errx(1, "bad %s offset: %s", optarg, argv[optind + 1]); + default_in_pass = default_out_pass; + } else if (strcasecmp(optarg, "peekout") == 0) { + if (offset_from_str(argv[optind], &default_out_peek) == 0) + errx(1, "bad %s offset: %s", optarg, argv[optind + 1]); + } else if (strcasecmp(optarg, "peekin") == 0) { + if (offset_from_str(argv[optind], &default_in_peek) == 0) + errx(1, "bad %s offset: %s", optarg, argv[optind + 1]); + } else if (strcasecmp(optarg, "peek") == 0) { + if (offset_from_str(argv[optind], &default_out_peek) == 0) + errx(1, "bad %s offset: %s", optarg, argv[optind + 1]); + default_in_peek = default_out_peek; + } else + errx(1, "syntax error"); + break; + } + case 'h': + usage(argv[0]); + exit(0); + case 'i': + mode |= MODE_INTERACTIVE; + break; + case 'k': + mode |= MODE_PEEK; + if (offset_from_str(optarg, &peek_inc) == 0) + errx(1, "bad peek offset: %s", optarg); + break; + case 'l': + pass_loopback = 1; + break; + case 'm': + max_dump_len = strtoul(optarg, NULL, 0); + break; + case 'p': + mode |= MODE_PASS; + if (offset_from_str(optarg, &pass_offset) == 0) + errx(1, "bad pass offset: %s", optarg); + break; + case 'q': + verbosity--; + break; + case 'r': + d = strtod(optarg, NULL); + if (d < 0 || d > 1) + errx(1, "bad drop rate: %s -- it must be between 0 and 1", optarg); + random_drop = (uint32_t)(d * UINT32_MAX); + break; + case 's': + if (strcasecmp(optarg, "all") == 0) { + stats_sock_list = 1; + stats_filt_list = 1; + stats_cfil_stats = 1; + } else if (strcasecmp(optarg, "sock") == 0) { + stats_sock_list = 1; + } else if (strcasecmp(optarg, "filt") == 0) { + stats_filt_list = 1; + } else if (strcasecmp(optarg, "cfil") == 0) { + stats_cfil_stats = 1; + } else { + warnx("# Error: unknown type of statistic: %s", optarg); + usage(argv[0]); + exit(0); + } + break; + case 't': + mode |= MODE_DELAY; + delay_ms = strtoul(optarg, NULL, 0); + delay_tv.tv_sec = delay_ms / 1000; + delay_tv.tv_usec = (delay_ms % 1000) * 1000; + break; + case 'u': + necp_control_unit = (uint32_t)strtoul(optarg, NULL, 0); + break; + case 'v': + verbosity++; + break; + default: + errx(1, "# syntax error, unknow option '%d'", ch); + usage(argv[0]); + exit(0); + } + } + + if (stats_filt_list) + print_filter_list(); + if (stats_sock_list) + print_socket_list(); + if (stats_cfil_stats) + print_cfil_stats(); + if (necp_control_unit == 0 && (stats_filt_list || stats_sock_list || stats_cfil_stats)) + return (0); + + if (necp_control_unit == 0) { + warnx("necp filter control unit is 0"); + usage(argv[0]); + exit(EX_USAGE); + } + doit(); + + + return (0); +} + diff --git a/network_cmds-543/dnctl/dnctl.8 b/network_cmds-543/dnctl/dnctl.8 new file mode 100644 index 00000000..4da47241 --- /dev/null +++ b/network_cmds-543/dnctl/dnctl.8 @@ -0,0 +1,508 @@ +.Dd August 13, 2002 +.Dt DNCTL 8 +.Os Darwin +.Sh NAME +.Nm dnctl +.Nd Traffic shaper control program +.Sh SYNOPSIS +.Nm +.Op Fl anqs +.Brq Cm list | show +.Nm +.Op Fl f | q +.Cm flush +.Nm +.Op Fl q +.Brq Cm delete +.Op Ar number ... +.Nm +.Brq Cm pipe | queue +.Ar number +.Cm config +.Ar config-options +.Nm +.Op Fl s Op Ar field +.Brq Cm pipe | queue +.Brq Cm delete | list | show +.Op Ar number ... +.Nm +.Op Fl nq +.Oo +.Fl p Ar preproc +.Oo +.Ar preproc-flags +.Oc +.Oc +.Ar pathname +.Sh DESCRIPTION +.Pp +The +.Nm +utility is the user interface for controlling the +.Xr dummynet 4 +traffic shaper. +.Pp +.Nm dummynet +operates by first using a packet filter to classify packets and divide them into +.Em flows , +using any match pattern that can be used in +.Nm +rules. +Depending on local policies, a flow can contain packets for a single +TCP connection, or from/to a given host, or entire subnet, or a +protocol type, etc. +.Pp +Packets belonging to the same flow are then passed to either of two +different objects, which implement the traffic regulation: +.Bl -hang -offset XXXX +.It Em pipe +A pipe emulates a link with given bandwidth, propagation delay, +queue size and packet loss rate. +Packets are queued in front of the pipe as they come out from the classifier, +and then transferred to the pipe according to the pipe's parameters. +.Pp +.It Em queue +A queue +is an abstraction used to implement the WF2Q+ +(Worst-case Fair Weighted Fair Queueing) policy, which is +an efficient variant of the WFQ policy. +.br +The queue associates a +.Em weight +and a reference pipe to each flow, and then all backlogged (i.e., +with packets queued) flows linked to the same pipe share the pipe's +bandwidth proportionally to their weights. +Note that weights are not priorities; a flow with a lower weight +is still guaranteed to get its fraction of the bandwidth even if a +flow with a higher weight is permanently backlogged. +.Pp +.El +In practice, +.Em pipes +can be used to set hard limits to the bandwidth that a flow can use, whereas +.Em queues +can be used to determine how different flow share the available bandwidth. +.Pp +The +.Em pipe +and +.Em queue +configuration commands are the following: +.Bd -ragged -offset indent +.Cm pipe Ar number Cm config Ar pipe-configuration +.Pp +.Cm queue Ar number Cm config Ar queue-configuration +.Ed +.Pp +The following parameters can be configured for a pipe: +.Pp +.Bl -tag -width indent -compact +.It Cm bw Ar bandwidth | device +Bandwidth, measured in +.Sm off +.Op Cm K | M +.Brq Cm bit/s | Byte/s . +.Sm on +.Pp +A value of 0 (default) means unlimited bandwidth. +The unit must immediately follow the number, as in +.Pp +.Dl "dnctl pipe 1 config bw 300Kbit/s" +.Pp +If a device name is specified instead of a numeric value, as in +.Pp +.Dl "dnctl pipe 1 config bw tun0" +.Pp +then the transmit clock is supplied by the specified device. +At the moment no +device supports this +functionality. +.Pp +.It Cm delay Ar ms-delay +Propagation delay, measured in milliseconds. +The value is rounded to the next multiple of the clock tick +(typically 10ms, but it is a good practice to run kernels +with +.Dq "options HZ=1000" +to reduce +the granularity to 1ms or less). +Default value is 0, meaning no delay. +.El +.Pp +The following parameters can be configured for a queue: +.Pp +.Bl -tag -width indent -compact +.It Cm pipe Ar pipe_nr +Connects a queue to the specified pipe. +Multiple queues (with the same or different weights) can be connected to +the same pipe, which specifies the aggregate rate for the set of queues. +.Pp +.It Cm weight Ar weight +Specifies the weight to be used for flows matching this queue. +The weight must be in the range 1..100, and defaults to 1. +.El +.Pp +Finally, the following parameters can be configured for both +pipes and queues: +.Pp +.Bl -tag -width XXXX -compact +.It Cm buckets Ar hash-table-size +Specifies the size of the hash table used for storing the +various queues. +Default value is 64 controlled by the +.Xr sysctl 8 +variable +.Em net.inet.ip.dummynet.hash_size , +allowed range is 16 to 65536. +.Pp +.It Cm mask Ar mask-specifier +Packets sent to a given pipe or queue by an +.Nm +rule can be further classified into multiple flows, each of which is then +sent to a different +.Em dynamic +pipe or queue. +A flow identifier is constructed by masking the IP addresses, +ports and protocol types as specified with the +.Cm mask +options in the configuration of the pipe or queue. +For each different flow identifier, a new pipe or queue is created +with the same parameters as the original object, and matching packets +are sent to it. +.Pp +Thus, when +.Em dynamic pipes +are used, each flow will get the same bandwidth as defined by the pipe, +whereas when +.Em dynamic queues +are used, each flow will share the parent's pipe bandwidth evenly +with other flows generated by the same queue (note that other queues +with different weights might be connected to the same pipe). +.br +Available mask specifiers are a combination of one or more of the following: +.Pp +.Cm dst-ip Ar mask , +.Cm dst-ip6 Ar mask , +.Cm src-ip Ar mask , +.Cm src-ip6 Ar mask , +.Cm dst-port Ar mask , +.Cm src-port Ar mask , +.Cm proto Ar mask +or +.Cm all , +.Pp +where the latter means all bits in all fields are significant. +.Pp +.It Cm noerror +When a packet is dropped by a dummynet queue or pipe, the error +is normally reported to the caller routine in the kernel, in the +same way as it happens when a device queue fills up. Setting this +option reports the packet as successfully delivered, which can be +needed for some experimental setups where you want to simulate +loss or congestion at a remote router. +.Pp +.It Cm plr Ar packet-loss-rate +Packet loss rate. +Argument +.Ar packet-loss-rate +is a floating-point number between 0 and 1, with 0 meaning no +loss, 1 meaning 100% loss. +The loss rate is internally represented on 31 bits. +.Pp +.It Cm queue Brq Ar slots | size Ns Cm Kbytes +Queue size, in +.Ar slots +or +.Cm KBytes . +Default value is 50 slots, which +is the typical queue size for Ethernet devices. +Note that for slow speed links you should keep the queue +size short or your traffic might be affected by a significant +queueing delay. +E.g., 50 max-sized ethernet packets (1500 bytes) mean 600Kbit +or 20s of queue on a 30Kbit/s pipe. +Even worse effect can result if you get packets from an +interface with a much larger MTU, e.g. the loopback interface +with its 16KB packets. +.Pp +.It Cm red | gred Ar w_q Ns / Ns Ar min_th Ns / Ns Ar max_th Ns / Ns Ar max_p +Make use of the RED (Random Early Detection) queue management algorithm. +.Ar w_q +and +.Ar max_p +are floating +point numbers between 0 and 1 (0 not included), while +.Ar min_th +and +.Ar max_th +are integer numbers specifying thresholds for queue management +(thresholds are computed in bytes if the queue has been defined +in bytes, in slots otherwise). +The +.Xr dummynet 4 +also supports the gentle RED variant (gred). +.Pp +Three +.Xr sysctl 8 +variables can be used to control the RED behaviour: +.Bl -tag -width indent +.It Em net.inet.ip.dummynet.red_lookup_depth +specifies the accuracy in computing the average queue +when the link is idle (defaults to 256, must be greater than zero) +.It Em net.inet.ip.dummynet.red_avg_pkt_size +specifies the expected average packet size (defaults to 512, must be +greater than zero) +.It Em net.inet.ip.dummynet.red_max_pkt_size +specifies the expected maximum packet size, only used when queue +thresholds are in bytes (defaults to 1500, must be greater than zero). +.El +.El +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl a +While listing, show counter values. +The +.Cm show +command just implies this option. +.It Fl f +Don't ask for confirmation for commands that can cause problems +if misused, +.No i.e. Cm flush . +If there is no tty associated with the process, this is implied. +.It Fl h +Displays a short help. +.It Fl n +Only check syntax of the command strings, without actually passing +them to the kernel. +.It Fl q +While +.Cm add Ns ing , +.Cm zero Ns ing , +.Cm resetlog Ns ging +or +.Cm flush Ns ing , +be quiet about actions +(implies +.Fl f ) . +This is useful for adjusting rules by executing multiple +.Nm +commands in a script +or by processing a file of many +.Nm +rules across a remote login session. +If a +.Cm flush +is performed in normal (verbose) mode (with the default kernel +configuration), it prints a message. +Because all rules are flushed, the message might not be delivered +to the login session, causing the remote login session to be closed +and the remainder of the ruleset to not be processed. +Access to the console would then be required to recover. +.It Fl s Op Ar field +While listing pipes, sort according to one of the four +counters (total or current packets or bytes). +.It Fl v +Be verbose. +.El +.Pp +To ease configuration, rules can be put into a file which is +processed using +.Nm +as shown in the last synopsis line. +An absolute +.Ar pathname +must be used. +The file will be read line by line and applied as arguments to the +.Nm +utility. +.Pp +Optionally, a preprocessor can be specified using +.Fl p Ar preproc +where +.Ar pathname +is to be piped through. +Useful preprocessors include +.Xr cpp 1 +and +.Xr m4 1 . +If +.Ar preproc +doesn't start with a slash +.Pq Ql / +as its first character, the usual +.Ev PATH +name search is performed. +Care should be taken with this in environments where not all +file systems are mounted (yet) by the time +.Nm +is being run (e.g. when they are mounted over NFS). +Once +.Fl p +has been specified, any additional arguments as passed on to the preprocessor +for interpretation. +This allows for flexible configuration files (like conditionalizing +them on the local hostname) and the use of macros to centralize +frequently required arguments like IP addresses. +.El +.Sh CHECKLIST +Here are some important points to consider when designing your +rules: +.Bl -bullet +.It +Remember that you filter both packets going +.Cm in +and +.Cm out . +Most connections need packets going in both directions. +.It +Remember to test very carefully. +It is a good idea to be near the console when doing this. +.It +Don't forget the loopback interface. +.El +.Sh SYSCTL VARIABLES +A set of +.Xr sysctl 8 +variables controls the behaviour of the +.Nm dummynet +module. +These are shown below together with their default value +(but always check with the +.Xr sysctl 8 +command what value is actually in use) and meaning: +.Bl -tag -width indent +.It Em net.inet.ip.dummynet.expire : No 1 +Lazily delete dynamic pipes/queue once they have no pending traffic. +You can disable this by setting the variable to 0, in which case +the pipes/queues will only be deleted when the threshold is reached. +.It Em net.inet.ip.dummynet.hash_size : No 64 +Default size of the hash table used for dynamic pipes/queues. +This value is used when no +.Cm buckets +option is specified when configuring a pipe/queue. +.It Em net.inet.ip.dummynet.max_chain_len : No 16 +Target value for the maximum number of pipes/queues in a hash bucket. +The product +.Cm max_chain_len*hash_size +is used to determine the threshold over which empty pipes/queues +will be expired even when +.Cm net.inet.ip.dummynet.expire=0 . +.It Em net.inet.ip.dummynet.red_lookup_depth : No 256 +.It Em net.inet.ip.dummynet.red_avg_pkt_size : No 512 +.It Em net.inet.ip.dummynet.red_max_pkt_size : No 1500 +Parameters used in the computations of the drop probability +for the RED algorithm. +.El +.Sh EXAMPLES +The following rules show some of the applications of +for simulations and the like by using +.Em dummynet +rules in +.Xr pf.conf 8 +configuration files. +.Pp +To drop random incoming IPv4 and IPv6 ICMP packets with a probability of 5%, +create a pipe: +.Dl "dnctl pipe 10 config plr 0.05" +.Pp +and add these rules in your pf.conf file: +.Dl "dummynet in inet proto icmp all pipe 10" +.Dl "dummynet in inet6 proto ipv6-icmp all pipe 10" +.Pp +Should we want to simulate a bidirectional link with bandwidth +limitations, the correct way is to create a pipe for each direction: +.Dl "dnctl pipe 1 config bw 14Kbit/s queue 10Kbytes" +.Dl "dnctl pipe 2 config bw 1Kbit/s queue 10Kbytes" +.Pp +and add these rules in your pf.conf file: +.Dl "dummynet in all pipe 1" +.Dl "dummynet out all pipe 2" +.Pp +The above can be very useful, e.g. if you want to see how +your fancy Web page will look for a residential user who +is connected only through a slow link. +You should not use only one pipe for both directions, unless +you want to simulate a half-duplex medium (e.g. AppleTalk, +Ethernet, IRDA). +.Pp +Note that with the above rules the pipes receive traffic for both the +IPv4 and IPv6 protocols. +.Pp +Should we want to verify network performance with the RED queue +management algorithm, create this pipe: +.Dl "dnctl pipe 1 config bw 500Kbit/s queue 100 red 0.002/30/80/0.1" +.Pp +and then add these rules to you pf.conf file: +.Dl "dummynet all pipe 1" +.Pp +Another typical application of the traffic shaper is to +introduce some delay in the communication. +This can significantly affect applications which do a lot of Remote +Procedure Calls, and where the round-trip-time of the +connection often becomes a limiting factor much more than +bandwidth: +.Dl "dnctl pipe 1 config delay 250ms bw 1Mbit/s" +.Dl "dnctl pipe 2 config delay 250ms bw 1Mbit/s" +.Pp +and add these rules in your pf.conf file: +.Dl "dummynet in all pipe 1" +.Dl "dummynet out all pipe 2" +.Pp +Per-flow queueing can be useful for a variety of purposes. +A very simple one is counting traffic: +.Dl "dnctl pipe 1 config mask all" +.Pp +and add these statements in your pf.conf file: +.Dl "dummynet in quick proto tcp all pipe 1" +.Dl "dummynet out quick proto tcp all pipe 1" +.Dl "dummynet in quick proto udp all pipe 1" +.Dl "dummynet out quick proto udp all pipe 1" +.Dl "dummynet in quick all pipe 1" +.Dl "dummynet out quick all pipe 1" +.Pp +The above set of rules will create queues (and collect +statistics) for all traffic. +Because the pipes have no limitations, the only effect is +collecting statistics. +Note that we need six rules, not just the last two one, because +when +.Nm +tries to match IP packets it will not consider ports, so we +would not see connections on separate ports as different +ones. +.Sh SEE ALSO +.Xr cpp 1 , +.Xr dummynet 4 , +.Xr m4 1 , +.Xr ip 4 , +.Xr pfctl 8 , +.Xr pf.conf 5 , +.Xr protocols 5 , +.Xr services 5 , +.Xr sysctl 8 +.Sh AUTHORS +.An Ugen J. S. Antsilevich , +.An Poul-Henning Kamp , +.An Alex Nash , +.An Archie Cobbs , +.An Luigi Rizzo . +.Pp +.An -nosplit +API based upon code written by +.An Daniel Boulet +for BSDI. +.Pp +Work on +.Xr dummynet 4 +traffic shaper supported by Akamba Corp. +.Sh HISTORY +The +.Nm +utility first appeared in +.Fx 2.0 . +.Xr dummynet 4 +was introduced in +.Fx 2.2.8 . +Stateful extensions were introduced in +.Fx 4.0 . diff --git a/network_cmds-543/dnctl/dnctl.c b/network_cmds-543/dnctl/dnctl.c new file mode 100644 index 00000000..119a8396 --- /dev/null +++ b/network_cmds-543/dnctl/dnctl.c @@ -0,0 +1,1315 @@ +/* + * Copyright (c) 2002-2015 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Copyright (c) 2002-2003 Luigi Rizzo + * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp + * Copyright (c) 1994 Ugen J.S.Antsilevich + * + * Idea and grammar partially left from: + * Copyright (c) 1993 Daniel Boulet + * + * Redistribution and use in source forms, with and without modification, + * are permitted provided that this entire comment appears intact. + * + * Redistribution in binary form may occur without any restrictions. + * Obviously, it would be nice if you gave credit where credit is due + * but requiring it would be too onerous. + * + * This software is provided ``AS IS'' without any warranties of any kind. + */ + +/* + * Ripped off ipfw2.c + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * Limit delay to avoid computation overflow + */ +#define MAX_DELAY (INT_MAX / 1000) + + +int +do_quiet, /* Be quiet in add and flush */ +do_pipe, /* this cmd refers to a pipe */ +do_sort, /* field to sort results (0 = no) */ +test_only, /* only check syntax */ +verbose; + +#define IP_MASK_ALL 0xffffffff + +/* + * _s_x is a structure that stores a string <-> token pairs, used in + * various places in the parser. Entries are stored in arrays, + * with an entry with s=NULL as terminator. + * The search routines are match_token() and match_value(). + * Often, an element with x=0 contains an error string. + * + */ +struct _s_x { + char const *s; + int x; +}; + +enum tokens { + TOK_NULL=0, + + TOK_ACCEPT, + TOK_COUNT, + TOK_PIPE, + TOK_QUEUE, + + TOK_PLR, + TOK_NOERROR, + TOK_BUCKETS, + TOK_DSTIP, + TOK_SRCIP, + TOK_DSTPORT, + TOK_SRCPORT, + TOK_ALL, + TOK_MASK, + TOK_BW, + TOK_DELAY, + TOK_RED, + TOK_GRED, + TOK_DROPTAIL, + TOK_PROTO, + TOK_WEIGHT, + + TOK_DSTIP6, + TOK_SRCIP6, +}; + +struct _s_x dummynet_params[] = { + { "plr", TOK_PLR }, + { "noerror", TOK_NOERROR }, + { "buckets", TOK_BUCKETS }, + { "dst-ip", TOK_DSTIP }, + { "src-ip", TOK_SRCIP }, + { "dst-port", TOK_DSTPORT }, + { "src-port", TOK_SRCPORT }, + { "proto", TOK_PROTO }, + { "weight", TOK_WEIGHT }, + { "all", TOK_ALL }, + { "mask", TOK_MASK }, + { "droptail", TOK_DROPTAIL }, + { "red", TOK_RED }, + { "gred", TOK_GRED }, + { "bw", TOK_BW }, + { "bandwidth", TOK_BW }, + { "delay", TOK_DELAY }, + { "pipe", TOK_PIPE }, + { "queue", TOK_QUEUE }, + { "dst-ipv6", TOK_DSTIP6}, + { "dst-ip6", TOK_DSTIP6}, + { "src-ipv6", TOK_SRCIP6}, + { "src-ip6", TOK_SRCIP6}, + { "dummynet-params", TOK_NULL }, + { NULL, 0 } /* terminator */ +}; + +static void show_usage(void); + + +void n2mask(struct in6_addr *, int ); +unsigned long long align_uint64(const uint64_t *); + +/* n2mask sets n bits of the mask */ +void +n2mask(struct in6_addr *mask, int n) +{ + static int minimask[9] = + { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; + u_char *p; + + memset(mask, 0, sizeof(struct in6_addr)); + p = (u_char *) mask; + for (; n > 0; p++, n -= 8) { + if (n >= 8) + *p = 0xff; + else + *p = minimask[n]; + } + return; +} + +/* + * The following is used to generate a printable argument for + * 64-bit numbers, irrespective of platform alignment and bit size. + * Because all the printf in this program use %llu as a format, + * we just return an unsigned long long, which is larger than + * we need in certain cases, but saves the hassle of using + * PRIu64 as a format specifier. + * We don't care about inlining, this is not performance critical code. + */ +unsigned long long +align_uint64(const uint64_t *pll) +{ + uint64_t ret; + + bcopy (pll, &ret, sizeof(ret)); + return ret; +} + +/* + * conditionally runs the command. + */ +static int +do_cmd(int optname, void *optval, socklen_t *optlen) +{ + static int s = -1; /* the socket */ + int i; + + if (test_only) + return 0; + + if (s == -1) + s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + if (s < 0) + err(EX_UNAVAILABLE, "socket"); + + if (optname == IP_DUMMYNET_GET) + i = getsockopt(s, IPPROTO_IP, optname, optval, optlen); + else + i = setsockopt(s, IPPROTO_IP, optname, optval, optlen ? *optlen : 0); + return i; +} + +/** + * match_token takes a table and a string, returns the value associated + * with the string (-1 in case of failure). + */ +static int +match_token(struct _s_x *table, char *string) +{ + struct _s_x *pt; + size_t i = strlen(string); + + for (pt = table ; i && pt->s != NULL ; pt++) + if (strlen(pt->s) == i && !bcmp(string, pt->s, i)) + return pt->x; + return -1; +}; + +static int +sort_q(const void *pa, const void *pb) +{ + int rev = (do_sort < 0); + int field = rev ? -do_sort : do_sort; + long long res = 0; + const struct dn_flow_queue *a = pa; + const struct dn_flow_queue *b = pb; + + switch (field) { + case 1: /* pkts */ + res = a->len - b->len; + break; + case 2: /* bytes */ + res = a->len_bytes - b->len_bytes; + break; + + case 3: /* tot pkts */ + res = a->tot_pkts - b->tot_pkts; + break; + + case 4: /* tot bytes */ + res = a->tot_bytes - b->tot_bytes; + break; + } + if (res < 0) + res = -1; + if (res > 0) + res = 1; + return (int)(rev ? res : -res); +} + +static void +list_queues(struct dn_flow_set *fs, struct dn_flow_queue *q) +{ + int l; + int index_printed = 0, indexes = 0; + char buff[255]; + struct protoent *pe; + + printf(" mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n", + fs->flow_mask.proto, + fs->flow_mask.src_ip, fs->flow_mask.src_port, + fs->flow_mask.dst_ip, fs->flow_mask.dst_port); + if (fs->rq_elements == 0) + return; + + printf("BKT Prot ___Source IP/port____ " + "____Dest. IP/port____ Tot_pkt/bytes Pkt/Byte Drp\n"); + if (do_sort != 0) + heapsort(q, fs->rq_elements, sizeof(struct dn_flow_queue), sort_q); + + /* Print IPv4 flows */ + for (l = 0; l < fs->rq_elements; l++) { + struct in_addr ina; + + /* XXX: Should check for IPv4 flows */ + if (IS_IP6_FLOW_ID(&(q[l].id))) + continue; + + if (!index_printed) { + index_printed = 1; + if (indexes > 0) /* currently a no-op */ + printf("\n"); + indexes++; + printf(" " + "mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n", + fs->flow_mask.proto, + fs->flow_mask.src_ip, fs->flow_mask.src_port, + fs->flow_mask.dst_ip, fs->flow_mask.dst_port); + + printf("BKT Prot ___Source IP/port____ " + "____Dest. IP/port____ " + "Tot_pkt/bytes Pkt/Byte Drp\n"); + } + + printf("%3d ", q[l].hash_slot); + pe = getprotobynumber(q[l].id.proto); + if (pe) + printf("%-4s ", pe->p_name); + else + printf("%4u ", q[l].id.proto); + ina.s_addr = htonl(q[l].id.src_ip); + printf("%15s/%-5d ", + inet_ntoa(ina), q[l].id.src_port); + ina.s_addr = htonl(q[l].id.dst_ip); + printf("%15s/%-5d ", + inet_ntoa(ina), q[l].id.dst_port); + printf("%4llu %8llu %2u %4u %3u\n", + align_uint64(&q[l].tot_pkts), + align_uint64(&q[l].tot_bytes), + q[l].len, q[l].len_bytes, q[l].drops); + if (verbose) + printf(" S %20llu F %20llu\n", + align_uint64(&q[l].S), align_uint64(&q[l].F)); + } + + /* Print IPv6 flows */ + index_printed = 0; + for (l = 0; l < fs->rq_elements; l++) { + if (!IS_IP6_FLOW_ID(&(q[l].id))) + continue; + + if (!index_printed) { + index_printed = 1; + if (indexes > 0) + printf("\n"); + indexes++; + printf("\n mask: proto: 0x%02x, flow_id: 0x%08x, ", + fs->flow_mask.proto, fs->flow_mask.flow_id6); + inet_ntop(AF_INET6, &(fs->flow_mask.src_ip6), + buff, sizeof(buff)); + printf("%s/0x%04x -> ", buff, fs->flow_mask.src_port); + inet_ntop( AF_INET6, &(fs->flow_mask.dst_ip6), + buff, sizeof(buff) ); + printf("%s/0x%04x\n", buff, fs->flow_mask.dst_port); + + printf("BKT ___Prot___ _flow-id_ " + "______________Source IPv6/port_______________ " + "_______________Dest. IPv6/port_______________ " + "Tot_pkt/bytes Pkt/Byte Drp\n"); + } + printf("%3d ", q[l].hash_slot); + pe = getprotobynumber(q[l].id.proto); + if (pe != NULL) + printf("%9s ", pe->p_name); + else + printf("%9u ", q[l].id.proto); + printf("%7d %39s/%-5d ", q[l].id.flow_id6, + inet_ntop(AF_INET6, &(q[l].id.src_ip6), buff, sizeof(buff)), + q[l].id.src_port); + printf(" %39s/%-5d ", + inet_ntop(AF_INET6, &(q[l].id.dst_ip6), buff, sizeof(buff)), + q[l].id.dst_port); + printf(" %4llu %8llu %2u %4u %3u\n", + align_uint64(&q[l].tot_pkts), + align_uint64(&q[l].tot_bytes), + q[l].len, q[l].len_bytes, q[l].drops); + if (verbose) + printf(" S %20llu F %20llu\n", + align_uint64(&q[l].S), + align_uint64(&q[l].F)); + } +} + +static void +print_flowset_parms(struct dn_flow_set *fs, char *prefix) +{ + int l; + char qs[30]; + char plr[30]; + char red[90]; /* Display RED parameters */ + + l = fs->qsize; + if (fs->flags_fs & DN_QSIZE_IS_BYTES) { + if (l >= 8192) + snprintf(qs, sizeof(qs), "%d KB", l / 1024); + else + snprintf(qs, sizeof(qs), "%d B", l); + } else + snprintf(qs, sizeof(qs), "%3d sl.", l); + if (fs->plr) + snprintf(plr, sizeof(plr), "plr %f", 1.0 * fs->plr / (double)(0x7fffffff)); + else + plr[0] = '\0'; + if (fs->flags_fs & DN_IS_RED) /* RED parameters */ + snprintf(red, sizeof(red), + "\n\t %cRED w_q %f min_th %d max_th %d max_p %f", + (fs->flags_fs & DN_IS_GENTLE_RED) ? 'G' : ' ', + 1.0 * fs->w_q / (double)(1 << SCALE_RED), + SCALE_VAL(fs->min_th), + SCALE_VAL(fs->max_th), + 1.0 * fs->max_p / (double)(1 << SCALE_RED)); + else + snprintf(red, sizeof(red), "droptail"); + + printf("%s %s%s %d queues (%d buckets) %s\n", + prefix, qs, plr, fs->rq_elements, fs->rq_size, red); +} + +static void +list_pipes(void *data, size_t nbytes, int ac, char *av[]) +{ + unsigned int rulenum; + void *next = data; + struct dn_pipe *p = (struct dn_pipe *) data; + struct dn_flow_set *fs; + struct dn_flow_queue *q; + size_t l; + + if (ac > 0) + rulenum = (unsigned int)strtoul(*av++, NULL, 10); + else + rulenum = 0; + for (; nbytes >= sizeof(struct dn_pipe); p = (struct dn_pipe *)next) { + double b = p->bandwidth; + char buf[30]; + char prefix[80]; + + if (p->next.sle_next != (struct dn_pipe *)DN_IS_PIPE) + break; /* done with pipes, now queues */ + + /* + * compute length, as pipe have variable size + */ + l = sizeof(struct dn_pipe) + p->fs.rq_elements * sizeof(struct dn_flow_queue); + next = (char *)p + l; + nbytes -= l; + + if (rulenum != 0 && rulenum != p->pipe_nr) + continue; + + /* + * Print rate (or clocking interface) + */ + if (p->if_name[0] != '\0') + snprintf(buf, sizeof(buf), "%s", p->if_name); + else if (b == 0) + snprintf(buf, sizeof(buf), "unlimited"); + else if (b >= 1000000) + snprintf(buf, sizeof(buf), "%7.3f Mbit/s", b/1000000); + else if (b >= 1000) + snprintf(buf, sizeof(buf), "%7.3f Kbit/s", b/1000); + else + snprintf(buf, sizeof(buf), "%7.3f bit/s ", b); + + snprintf(prefix, sizeof(prefix), "%05d: %s %4d ms ", + p->pipe_nr, buf, p->delay); + print_flowset_parms(&(p->fs), prefix); + if (verbose) + printf(" V %20qd\n", p->V >> MY_M); + + q = (struct dn_flow_queue *)(p+1); + list_queues(&(p->fs), q); + } + for (fs = next; nbytes >= sizeof *fs; fs = next) { + char prefix[80]; + + if (fs->next.sle_next != (struct dn_flow_set *)DN_IS_QUEUE) + break; + l = sizeof(struct dn_flow_set) + fs->rq_elements * sizeof(struct dn_flow_queue); + next = (char *)fs + l; + nbytes -= l; + q = (struct dn_flow_queue *)(fs+1); + snprintf(prefix, sizeof(prefix), "q%05d: weight %d pipe %d ", + fs->fs_nr, fs->weight, fs->parent_nr); + print_flowset_parms(fs, prefix); + list_queues(fs, q); + } +} + +static void +list(int ac, char *av[], int show_counters) +{ + void *data = NULL; + socklen_t nbytes; + int exitval = EX_OK; + + int nalloc = 1024; /* start somewhere... */ + + if (test_only) { + fprintf(stderr, "Testing only, list disabled\n"); + return; + } + + ac--; + av++; + + /* get rules or pipes from kernel, resizing array as necessary */ + nbytes = nalloc; + + while (nbytes >= nalloc) { + nalloc = nalloc * 2 + 200; + nbytes = nalloc; + if ((data = realloc(data, nbytes)) == NULL) + err(EX_OSERR, "realloc"); + + if (do_cmd(IP_DUMMYNET_GET, data, &nbytes) < 0) { + if (errno == ENOBUFS) { + nbytes = 0; + break; + } + err(EX_OSERR, "getsockopt(IP_DUMMYNET_GET)"); + + } + } + + list_pipes(data, nbytes, ac, av); + + free(data); + + if (exitval != EX_OK) + exit(exitval); +} + +static void +show_usage(void) +{ + fprintf(stderr, "usage: dnctl [options]\n" + "do \"dnctl -h\" or see dnctl manpage for details\n" + ); + exit(EX_USAGE); +} + +static void +help(void) +{ + fprintf(stderr, + "dnclt [-acdeftTnNpqS] where is one of:\n" + "{pipe|queue} N config PIPE-BODY\n" + "[pipe|queue] {zero|delete|show} [N{,N}]\n" + ); + exit(0); +} + +static void +delete(int ac, char *av[]) +{ + struct dn_pipe p; + int i; + int exitval = EX_OK; + socklen_t len; + + memset(&p, 0, sizeof(struct dn_pipe)); + + av++; ac--; + + while (ac && isdigit(**av)) { + i = atoi(*av); av++; ac--; + + if (do_pipe == 1) + p.pipe_nr = i; + else + p.fs.fs_nr = i; + len = sizeof(struct dn_pipe); + i = do_cmd(IP_DUMMYNET_DEL, &p, &len); + if (i) { + exitval = 1; + warn("rule %u: setsockopt(IP_DUMMYNET_DEL)", + do_pipe == 1 ? p.pipe_nr : p.fs.fs_nr); + } + } + if (exitval != EX_OK) + exit(exitval); +} + +/* + * the following macro returns an error message if we run out of + * arguments. + */ +#define NEED1(msg) {if (!ac) errx(EX_USAGE, msg);} +#define NEED2(msg, arg) {if (!ac) errx(EX_USAGE, msg, arg);} + +static void +config_pipe(int ac, char **av) +{ + struct dn_pipe p; + int i; + char *end; + void *par = NULL; + socklen_t len; + + memset(&p, 0, sizeof(struct dn_pipe)); + + av++; ac--; + /* Pipe number */ + if (ac && isdigit(**av)) { + i = atoi(*av); av++; ac--; + if (do_pipe == 1) + p.pipe_nr = i; + else + p.fs.fs_nr = i; + } + while (ac > 0) { + double d; + int tok = match_token(dummynet_params, *av); + ac--; av++; + + switch(tok) { + case TOK_NOERROR: + p.fs.flags_fs |= DN_NOERROR; + break; + + case TOK_PLR: + NEED1("plr needs argument 0..1\n"); + d = strtod(av[0], NULL); + if (d > 1) + d = 1; + else if (d < 0) + d = 0; + p.fs.plr = (int)(d*0x7fffffff); + ac--; av++; + break; + + case TOK_QUEUE: + NEED1("queue needs queue size\n"); + end = NULL; + p.fs.qsize = (int)strtoul(av[0], &end, 0); + if (*end == 'K' || *end == 'k') { + p.fs.flags_fs |= DN_QSIZE_IS_BYTES; + p.fs.qsize *= 1024; + } else if (*end == 'B' || !strncmp(end, "by", 2)) { + p.fs.flags_fs |= DN_QSIZE_IS_BYTES; + } + ac--; av++; + break; + + case TOK_BUCKETS: + NEED1("buckets needs argument\n"); + p.fs.rq_size = (int)strtoul(av[0], NULL, 0); + ac--; av++; + break; + + case TOK_MASK: + NEED1("mask needs mask specifier\n"); + /* + * per-flow queue, mask is dst_ip, dst_port, + * src_ip, src_port, proto measured in bits + */ + par = NULL; + + p.fs.flow_mask.dst_ip = 0; + p.fs.flow_mask.src_ip = 0; + p.fs.flow_mask.dst_port = 0; + p.fs.flow_mask.src_port = 0; + p.fs.flow_mask.proto = 0; + end = NULL; + + while (ac >= 1) { + uint32_t *p32 = NULL; + uint16_t *p16 = NULL; + struct in6_addr *pa6 = NULL; + uint32_t a; + + tok = match_token(dummynet_params, *av); + ac--; av++; + switch(tok) { + case TOK_ALL: + /* + * special case, all bits significant + */ + p.fs.flow_mask.dst_ip = ~0; + p.fs.flow_mask.src_ip = ~0; + p.fs.flow_mask.dst_port = ~0; + p.fs.flow_mask.src_port = ~0; + p.fs.flow_mask.proto = ~0; + n2mask(&(p.fs.flow_mask.dst_ip6), 128); + n2mask(&(p.fs.flow_mask.src_ip6), 128); + p.fs.flags_fs |= DN_HAVE_FLOW_MASK; + goto end_mask; + + case TOK_DSTIP: + p32 = &p.fs.flow_mask.dst_ip; + break; + + case TOK_SRCIP: + p32 = &p.fs.flow_mask.src_ip; + break; + + case TOK_DSTIP6: + pa6 = &(p.fs.flow_mask.dst_ip6); + break; + + case TOK_SRCIP6: + pa6 = &(p.fs.flow_mask.src_ip6); + break; + + case TOK_DSTPORT: + p16 = &p.fs.flow_mask.dst_port; + break; + + case TOK_SRCPORT: + p16 = &p.fs.flow_mask.src_port; + break; + + case TOK_PROTO: + break; + + default: + ac++; av--; /* backtrack */ + goto end_mask; + } + if (ac < 1) + errx(EX_USAGE, "mask: value missing"); + if (*av[0] == '/') { + a = (int)strtoul(av[0]+1, &end, 0); + if (pa6 == NULL) + a = (a == 32) ? ~0 : (1 << a) - 1; + } else + a = (int)strtoul(av[0], &end, 0); + if (p32 != NULL) + *p32 = a; + else if (p16 != NULL) { + if (a > 65535) + errx(EX_DATAERR, + "mask: must be 16 bit"); + *p16 = (uint16_t)a; + } else if (pa6 != NULL) { + if (a > 128) + errx(EX_DATAERR, + "in6addr invalid mask len"); + else + n2mask(pa6, a); + } else { + if (a > 255) + errx(EX_DATAERR, + "mask: must be 8 bit"); + p.fs.flow_mask.proto = (uint8_t)a; + } + if (a != 0) + p.fs.flags_fs |= DN_HAVE_FLOW_MASK; + ac--; av++; + } /* end while, config masks */ +end_mask: + break; + + case TOK_RED: + case TOK_GRED: + NEED1("red/gred needs w_q/min_th/max_th/max_p\n"); + p.fs.flags_fs |= DN_IS_RED; + if (tok == TOK_GRED) + p.fs.flags_fs |= DN_IS_GENTLE_RED; + /* + * the format for parameters is w_q/min_th/max_th/max_p + */ + if ((end = strsep(&av[0], "/"))) { + double w_q = strtod(end, NULL); + if (w_q > 1 || w_q <= 0) + errx(EX_DATAERR, "0 < w_q <= 1"); + p.fs.w_q = (int) (w_q * (1 << SCALE_RED)); + } + if ((end = strsep(&av[0], "/"))) { + p.fs.min_th = (int)strtoul(end, &end, 0); + if (*end == 'K' || *end == 'k') + p.fs.min_th *= 1024; + } + if ((end = strsep(&av[0], "/"))) { + p.fs.max_th = (int)strtoul(end, &end, 0); + if (*end == 'K' || *end == 'k') + p.fs.max_th *= 1024; + } + if ((end = strsep(&av[0], "/"))) { + double max_p = strtod(end, NULL); + if (max_p > 1 || max_p <= 0) + errx(EX_DATAERR, "0 < max_p <= 1"); + p.fs.max_p = (int)(max_p * (1 << SCALE_RED)); + } + ac--; av++; + break; + + case TOK_DROPTAIL: + p.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED); + break; + + case TOK_BW: + NEED1("bw needs bandwidth or interface\n"); + if (do_pipe != 1) + errx(EX_DATAERR, "bandwidth only valid for pipes"); + /* + * set clocking interface or bandwidth value + */ + if (av[0][0] >= 'a' && av[0][0] <= 'z') { + int l = sizeof(p.if_name)-1; + /* interface name */ + strncpy(p.if_name, av[0], l); + p.if_name[l] = '\0'; + p.bandwidth = 0; + } else { + p.if_name[0] = '\0'; + p.bandwidth = (int)strtoul(av[0], &end, 0); + if (*end == 'K' || *end == 'k') { + end++; + p.bandwidth *= 1000; + } else if (*end == 'M') { + end++; + p.bandwidth *= 1000000; + } + if (*end == 'B' || !strncmp(end, "by", 2)) + p.bandwidth *= 8; + if (p.bandwidth < 0) + errx(EX_DATAERR, "bandwidth too large"); + } + ac--; av++; + break; + + case TOK_DELAY: + if (do_pipe != 1) + errx(EX_DATAERR, "delay only valid for pipes"); + NEED2("delay needs argument 0..%d\n", MAX_DELAY); + p.delay = (int)strtoul(av[0], NULL, 0); + ac--; av++; + break; + + case TOK_WEIGHT: + if (do_pipe == 1) + errx(EX_DATAERR,"weight only valid for queues"); + NEED1("weight needs argument 0..100\n"); + p.fs.weight = (int)strtoul(av[0], &end, 0); + ac--; av++; + break; + + case TOK_PIPE: + if (do_pipe == 1) + errx(EX_DATAERR,"pipe only valid for queues"); + NEED1("pipe needs pipe_number\n"); + p.fs.parent_nr = strtoul(av[0], &end, 0); + ac--; av++; + break; + + default: + errx(EX_DATAERR, "unrecognised option ``%s''", *(--av)); + } + } + if (do_pipe == 1) { + if (p.pipe_nr == 0) + errx(EX_DATAERR, "pipe_nr must be > 0"); + if (p.delay > MAX_DELAY) + errx(EX_DATAERR, "delay must be < %d ms", MAX_DELAY); + } else { /* do_pipe == 2, queue */ + if (p.fs.parent_nr == 0) + errx(EX_DATAERR, "pipe must be > 0"); + if (p.fs.weight >100) + errx(EX_DATAERR, "weight must be <= 100"); + } + if (p.fs.flags_fs & DN_QSIZE_IS_BYTES) { + if (p.fs.qsize > 1024*1024) + errx(EX_DATAERR, "queue size must be < 1MB"); + } else { + if (p.fs.qsize > 100) + errx(EX_DATAERR, "2 <= queue size <= 100"); + } + if (p.fs.flags_fs & DN_IS_RED) { + size_t len; + int lookup_depth, avg_pkt_size; + double s, idle, weight, w_q; + struct clockinfo ck; + int t; + + if (p.fs.min_th >= p.fs.max_th) + errx(EX_DATAERR, "min_th %d must be < than max_th %d", + p.fs.min_th, p.fs.max_th); + if (p.fs.max_th == 0) + errx(EX_DATAERR, "max_th must be > 0"); + + len = sizeof(int); + if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth", + &lookup_depth, &len, NULL, 0) == -1) + + errx(1, "sysctlbyname(\"%s\")", + "net.inet.ip.dummynet.red_lookup_depth"); + if (lookup_depth == 0) + errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth" + " must be greater than zero"); + + len = sizeof(int); + if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size", + &avg_pkt_size, &len, NULL, 0) == -1) + + errx(1, "sysctlbyname(\"%s\")", + "net.inet.ip.dummynet.red_avg_pkt_size"); + if (avg_pkt_size == 0) + errx(EX_DATAERR, + "net.inet.ip.dummynet.red_avg_pkt_size must" + " be greater than zero"); + + len = sizeof(struct clockinfo); + if (sysctlbyname("kern.clockrate", &ck, &len, NULL, 0) == -1) + errx(1, "sysctlbyname(\"%s\")", "kern.clockrate"); + + /* + * Ticks needed for sending a medium-sized packet. + * Unfortunately, when we are configuring a WF2Q+ queue, we + * do not have bandwidth information, because that is stored + * in the parent pipe, and also we have multiple queues + * competing for it. So we set s=0, which is not very + * correct. But on the other hand, why do we want RED with + * WF2Q+ ? + */ + if (p.bandwidth==0) /* this is a WF2Q+ queue */ + s = 0; + else + s = ck.hz * avg_pkt_size * 8 / p.bandwidth; + + /* + * max idle time (in ticks) before avg queue size becomes 0. + * NOTA: (3/w_q) is approx the value x so that + * (1-w_q)^x < 10^-3. + */ + w_q = ((double)p.fs.w_q) / (1 << SCALE_RED); + idle = s * 3. / w_q; + p.fs.lookup_step = (int)idle / lookup_depth; + if (!p.fs.lookup_step) + p.fs.lookup_step = 1; + weight = 1 - w_q; + for (t = p.fs.lookup_step; t > 0; --t) + weight *= weight; + p.fs.lookup_weight = (int)(weight * (1 << SCALE_RED)); + } + len = sizeof(struct dn_pipe); + i = do_cmd(IP_DUMMYNET_CONFIGURE, &p, &len); + if (i) + err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE"); +} + +static void +flush(int force) +{ + if (!force && !do_quiet) { /* need to ask user */ + int c; + + printf("Are you sure? [yn] "); + fflush(stdout); + do { + c = toupper(getc(stdin)); + while (c != '\n' && getc(stdin) != '\n') + if (feof(stdin)) + return; /* and do not flush */ + } while (c != 'Y' && c != 'N'); + printf("\n"); + if (c == 'N') /* user said no */ + return; + } + + if (do_cmd(IP_DUMMYNET_FLUSH, NULL, 0) < 0) + err(EX_UNAVAILABLE, "setsockopt(IP_DUMMYNET_FLUSH)"); + + if (!do_quiet) + printf("Flushed all pipes.\n"); +} + +/* + * Free a the (locally allocated) copy of command line arguments. + */ +static void +free_args(int ac, char **av) +{ + int i; + + for (i=0; i < ac; i++) + free(av[i]); + free(av); +} + +/* + * Called with the arguments (excluding program name). + * Returns 0 if successful, 1 if empty command, errx() in case of errors. + */ +static int +parse_args(int oldac, char **oldav) +{ + int ch, ac, save_ac; + char **av, **save_av; + int do_acct = 0; /* Show packet/byte count */ + int do_force = 0; /* Don't ask for confirmation */ + +#define WHITESP " \t\f\v\n\r" + if (oldac == 0) + return 1; + else if (oldac == 1) { + /* + * If we are called with a single string, try to split it into + * arguments for subsequent parsing. + * But first, remove spaces after a ',', by copying the string + * in-place. + */ + char *arg = oldav[0]; /* The string... */ + size_t l = strlen(arg); + int copy = 0; /* 1 if we need to copy, 0 otherwise */ + int i, j; + for (i = j = 0; i < l; i++) { + if (arg[i] == '#') /* comment marker */ + break; + if (copy) { + arg[j++] = arg[i]; + copy = !index("," WHITESP, arg[i]); + } else { + copy = !index(WHITESP, arg[i]); + if (copy) + arg[j++] = arg[i]; + } + } + if (!copy && j > 0) /* last char was a 'blank', remove it */ + j--; + l = j; /* the new argument length */ + arg[j++] = '\0'; + if (l == 0) /* empty string! */ + return 1; + + /* + * First, count number of arguments. Because of the previous + * processing, this is just the number of blanks plus 1. + */ + for (i = 0, ac = 1; i < l; i++) + if (index(WHITESP, arg[i]) != NULL) + ac++; + + av = calloc(ac, sizeof(char *)); + + /* + * Second, copy arguments from cmd[] to av[]. For each one, + * j is the initial character, i is the one past the end. + */ + for (ac = 0, i = j = 0; i < l; i++) + if (index(WHITESP, arg[i]) != NULL || i == l-1) { + if (i == l-1) + i++; + av[ac] = calloc(i-j+1, 1); + bcopy(arg+j, av[ac], i-j); + ac++; + j = i + 1; + } + } else { + /* + * If an argument ends with ',' join with the next one. + * Just add its length to 'l' and continue. When we have a string + * without a ',' ending, we'll have the combined length in 'l' + */ + int first, i; + size_t l; + + av = calloc(oldac, sizeof(char *)); + for (first = i = ac = 0, l = 0; i < oldac; i++) { + char *arg = oldav[i]; + size_t k = strlen(arg); + + l += k; + if (arg[k-1] != ',' || i == oldac-1) { + size_t buflen = l+1; + /* Time to copy. */ + av[ac] = calloc(l+1, 1); + for (l=0; first <= i; first++) { + strlcat(av[ac]+l, oldav[first], buflen-l); + l += strlen(oldav[first]); + } + ac++; + l = 0; + first = i+1; + } + } + } + + /* Set the force flag for non-interactive processes */ + do_force = !isatty(STDIN_FILENO); + + /* Save arguments for final freeing of memory. */ + save_ac = ac; + save_av = av; + + optind = optreset = 0; + while ((ch = getopt(ac, av, "afhnqsv")) != -1) + switch (ch) { + case 'a': + do_acct = 1; + break; + + case 'f': + do_force = 1; + break; + + case 'h': /* help */ + free_args(save_ac, save_av); + help(); + break; /* NOTREACHED */ + + case 'n': + test_only = 1; + break; + + case 'q': + do_quiet = 1; + break; + + case 's': /* sort */ + do_sort = atoi(optarg); + break; + + case 'v': /* verbose */ + verbose = 1; + break; + + default: + free_args(save_ac, save_av); + return 1; + } + + ac -= optind; + av += optind; + NEED1("bad arguments, for usage summary ``dnctl''"); + + /* + * An undocumented behaviour of dnctl1 was to allow rule numbers first, + * e.g. "100 add allow ..." instead of "add 100 allow ...". + * In case, swap first and second argument to get the normal form. + */ + if (ac > 1 && isdigit(*av[0])) { + char *p = av[0]; + + av[0] = av[1]; + av[1] = p; + } + + /* + * optional: pipe or queue + */ + do_pipe = 0; + if (!strncmp(*av, "pipe", strlen(*av))) + do_pipe = 1; + else if (!strncmp(*av, "queue", strlen(*av))) + do_pipe = 2; + if (do_pipe) { + ac--; + av++; + } + NEED1("missing command"); + + /* + * For pipes and queues we normally say 'pipe NN config' + * but the code is easier to parse as 'pipe config NN' + * so we swap the two arguments. + */ + if (do_pipe > 0 && ac > 1 && isdigit(*av[0])) { + char *p = av[0]; + + av[0] = av[1]; + av[1] = p; + } + + if (do_pipe && !strncmp(*av, "config", strlen(*av))) + config_pipe(ac, av); + else if (!strncmp(*av, "delete", strlen(*av))) + delete(ac, av); + else if (!strncmp(*av, "flush", strlen(*av))) + flush(do_force); + else if (!strncmp(*av, "print", strlen(*av)) || + !strncmp(*av, "list", strlen(*av))) + list(ac, av, do_acct); + else if (!strncmp(*av, "show", strlen(*av))) + list(ac, av, 1 /* show counters */); + else + errx(EX_USAGE, "bad command `%s'", *av); + + /* Free memory allocated in the argument parsing. */ + free_args(save_ac, save_av); + return 0; +} + +static void +dnctl_readfile(int ac, char *av[]) +{ +#define MAX_ARGS 32 + char buf[BUFSIZ]; + char *cmd = NULL, *filename = av[ac-1]; + int c, lineno=0; + FILE *f = NULL; + pid_t preproc = 0; + + while ((c = getopt(ac, av, "np:q")) != -1) { + switch(c) { + case 'n': + test_only = 1; + break; + + case 'p': + cmd = optarg; + /* + * Skip previous args and delete last one, so we + * pass all but the last argument to the preprocessor + * via av[optind-1] + */ + av += optind - 1; + ac -= optind - 1; + av[ac-1] = NULL; + fprintf(stderr, "command is %s\n", av[0]); + break; + + case 'q': + do_quiet = 1; + break; + + default: + errx(EX_USAGE, "bad arguments, for usage" + " summary ``dnctl''"); + } + + if (cmd != NULL) + break; + } + + if (cmd == NULL && ac != optind + 1) { + fprintf(stderr, "ac %d, optind %d\n", ac, optind); + errx(EX_USAGE, "extraneous filename arguments"); + } + + if ((f = fopen(filename, "r")) == NULL) + err(EX_UNAVAILABLE, "fopen: %s", filename); + + if (cmd != NULL) { /* pipe through preprocessor */ + int pipedes[2]; + + if (pipe(pipedes) == -1) + err(EX_OSERR, "cannot create pipe"); + + preproc = fork(); + if (preproc == -1) + err(EX_OSERR, "cannot fork"); + + if (preproc == 0) { + /* + * Child, will run the preprocessor with the + * file on stdin and the pipe on stdout. + */ + if (dup2(fileno(f), 0) == -1 + || dup2(pipedes[1], 1) == -1) + err(EX_OSERR, "dup2()"); + fclose(f); + close(pipedes[1]); + close(pipedes[0]); + execvp(cmd, av); + err(EX_OSERR, "execvp(%s) failed", cmd); + } else { /* parent, will reopen f as the pipe */ + fclose(f); + close(pipedes[1]); + if ((f = fdopen(pipedes[0], "r")) == NULL) { + int savederrno = errno; + + (void)kill(preproc, SIGTERM); + errno = savederrno; + err(EX_OSERR, "fdopen()"); + } + } + } + + while (fgets(buf, BUFSIZ, f)) { /* read commands */ + char linename[16]; + char *args[1]; + + lineno++; + snprintf(linename, sizeof(linename), "Line %d", lineno); + setprogname(linename); /* XXX */ + args[0] = buf; + parse_args(1, args); + } + fclose(f); + if (cmd != NULL) { + int status; + + if (waitpid(preproc, &status, 0) == -1) + errx(EX_OSERR, "waitpid()"); + if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK) + errx(EX_UNAVAILABLE, + "preprocessor exited with status %d", + WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + errx(EX_UNAVAILABLE, + "preprocessor exited with signal %d", + WTERMSIG(status)); + } +} + +int +main(int ac, char *av[]) +{ + /* + * If the last argument is an absolute pathname, interpret it + * as a file to be preprocessed. + */ + + if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0) + dnctl_readfile(ac, av); + else { + if (parse_args(ac-1, av+1)) + show_usage(); + } + return EX_OK; +} diff --git a/network_cmds-543/ecnprobe/base.h b/network_cmds-543/ecnprobe/base.h new file mode 100644 index 00000000..562b4ef5 --- /dev/null +++ b/network_cmds-543/ecnprobe/base.h @@ -0,0 +1,57 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI 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 ICSI 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include diff --git a/network_cmds-543/ecnprobe/capture.c b/network_cmds-543/ecnprobe/capture.c new file mode 100644 index 00000000..ff6b6d58 --- /dev/null +++ b/network_cmds-543/ecnprobe/capture.c @@ -0,0 +1,194 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI 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 ICSI 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 +#include +#include +#include +#include +#include +#include "gmt2local.h" +#include "pcap.h" +#include "inet.h" +#include "capture.h" + +/* set snaplen to max etherenet packet size */ +#define DEFAULT_SNAPLEN 1500 + +pcap_t *pc; /* pcap device */ +int datalinkOffset; /* offset of ip packet from datalink packet */ +int captureDebug = 1; +unsigned int thisTimeZone; + +void CaptureInit(u_int32_t sourceIP, u_int16_t sourcePort, + u_int32_t targetIP, u_int16_t targetPort, char *dev) +{ + + char *device = NULL; + char errbuf[PCAP_ERRBUF_SIZE]; + int snaplen = DEFAULT_SNAPLEN; + int promisc = 1; + int timeout = 10; /* timeout in 1 second (10 ms) */ + char filtercmds[255]; + bpf_u_int32 netmask = 0; + struct bpf_program filter; + char source[18]; + char target[18]; + int i; + + /* Get local time zone for interpreting timestamps */ + /* XXX - does this belong here? */ + thisTimeZone = gmt2local(0); + + if (dev != NULL) { + device = dev; + } else { + device = pcap_lookupdev(errbuf); + if (device == NULL) { + fprintf(stderr, "Can't find capture device: %s\n", errbuf); + exit(-1); + } + } + + if (captureDebug) { + printf("Device name is %s\n", device); + } + pc = pcap_open_live(device, snaplen, promisc, timeout, errbuf); + if (pc == NULL) { + fprintf(stderr,"Can't open capture device %s: %s\n",device, errbuf); + exit(-1); + } + + /* XXX why do we need to do this? */ + i = pcap_snapshot(pc); + if (snaplen < i) { + fprintf(stderr, "Warning: snaplen raised to %d from %d", + snaplen, i); + } + + if ((i = pcap_datalink(pc)) < 0) { + fprintf(stderr,"Unable to determine datalink type for %s: %s\n", + device, errbuf); + exit(-1); + } + + switch(i) { + + case DLT_EN10MB: datalinkOffset = 14; break; + case DLT_IEEE802: datalinkOffset = 22; break; + case DLT_NULL: datalinkOffset = 4; break; + case DLT_SLIP: + case DLT_PPP: datalinkOffset = 24; break; + case DLT_RAW: datalinkOffset = 0; break; + default: + fprintf(stderr,"Unknown datalink type %d\n",i); + exit(-1); + break; + + } + + if (InetAddress(sourceIP) < 0) { + fprintf(stderr, "Invalid source IP address (%d)\n", sourceIP); + exit(-1); + } + + strncpy(source, InetAddress(sourceIP), sizeof(source) - 1); + strncpy(target, InetAddress(targetIP), sizeof(target) - 1); + + /* Setup initial filter */ + sprintf(filtercmds, + "(host %s && host %s && port %d) || icmp\n", + source, target, targetPort); + + if (captureDebug) { + printf("datalinkOffset = %d\n", datalinkOffset); + printf("filter = %s\n", filtercmds); + } + if (pcap_compile(pc, &filter, filtercmds, 1, netmask) < 0) { + printf("Error: %s", pcap_geterr(pc)); + exit(-1); + } + + if (pcap_setfilter(pc, &filter) < 0) { + fprintf(stderr, "Can't set filter: %s",pcap_geterr(pc)); + exit(-1); + } + + if (captureDebug) { + printf("Listening on %s...\n", device); + } + +} + +char *CaptureGetPacket(struct pcap_pkthdr *pi) +{ + + const u_char *p; + + p = pcap_next(pc, (struct pcap_pkthdr *)pi); + + if (p != NULL) { + p += datalinkOffset; + } + + pi->ts.tv_sec = (pi->ts.tv_sec + thisTimeZone) % 86400; + + return (char *)p; + +} + + +void CaptureEnd() +{ + struct pcap_stat stat; + + if (pcap_stats(pc, &stat) < 0) { + (void)fprintf(stderr, "pcap_stats: %s\n", pcap_geterr(pc)); + } + else { + (void)fprintf(stderr, "%d packets received by filter\n", stat.ps_recv); + (void)fprintf(stderr, "%d packets dropped by kernel\n", stat.ps_drop); + } + + pcap_close(pc); +} + diff --git a/network_cmds-543/ecnprobe/capture.h b/network_cmds-543/ecnprobe/capture.h new file mode 100644 index 00000000..9bb83beb --- /dev/null +++ b/network_cmds-543/ecnprobe/capture.h @@ -0,0 +1,58 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI 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 ICSI 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 _CAPTURE_H_ +#define _CAPTURE_H_ +#include + +struct PacketInfo { + struct timeval ts; + unsigned int caplen; + unsigned int len; +}; + +void CaptureInit(u_int32_t sourceIP, u_int16_t sourcePort, + u_int32_t targetIP, u_int16_t targetPort, char *dev); +char *CaptureGetPacket(struct pcap_pkthdr *); +void CaptureEnd(); + +#endif /* _CAPTURE_H_ */ diff --git a/network_cmds-543/ecnprobe/ecn.c b/network_cmds-543/ecnprobe/ecn.c new file mode 100644 index 00000000..b7dabdf1 --- /dev/null +++ b/network_cmds-543/ecnprobe/ecn.c @@ -0,0 +1,1119 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI 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 ICSI 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 +#include +#include +#include +#include +#include +#include +#include "base.h" +#include "inet.h" +#include "session.h" +#include "capture.h" +#include "support.h" +#include "history.h" +#include "ecn.h" + +extern struct TcpSession session; +extern struct History history[]; + +#define ESTABLISH_SUCCESS 0 +#define ESTABLISH_FAILURE_EARLY_RST 1 +#define ESTABLISH_FAILURE_NO_REPLY 2 +int EstablishTcpConnection(u_int8_t syn_flags, u_int8_t ip_tos) +{ + struct IPPacket *synPacket = NULL, *ackPacket = NULL; + char *read_packet; + struct pcap_pkthdr pi; + int synAckReceived = 0; + int numRetransmits = 0; + double timeoutTime; + int tcpoptlen = 4; /* For negotiating MSS */ + u_int8_t *opt = NULL; + struct IPPacket *p = NULL; + + /* allocate the syn packet -- Changed for new IPPacket structure */ + synPacket = AllocateIPPacket(0, tcpoptlen, 0, "ECN (SYN)"); + opt = (((u_int8_t *)synPacket->tcp) + sizeof(struct TcpHeader)); + opt[0] = (u_int8_t)TCPOPT_MAXSEG; + opt[1] = (u_int8_t)TCPOLEN_MAXSEG; + *((u_int16_t *)((u_int8_t *)opt + 2)) = htons(session.mss); + + SendSessionPacket(synPacket, + sizeof(struct IpHeader) + sizeof(struct TcpHeader) + tcpoptlen, + TCPFLAGS_SYN | syn_flags, 0, tcpoptlen, ip_tos); + timeoutTime = GetTime() + 1; + + /* + * Wait for SYN/ACK and retransmit SYN if appropriate + * not great, but it gets the job done + */ + + while(!synAckReceived && numRetransmits < 3) { + while(GetTime() < timeoutTime) { + /* Have we captured any packets? */ + if ((read_packet = (char *)CaptureGetPacket(&pi)) != NULL) { + p = (struct IPPacket *)FindHeaderBoundaries(read_packet); + /* Received a packet from us to them */ + if (INSESSION(p, session.src, session.sport, + session.dst, session.dport)) { + /* Is it a SYN/ACK? */ + if (p->tcp->tcp_flags & TCPFLAGS_SYN) { + if (session.debug >= SESSION_DEBUG_LOW) { + PrintTcpPacket(p); + } + StorePacket(p); + session.totSeenSent++ ; + } else { + processBadPacket(p); + } + continue; + } + + /* Received a packet from them to us */ + if (INSESSION(p, session.dst, session.dport, session.src, + session.sport)) { + /* Is it a SYN/ACK? */ + if ((p->tcp->tcp_flags & TCPFLAGS_SYN) && + (p->tcp->tcp_flags & TCPFLAGS_ACK)) { + timeoutTime = GetTime(); /* force exit */ + synAckReceived++; + if (session.debug >= SESSION_DEBUG_LOW) { + PrintTcpPacket(p); + } + StorePacket(p); + + /* + * Save ttl for,admittedly poor,indications of reverse + * route change + */ + session.ttl = p->ip->ip_ttl; + session.snd_wnd = ntohl(p->tcp->tcp_win); + session.totRcvd ++; + break; + } else { + if ((p->tcp->tcp_flags)& (TCPFLAGS_RST)) { + printf ("ERROR: EARLY_RST\n"); + return(ESTABLISH_FAILURE_EARLY_RST); + } + } + } + } + } + + if (!synAckReceived) { + if (session.debug >= SESSION_DEBUG_LOW) { + printf("SYN timeout. Retransmitting\n"); + } + SendSessionPacket(synPacket, + sizeof(struct IpHeader) + sizeof(struct TcpHeader) + tcpoptlen, + TCPFLAGS_SYN | syn_flags, 0, tcpoptlen, ip_tos); + timeoutTime = GetTime() + 1; + numRetransmits++; + } + } + + if (numRetransmits >= 3) { + printf("ERROR: No connection after 3 retries...\nRETURN CODE: %d\n", + NO_CONNECTION); + return(ESTABLISH_FAILURE_NO_REPLY); + } + if (session.debug >= SESSION_DEBUG_LOW) + printf("Received SYN-ACK, try to send the third Ack\n"); + /* Update session variables */ + session.irs = ntohl(p->tcp->tcp_seq); + session.dataRcvd[0] = 1 ; + session.rcv_nxt = session.irs + 1; /* SYN/ACK takes up a byte of seq space */ + session.snd_nxt = session.iss + 1; /* SYN takes up a byte of seq space */ + session.snd_una = session.iss + 1; + session.maxseqseen = ntohl(p->tcp->tcp_seq); + session.initSession = 1; + if (session.debug >= SESSION_DEBUG_LOW) { + printf("src = %s:%d (%u)\n", InetAddress(session.src), + session.sport, session.iss); + printf("dst = %s:%d (%u)\n",InetAddress(session.dst), + session.dport, session.irs); + } + + /* allocate the syn packet -- Changed for new IPPacket structure */ + ackPacket = AllocateIPPacket(0, 0, 0, "Third ACK"); + /* send an ACK */ + SendSessionPacket(ackPacket, + sizeof(struct IpHeader) + sizeof(struct TcpHeader), + TCPFLAGS_ACK, 0, 0, 0); + FreeIPPacket(&synPacket); + FreeIPPacket(&ackPacket); + return(ESTABLISH_SUCCESS); +} + +void ECNTest(u_int32_t sourceAddress, u_int16_t sourcePort, + u_int32_t targetAddress, u_int16_t targetPort, int mss) +{ + int rawSocket, rc, flag = 1; + + arc4random_stir(); + + session.src = sourceAddress; + session.sport = sourcePort; + session.dst = targetAddress; + session.dport = targetPort; + session.rcv_wnd = 5*mss; + /* random initial sequence number */ + session.snd_nxt = arc4random(); + session.iss = session.snd_nxt; + session.rcv_nxt = 0; + session.irs = 0; + session.mss = mss; + session.maxseqseen = 0; + session.epochTime = GetTime (); + session.maxpkts = 1000; +/* session.debug = SESSION_DEBUG_LOW; */ + session.debug = 0; + if ((session.dataRcvd = (u_int8_t *)calloc(sizeof(u_int8_t), + mss * session.maxpkts)) == NULL) { + printf("no memmory to store data:\nRETURN CODE: %d", + ERR_MEM_ALLOC); + Quit(ERR_MEM_ALLOC); + } + + if ((rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { + perror("ERROR: couldn't open socket:"); + Quit(ERR_SOCKET_OPEN); + } + + if (setsockopt(rawSocket, IPPROTO_IP, IP_HDRINCL, + (char *)&flag, sizeof(flag)) < 0) { + perror("ERROR: couldn't set raw socket options:"); + Quit(ERR_SOCKOPT); + } + + session.socket = rawSocket; + + /* Establish a TCP connections with ECN bits */ + rc = EstablishTcpConnection(TCPFLAGS_ECN_ECHO | TCPFLAGS_CWR, 0); + switch (rc) { + case ESTABLISH_FAILURE_EARLY_RST: + { + /* Received a RST when ECN bits are used. Try again without ECN */ + rc = EstablishTcpConnection(0, 0); + if (rc == ESTABLISH_FAILURE_EARLY_RST) { + printf("Received RST with or without ECN negotiation\n"); + printf("The server might not be listening on this port\n"); + Quit(EARLY_RST); + } else if (rc == ESTABLISH_SUCCESS) { + printf("Received RST with ECN.\n"); + printf("Connection established successfully without ECN\n"); + Quit(ECN_SYN_DROP); + } else if (rc == ESTABLISH_FAILURE_NO_REPLY) { + printf("Received RST with ECN\n"); + printf("Exceed max SYN retransmits without ECN\n"); + Quit(NO_CONNECTION); + } + break; + } + case ESTABLISH_FAILURE_NO_REPLY: + { + /* No reply after retring, try again without ECN */ + rc = EstablishTcpConnection(0, 0); + if (rc == ESTABLISH_FAILURE_EARLY_RST) { + printf("Exceeded max SYN retransmits with ECN\n"); + printf("Received RST without ECN\n"); + Quit(NO_CONNECTION); + } else if (rc == ESTABLISH_FAILURE_NO_REPLY) { + printf("Exceeded max SYN retransmits with ECN\n"); + printf("Exceeded max SYN retransmits without ECN\n"); + Quit(NO_CONNECTION); + } else { + printf("Exceeded max SYN retransmits with ECN\n"); + printf("Connection established successfully without ECN\n"); + Quit(ECN_SYN_DROP); + } + break; + } + } + + /* Test for propogation of CE correctly */ + DataPkt(session.filename, 3, 0); + + checkECN(); + return; +} + +void DataPkt (char *filename, u_int8_t iptos, u_int8_t tcp_flags) +{ + struct IPPacket *p, *datapkt; + struct pcap_pkthdr pi; + char *read_packet; + int i ; + int sendflag = 1 ; + u_int16_t lastSeqSent = session.snd_nxt; + double startTime = 0; + char *dataptr ; + char data[MAXREQUESTLEN]; + int datalen; + int ipsz; + + datalen = PrepareRequest (data, filename); + + datapkt = AllocateIPPacket(0, 0, datalen + 1, "ECN (datapkt)"); + + dataptr = (char *)datapkt->tcp + sizeof(struct TcpHeader); + memcpy((void *)dataptr,(void *)data, datalen); + + ipsz = sizeof(struct IpHeader) + sizeof(struct TcpHeader) + datalen + 1; + + /* send the data packet + * we try to "achieve" reliability by + * sending the packet upto 5 times, wating for + * 2 seconds between packets + * BAD busy-wait loop + */ + + i = 0 ; + while(1) { + + if (sendflag == 1) { + SendSessionPacket(datapkt, ipsz, + TCPFLAGS_PSH | TCPFLAGS_ACK | tcp_flags, 0, 0, iptos); + + startTime = GetTime(); + sendflag = 0 ; + i++ ; + } + + /* Check if we have received any packets */ + if ((read_packet =(char *)CaptureGetPacket(&pi)) != NULL) { + p = (struct IPPacket *)FindHeaderBoundaries(read_packet); + + /* + * packet that we sent? + */ + + if (INSESSION(p,session.src, session.sport, + session.dst,session.dport) && + (p->tcp->tcp_flags == (TCPFLAGS_PSH | TCPFLAGS_ACK)) && + SEQ_GT(ntohl(p->tcp->tcp_seq), lastSeqSent) && + SEQ_LEQ(ntohl(p->tcp->tcp_ack), session.rcv_nxt)) { + lastSeqSent = ntohl(p->tcp->tcp_seq); + if (session.debug >= SESSION_DEBUG_LOW) { + printf("xmit %d\n", i); + PrintTcpPacket(p); + } + StorePacket(p); + session.snd_nxt += datalen + 1; + session.totSeenSent ++ ; + continue ; + } + + /* + * from them? + */ + if (INSESSION(p, session.dst, session.dport, session.src, + session.sport) && (p->tcp->tcp_flags & TCPFLAGS_ACK) && + (ntohl(p->tcp->tcp_seq) == session.rcv_nxt) && + SEQ_GT(ntohl(p->tcp->tcp_ack), session.snd_una)) { + session.snd_una = ntohl(p->tcp->tcp_ack); + session.snd_nxt = session.snd_una; + if (p->ip->ip_ttl != session.ttl) { + session.ttl = p->ip->ip_ttl; + } + if (session.debug >= SESSION_DEBUG_LOW) { + printf("rcvd %d\n", i); + PrintTcpPacket(p); + } + StorePacket(p); + session.totRcvd ++; + break ; + } + /* + * otherwise, this is a bad packet + * we must quit + */ + //processBadPacket(p); + } + if ((GetTime() - startTime >= 1) && (sendflag == 0) && (i < 3)) { + sendflag = 1 ; + } + if (i >= 3) { + printf ("ERROR: sent request 3 times without response\n"); + return; + } + } + + FreeIPPacket(&datapkt); + + /* process any response by sending Acks */ + rcvData (ECNAckData); +} + +void ECNAckData (struct IPPacket *p) +{ + + uint32 seq = history[session.hsz - 1].seqno; + uint16 datalen = history[session.hsz - 1].dlen; + int fin = history[session.hsz - 1].fin; + int rst = history[session.hsz - 1].rst; + int i; + struct IPPacket *ackpkt = NULL; + static int ECT_00 = 0; + static int ECT_01 = 0; + static int ECT_10 = 0; + static int ECT_11 = 0; + static int ECN_ECHO = 0; + static int send_cwr = 0; + static int send_ece = 0; + uint8 tcp_flags = 0; + + + /* Legend: + * ECN_ECHO: counts packets with TCP header ECN bit set + * ECT_XX: counts packets with ECT codepoint XX (IP) + */ + + if (datalen > session.mss) { + printf ("ERROR: mss=%d datalen=%d\nRETURN CODE: %d\n", + session.mss, datalen, MSS_ERR); + Quit(MSS_ERR); + } + + if (datalen > 0) { + char *http_code = (char *)calloc(4, sizeof(char)); + if (seq - session.irs == 1) { + /* Response to request packet --> check HTTP response code */ + memcpy(http_code, ((char *)(p->tcp) + sizeof(struct TcpHeader) + + history[session.hsz - 1].optlen + 9), 3); + if (strncmp(http_code, HTTP_OK, 3) != 0) { + printf("HTTP ERROR - HTTP RESPONSE CODE: %s\nRETURN CODE: %d\n", + http_code, atoi(http_code)); + Quit(atoi(http_code)); + } + } + + session.totDataPktsRcvd++; + + if (session.verbose) { + printf ("r %f %d %d\n", + GetTime() - session.epochTime, + seq - session.irs, + seq - session.irs + datalen); + } + + } + + /* Check if packet has the ECN_ECHO flag set */ + if (history[session.hsz - 1].ecn_echo) { + ECN_ECHO += 1; + } + + if ((p->ip->ip_tos & 0x17) == 0) { + ECT_00 += 1; + } + if ((p->ip->ip_tos & 0x17) == 1) { + ECT_01 += 1; + } + if ((p->ip->ip_tos & 0x17) == 2) { + ECT_10 += 1; + } + if ((p->ip->ip_tos & 0x17) == 3) { + ECT_11 += 1; + } + + if(session.maxseqseen < seq + datalen - 1) { + session.maxseqseen = seq + datalen - 1; + } else { + if (datalen > 0) { + if (reordered(p) != 1) { + session.num_unwanted_drops += 1; + } + } + } + + /* from TCP/IP vol. 2, p 808 */ + if (SEQ_LEQ(session.rcv_nxt, seq) && + SEQ_LT(seq, (session.rcv_nxt + session.rcv_wnd)) && + SEQ_LEQ(session.rcv_nxt, (seq + datalen)) && + SEQ_LT((seq+datalen-1), (session.rcv_nxt + session.rcv_wnd))) { + int start, end; + start = seq - session.irs ; + end = start + datalen ; + + for (i = start ; i < end ; i++) { + session.dataRcvd[i] = 1 ; + } + + start = session.rcv_nxt - session.irs ; + end = session.mss * session.maxpkts ; + + for (i = start ; i < end ; i++) { + if (session.dataRcvd[i] == 0) { + break ; + } + session.rcv_nxt++ ; + } + } + + if (datalen > 0) { + if (session.verbose) { + printf ("a %f %d\n", GetTime() - session.epochTime, + session.rcv_nxt - session.irs); + } + ackpkt = AllocateIPPacket(0, 0, 0, "NewECN (ACK)"); + if ((p->ip->ip_tos & 0x17) == 3) { + tcp_flags = TCPFLAGS_ACK | TCPFLAGS_ECN_ECHO; + } else { + tcp_flags = TCPFLAGS_ACK; + } + + if (send_cwr == 2 && send_ece < 2) { + /* Send ECE as if a CE was received, we have to get CWR back */ + send_ece = 1; + tcp_flags |= TCPFLAGS_ECN_ECHO; + } + + SendSessionPacket (ackpkt, + sizeof(struct IpHeader) + sizeof(struct TcpHeader), + tcp_flags, 0, 0, 0); + } + + if (send_cwr == 0 && (p->tcp->tcp_flags & TCPFLAGS_ECN_ECHO)) { + /* Send CWR atleast once if ECN ECHO is set */ + int datalen; + struct IPPacket *datapkt; + char *dataptr; + char data[MAXREQUESTLEN]; + int ipsz; + + datalen = PrepareRequest(data, NULL); + datapkt = AllocateIPPacket(0, 0, datalen + 1, "ECN (datapkt)"); + dataptr = (char *)datapkt->tcp + sizeof(struct TcpHeader); + memcpy((void *)dataptr, (void *)data, datalen); + ipsz = sizeof(struct IpHeader) + sizeof(struct TcpHeader) + + datalen + 1; + + SendSessionPacket(datapkt, ipsz, + TCPFLAGS_PSH | TCPFLAGS_ACK | TCPFLAGS_CWR, 0, 0, 2); + + session.snd_nxt += (datalen + 1); + send_cwr = 1; + FreeIPPacket(&datapkt); + } + + if (send_cwr == 1 && !(p->tcp->tcp_flags & TCPFLAGS_ECN_ECHO)) { + /* ECE was reset in response to CWR, move to the next state of probing */ + send_cwr = 2; + } + + if (send_ece == 1 && (p->tcp->tcp_flags & TCPFLAGS_CWR)) { + /* Received CWR in response to ECE */ + send_ece = 2; + } + + if (SEQ_GT(ntohl(p->tcp->tcp_ack), session.snd_una)) + session.snd_una = ntohl(p->tcp->tcp_ack); + if (SEQ_LT(session.snd_nxt, session.snd_una)) + session.snd_nxt = session.snd_una; + + if (fin || rst) { + /* Increment sequence number for FIN rcvd */ + session.rcv_nxt++; + if (ECT_01 == 0 && ECT_10 == 0) { + printf("Never received ECT(0) or ECT(1) in ToS field: FAIL\n"); + } + if (ECT_11 > 3) { + /* If we received more than 3 CE, flag it as an error */ + printf("Received too many ECT_CE (%d): FAIL\n", ECT_11); + } + printf ("Totdata = %d ECN_ECHO: %d ECT00: %d ECT01: %d ECT10: %d ECT11: %d drops: %d\n", + session.rcv_nxt - session.irs, ECN_ECHO, ECT_00, + ECT_01, ECT_10, ECT_11, session.num_unwanted_drops); + if (fin) { + SendSessionPacket (ackpkt, + sizeof(struct IpHeader) + sizeof(struct TcpHeader), + tcp_flags, 0, 0, 0); + } + checkECN(); + Quit(SUCCESS); + } +} + +void checkECN () +{ + int i; + int sr = 0; /* sr=1: SYN/ACK rcvd */ + int se = 0; /* se=0: no CWR/no ECHO; se=1: no CWR/ECHO; se=2: CWR/ECHO */ + int ar = 0; /* ar=0: no ACK rcvd; ar=1: ACK rcvd */ + int ae = 0; /* ae=0: ACK/no ECHO; ae=1: ACK/ECHO */ + int we = 0; /* we=0: no ECHO; we=1 ECHO/CWR; we=2 ECHO/CWR/ECHO stop */ + int ee = 0; /* ee=0 never sent ECE; ee=1 sent ECE; ee=2 ECE / CWR */ + + for (i = 0 ; i < session.hsz; i++) { + if ((history[i].type == RCVD) && (history[i].syn == 1) && + (history[i].ack == 1)) { + sr = 1; + if (history[i].ecn_echo == 1) { + se = 1; + if (history[i].cwr == 1) { + se = 2; + } + } + } + } + + for (i = 0 ; i < session.hsz; i++) { + if (history[i].type == RCVD && history[i].syn == 0 && + history[i].ack == 1) { + ar = 1; + if (history[i].ecn_echo == 1) { + ae = 1; + } + } + } + + for (i = 0; i < session.hsz; i++) { + if (history[i].type == SENT && history[i].dlen > 0 && + history[i].cwr == 1) { + we = 1; + continue; + } + if (we == 1 && history[i].type == RCVD && history[i].ecn_echo == 0) { + we = 2; + break; + } + if (we == 2 && history[i].type == RCVD && history[i].ecn_echo == 1) { + we = 1; + break; + } + } + + for (i = 0; i < session.hsz; i++) { + if (history[i].type == SENT && history[i].ecn_echo == 1) { + ee = 1; + continue; + } + if (ee == 1 && history[i].type == RCVD && history[i].dlen > 0 && + history[i].cwr == 1) { + /* Received cwr in response to ECE */ + ee = 2; + break; + } + } + + printf ("sr=%d se=%d ar=%d ae=%d we=%d\n", sr, se, ar, ae, we); + switch (sr) { + case 0: + printf("No SYN/ACK received from server\n"); + break; + case 1: + printf("SYN/ACK received: PASS \n"); + break; + default: + printf("Unknown value for sr: %d\n", sr); + break; + } + switch (se) { + case 0: + printf("No CWR or ECE on SYN/ACK, server does not support ECN\n"); + break; + case 1: + printf("ECE flag set on SYN/ACK, server supports ECN: PASS\n"); + break; + case 2: + printf("Both CWR and ECE set on SYN/ACK, incompatible SYN/ACK\n"); + break; + default: + printf("Unknown value for se: %d\n", se); + break; + } + + switch (ar) { + case 0: + printf("No ACK received\n"); + break; + case 1: + printf("ACK received: PASS\n"); + break; + default: + printf("Unknown value for ar: %d\n", ar); + break; + } + + switch (ae) { + case 0: + printf("Received ACKS but never ECE\n"); + break; + case 1: + printf("Received ACKs with ECE, in response to simulated CE bit: PASS\n"); + break; + default: + printf("Unknown value for ae: %d\n", ae); + break; + } + + switch (we) { + case 0: + printf("Never received ECE\n"); + break; + case 1: + printf("Received ECE and sent CWR\n"); + break; + case 2: + printf("Received ECE, sent CWR and stopped receiving ECE afterwards: PASS\n"); + break; + default: + printf("Unknown value for we: %d\n", we); + break; + } + + switch (ee) { + case 0: + printf("Never sent ECE\n"); + break; + case 1: + printf("Sent ECE to simulate receiving CE \n"); + break; + case 2: + printf("Sent ECE and received CWR in response: PASS\n"); + break; + default: + printf("Unknown value for ee: %d\n", ee); + break; + } + return; +} + +void DataPktPathCheck(char *filename, u_int8_t iptos, u_int8_t tcp_flags) +{ + struct IPPacket *p, *datapkt; + struct pcap_pkthdr pi; + char *read_packet; + int i ; + int sendflag = 1 ; + u_int16_t lastSeqSent = session.snd_nxt; + double startTime = 0; + char *dataptr; + char data[MAXREQUESTLEN]; + int datalen; + int ipsz; + unsigned int init_ttl; + + datalen = PrepareRequest (data, filename); + + datapkt = AllocateIPPacket(0, 0, datalen + 1, "ECN (datapkt)"); + + dataptr = (char *)datapkt->tcp + sizeof(struct TcpHeader); + memcpy((void *)dataptr,(void *)data, datalen); + + ipsz = sizeof(struct IpHeader) + sizeof(struct TcpHeader) + datalen + 1; + /* send the data packet + * we try to "achieve" reliability by + * sending the packet upto 5 times, wating for + * 2 seconds between packets + * BAD busy-wait loop + */ + + i = 0 ; + init_ttl = 1; + while(1) { + + if (sendflag == 1) { + session.curr_ttl = ++init_ttl; + if (init_ttl > 64) /* reached the max */ + break; + SendSessionPacket(datapkt, ipsz, + TCPFLAGS_PSH | TCPFLAGS_ACK | tcp_flags, 0, 0, 0x3); + + startTime = GetTime(); + sendflag = 0; + i++ ; + } + + /* Check if we have received any packets */ + if ((read_packet =(char *)CaptureGetPacket(&pi)) != NULL) { + + p = (struct IPPacket *)FindHeaderBoundaries(read_packet); + + /* + * packet that we sent? + */ + + if (INSESSION(p,session.src, session.sport, + session.dst,session.dport) && + (p->tcp->tcp_flags == (TCPFLAGS_PSH | TCPFLAGS_ACK)) && + SEQ_GT(ntohl(p->tcp->tcp_seq), lastSeqSent) && + SEQ_LEQ(ntohl(p->tcp->tcp_ack), session.rcv_nxt)) { + lastSeqSent = ntohl(p->tcp->tcp_seq); + if (session.debug >= SESSION_DEBUG_LOW) { + printf("xmit %d\n", i); + PrintTcpPacket(p); + } + StorePacket(p); + session.snd_nxt += datalen + 1; + session.totSeenSent ++ ; + continue ; + } + + /* + * from them? + */ + if (INSESSION(p, session.dst, session.dport, session.src, + session.sport) && (p->tcp->tcp_flags & TCPFLAGS_ACK) && + (ntohl(p->tcp->tcp_seq) == session.rcv_nxt) && + ntohl(p->tcp->tcp_ack) == session.snd_nxt) { + session.snd_una = ntohl(p->tcp->tcp_ack); + session.snd_nxt = session.snd_una; + if (p->ip->ip_ttl != session.ttl) { + session.ttl = p->ip->ip_ttl; + } + if (session.debug >= SESSION_DEBUG_LOW) { + printf("rcvd %d\n", i); + PrintTcpPacket(p); + } + StorePacket(p); + session.totRcvd ++; + break ; + } + /* + * ICMP ttl exceeded + */ + if (p->ip->ip_p == IPPROTOCOL_ICMP) { + uint16_t ip_hl; + struct IcmpHeader *icmp; + ip_hl = (p->ip->ip_vhl & 0x0f) << 2; + void *nexthdr; + + icmp = (struct IcmpHeader *)((char *)p->ip + ip_hl); + nexthdr = (void *)icmp; + if (icmp->icmp_type == ICMP_TIMXCEED) { + struct IpHeader off_ip; + struct TcpHeader off_tcp; + + bzero(&off_ip, sizeof(struct IpHeader)); + nexthdr = nexthdr + sizeof(struct IcmpHeader); + memcpy((void *)&off_ip, nexthdr, + sizeof(struct IpHeader)); + nexthdr += sizeof(struct IpHeader); + + bzero(&off_tcp, sizeof(struct TcpHeader)); + memcpy(&off_tcp, nexthdr, 4); + if (session.src == off_ip.ip_src && + session.dst == off_ip.ip_dst) { + printf("Received ICMP TTL exceeded from %s:" + "ttl used %u ip_tos 0x%x sport %u dport %u\n", + InetAddress(p->ip->ip_src), init_ttl, off_ip.ip_tos, + ntohs(off_tcp.tcp_sport), ntohs(off_tcp.tcp_dport)); + } + } + } + /* + * otherwise, this is a bad packet + * we must quit + */ + //processBadPacket(p); + } + if ((GetTime() - startTime >= 1) && (sendflag == 0)) { + sendflag = 1; + session.snd_nxt = session.snd_una; + } + } + + FreeIPPacket(&datapkt); +} +void ECNPathCheckTest(u_int32_t sourceAddress, u_int16_t sourcePort, + u_int32_t targetAddress, u_int16_t targetPort, int mss) +{ + int rawSocket, rc, flag; + + arc4random_stir(); + + session.src = sourceAddress; + session.sport = sourcePort; + session.dst = targetAddress; + session.dport = targetPort; + session.rcv_wnd = 5*mss; + session.snd_nxt = arc4random(); + session.iss = session.snd_nxt; + session.rcv_nxt = 0; + session.irs = 0; + session.mss = mss; + session.maxseqseen = 0; + session.epochTime = GetTime(); + session.maxpkts = 1000; + session.debug = 0; + + if ((session.dataRcvd = (u_int8_t *)calloc(sizeof(u_int8_t), + mss * session.maxpkts)) == NULL) { + printf("no memory to store data, error: %d \n", ERR_MEM_ALLOC); + Quit(ERR_MEM_ALLOC); + } + + if ((rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { + perror("ERROR: couldn't open socket:"); + Quit(ERR_SOCKET_OPEN); + } + + flag = 1; + if (setsockopt(rawSocket, IPPROTO_IP, IP_HDRINCL, + (char *)&flag, sizeof(flag)) < 0) { + perror("ERROR: couldn't set raw socket options:"); + Quit(ERR_SOCKOPT); + } + + session.socket = rawSocket; + + /* Establish a TCP connections with ECN bits */ + rc = EstablishTcpConnection(TCPFLAGS_ECN_ECHO | TCPFLAGS_CWR, 0); + switch (rc) { + case ESTABLISH_FAILURE_EARLY_RST: + { + /* Received a RST when ECN bits are used. Try again without ECN */ + rc = EstablishTcpConnection(0, 0); + if (rc == ESTABLISH_FAILURE_EARLY_RST) { + printf("Received RST with or without ECN negotiation\n"); + printf("The server might not be listening on this port\n"); + Quit(EARLY_RST); + } else if (rc == ESTABLISH_SUCCESS) { + printf("Received RST with ECN.\n"); + printf("Connection established successfully without ECN\n"); + Quit(ECN_SYN_DROP); + } else if (rc == ESTABLISH_FAILURE_NO_REPLY) { + printf("Received RST with ECN\n"); + printf("Exceed max SYN retransmits without ECN\n"); + Quit(NO_CONNECTION); + } + break; + } + case ESTABLISH_FAILURE_NO_REPLY: + { + /* No reply after retring, try again without ECN */ + rc = EstablishTcpConnection(0, 0); + if (rc == ESTABLISH_FAILURE_EARLY_RST) { + printf("Exceeded max SYN retransmits with ECN\n"); + printf("Received RST without ECN\n"); + Quit(NO_CONNECTION); + } else if (rc == ESTABLISH_FAILURE_NO_REPLY) { + printf("Exceeded max SYN retransmits with ECN\n"); + printf("Exceeded max SYN retransmits without ECN\n"); + Quit(NO_CONNECTION); + } else { + printf("Exceeded max SYN retransmits with ECN\n"); + printf("Connection established successfully without ECN\n"); + Quit(ECN_SYN_DROP); + } + break; + } + } + + DataPktPathCheck(session.filename, 3, 0); + return; +} + + +void +SynTest(u_int32_t sourceAddress, u_int16_t sourcePort, + u_int32_t targetAddress, u_int16_t targetPort, int mss, int syn_reply) +{ + int rawSocket, flag; + struct IPPacket *synPacket = NULL, *ackPacket = NULL; + char *read_packet; + struct pcap_pkthdr pi; + int synAckReceived = 0; + int numRetransmits = 0; + double timeoutTime; + int tcpoptlen = 4; /* For negotiating MSS */ + u_int8_t *opt = NULL; + struct IPPacket *p = NULL; + + arc4random_stir(); + + session.src = sourceAddress; + session.sport = sourcePort; + session.dst = targetAddress; + session.dport = targetPort; + session.rcv_wnd = 5*mss; + session.snd_nxt = arc4random(); + session.iss = session.snd_nxt; + session.rcv_nxt = 0; + session.irs = 0; + session.mss = mss; + session.maxseqseen = 0; + session.epochTime = GetTime(); + session.maxpkts = 1000; + + if ((session.dataRcvd = (u_int8_t *)calloc(sizeof(u_int8_t), + mss * session.maxpkts)) == NULL) { + printf("no memory to store data, error: %d \n", ERR_MEM_ALLOC); + Quit(ERR_MEM_ALLOC); + } + + if ((rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { + perror("ERROR: couldn't open socket:"); + Quit(ERR_SOCKET_OPEN); + } + + flag = 1; + if (setsockopt(rawSocket, IPPROTO_IP, IP_HDRINCL, + (char *)&flag, sizeof(flag)) < 0) { + perror("ERROR: couldn't set raw socket options:"); + Quit(ERR_SOCKOPT); + } + + session.socket = rawSocket; + + + /* allocate the syn packet -- Changed for new IPPacket structure */ + synPacket = AllocateIPPacket(0, tcpoptlen, 0, "ECN (SYN)"); + opt = (((u_int8_t *)synPacket->tcp) + sizeof(struct TcpHeader)); + opt[0] = (u_int8_t)TCPOPT_MAXSEG; + opt[1] = (u_int8_t)TCPOLEN_MAXSEG; + *((u_int16_t *)((u_int8_t *)opt + 2)) = htons(session.mss); + + SendSessionPacket(synPacket, + sizeof(struct IpHeader) + sizeof(struct TcpHeader) + tcpoptlen, + TCPFLAGS_SYN , 0, tcpoptlen, 0); + timeoutTime = GetTime() + 1; + + /* + * Wait for SYN/ACK and retransmit SYN if appropriate + * not great, but it gets the job done + */ + + while(!synAckReceived && numRetransmits < 3) { + while(GetTime() < timeoutTime) { + /* Have we captured any packets? */ + if ((read_packet = (char *)CaptureGetPacket(&pi)) != NULL) { + p = (struct IPPacket *)FindHeaderBoundaries(read_packet); + /* Received a packet from us to them */ + if (INSESSION(p, session.src, session.sport, + session.dst, session.dport)) { + /* Is it a SYN/ACK? */ + if (p->tcp->tcp_flags & TCPFLAGS_SYN) { + if (session.debug >= SESSION_DEBUG_LOW) { + PrintTcpPacket(p); + } + StorePacket(p); + session.totSeenSent++ ; + } else { + processBadPacket(p); + } + continue; + } + + /* Received a packet from them to us */ + if (INSESSION(p, session.dst, session.dport, session.src, + session.sport)) { + /* Is it a SYN/ACK? */ + if ((p->tcp->tcp_flags & TCPFLAGS_SYN) && + (p->tcp->tcp_flags & TCPFLAGS_ACK)) { + timeoutTime = GetTime(); /* force exit */ + synAckReceived++; + if (session.debug >= SESSION_DEBUG_LOW) { + PrintTcpPacket(p); + } + StorePacket(p); + + /* + * Save ttl for,admittedly poor,indications of reverse + * route change + */ + session.ttl = p->ip->ip_ttl; + session.snd_wnd = ntohl(p->tcp->tcp_win); + session.totRcvd ++; + break; + } else { + if ((p->tcp->tcp_flags)& (TCPFLAGS_RST)) { + printf ("ERROR: EARLY_RST\n"); + goto done; + } + } + } + } + } + + if (!synAckReceived) { + if (session.debug >= SESSION_DEBUG_LOW) { + printf("SYN timeout. Retransmitting\n"); + } + SendSessionPacket(synPacket, + sizeof(struct IpHeader) + sizeof(struct TcpHeader) + tcpoptlen, + TCPFLAGS_SYN , 0, tcpoptlen, 0); + timeoutTime = GetTime() + 1; + numRetransmits++; + } + } + + if (numRetransmits >= 3) { + printf("ERROR: No connection after 3 retries...\nRETURN CODE: %d\n", + NO_CONNECTION); + goto done; + } + if (session.debug >= SESSION_DEBUG_LOW) + printf("Received SYN-ACK\n"); + if (syn_reply != 0) { + /* Update session variables */ + session.irs = ntohl(p->tcp->tcp_seq); + session.dataRcvd[0] = 1 ; + session.rcv_nxt = session.irs + 1; /* SYN/ACK takes up a byte of seq space */ + session.snd_nxt = session.iss + 1; /* SYN takes up a byte of seq space */ + session.snd_una = session.iss + 1; + session.maxseqseen = ntohl(p->tcp->tcp_seq); + session.initSession = 1; + if (session.debug >= SESSION_DEBUG_LOW) { + printf("try to send the %s\n", syn_reply == TCPFLAGS_ACK ? "third Ack" : "RST"); + printf("src = %s:%d (%u)\n", InetAddress(session.src), + session.sport, session.iss); + printf("dst = %s:%d (%u)\n",InetAddress(session.dst), + session.dport, session.irs); + } + + /* allocate the syn packet -- Changed for new IPPacket structure */ + ackPacket = AllocateIPPacket(0, 0, 0, "SYN reply"); + /* send an ACK */ + SendSessionPacket(ackPacket, + sizeof(struct IpHeader) + sizeof(struct TcpHeader), + syn_reply, 0, 0, 0); + FreeIPPacket(&ackPacket); + } +done: + FreeIPPacket(&synPacket); +} diff --git a/network_cmds-543/ecnprobe/ecn.h b/network_cmds-543/ecnprobe/ecn.h new file mode 100644 index 00000000..5ac582c3 --- /dev/null +++ b/network_cmds-543/ecnprobe/ecn.h @@ -0,0 +1,49 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI 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 ICSI 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. +*/ +void ECNTest (u_int32_t sourceIpAddress, u_int16_t sourcePort, u_int32_t targetIpAddress, u_int16_t targetPort, int mss) ; +void ECNAckData (struct IPPacket *p); +void DataPkt (char *filename, u_int8_t iptos, u_int8_t tcp_flags); +void checkECN (); +void ECNPathCheckTest(u_int32_t sourceIpAddress, u_int16_t surcePort, + u_int32_t targetIpAddress, u_int16_t targetPort, int mss); +void SynTest(u_int32_t sourceIpAddress, u_int16_t surcePort, u_int32_t targetIpAddress, u_int16_t targetPort, int mss, int syn_reply); diff --git a/network_cmds-543/ecnprobe/ecn_probe.c b/network_cmds-543/ecnprobe/ecn_probe.c new file mode 100644 index 00000000..ae00f4ad --- /dev/null +++ b/network_cmds-543/ecnprobe/ecn_probe.c @@ -0,0 +1,475 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI 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 ICSI 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "inet.h" +#include "capture.h" +#include "support.h" +#include "session.h" +#include "ecn.h" +#include "history.h" + +extern struct TcpSession session; + +void usage (char *name); +int GetCannonicalInfo(char *string, u_int32_t *address); +int BindTcpPort(int sockfd) ; + +void usage(char *name) +{ + printf("%s [options]\n", name); + printf("\t-n \n"); + printf("\t-p \n"); + printf("\t-m \n"); + printf("\t-M \n"); + printf("\t-w \n"); + printf("\t-s \n"); + printf("\t-f \n"); + printf("\t-d \n"); + printf("\t-C for CE path check\n"); + printf("\t-S [A|R|X] SYN followed by ACK or RST or nothing\n"); + printf("\t-F [set|clear|skip] how to handle firewall rules\n"); + return; +} + +void SetupFirewall(u_int32_t targetIP, u_int16_t port, char *dev) +{ + char pfcmd[512]; + char *pf_file_name = "/tmp/pf.conf"; + int pf_fd = 0, rc; + ssize_t bytes; + char *args[4]; + + bzero(pfcmd, sizeof(pfcmd)); + + bzero(args, sizeof(args)); + sprintf(pfcmd, "block in quick on %s inet proto tcp from %s port %u\n", + dev, InetAddress(targetIP), port); + if (session.debug >= SESSION_DEBUG_LOW) + printf("PF rule: %s\n", pfcmd); + + pf_fd = open(pf_file_name, O_RDWR|O_TRUNC|O_CREAT); + if (pf_fd < 0) { + perror("failed to open pf file"); + exit(1); + } + bytes = write(pf_fd, pfcmd, strlen(pfcmd) + 1); + close(pf_fd); + args[0] = "pfctl"; + args[1] = "-d"; + args[2] = NULL; + rc = posix_spawn(NULL, "/sbin/pfctl", NULL, NULL, args, NULL); + if (rc != 0) { + printf("Failed to exec: pfctl -d: %d\n", rc); + Quit(FAIL); + } + + args[1] = "-f"; + args[2] = pf_file_name; + args[3] = NULL; + rc = posix_spawn(NULL, "/sbin/pfctl", NULL, NULL, args, NULL); + if (rc != 0) { + printf("Failed to exec: pfctl -f /tmp/pf.conf: %d\n", rc); + Quit(FAIL); + } + + args[1] = "-e"; + args[2] = NULL; + rc = posix_spawn(NULL, "/sbin/pfctl", NULL, NULL, args, NULL); + if (rc != 0) { + printf("Failed to exec: pfctl -e: %d\n", rc); + Quit(FAIL); + } +} + +void CleanupFirewall() +{ + char * args[3]; + int rc; + + args[0] = "pfctl"; + args[1] = "-d"; + args[2] = NULL; + rc = posix_spawn(NULL, "/sbin/pfctl", NULL, NULL, args, NULL); + if (rc != 0) { + printf("Failed to exec: pfctl -d: %d\n", rc); + Quit(FAIL); + } +} + +void Cleanup() +{ + if (session.initSession > 0) { + shutdown(session.socket, 2); + } + if (session.initCapture > 0) { + CaptureEnd(); + } + if (session.initFirewall > 0) { + CleanupFirewall(); + } +} + +void Quit(int how) +{ + SendReset(); + Cleanup(); + fflush(stdout); + fflush(stderr); + exit(how); +} + +void SigHandle(int signo) +{ + Cleanup(); + fflush(stdout); + fflush(stderr); + exit(-1); +} + +int GetCannonicalInfo(char *string, u_int32_t *address) +{ + struct hostent *hp; + /* Is string in dotted decimal format? */ + if ((*address = inet_addr(string)) == INADDR_NONE) { + /* No, then lookup IP address */ + if ((hp = gethostbyname(string)) == NULL) { + /* Can't find IP address */ + printf("ERROR: Couldn't obtain address for %s\n" + "RETURN CODE: %d\n", string, FAIL); + return(-1); + } else { + strncpy(string, hp->h_name, MAXHOSTNAMELEN-1); + memcpy((void *)address, (void *)hp->h_addr, + hp->h_length); + } + } else { + if ((hp = gethostbyaddr((char *)address, sizeof(*address), + AF_INET)) == NULL) { + /* + * Can't get cannonical hostname, so just use + * input string + */ + if (session.debug) { + printf("WARNING: Couldn't obtain cannonical" + " name for %s\nRETURN CODE: %d", + string, NO_SRC_CANON_INFO); + } + /* strncpy(name, string, MAXHOSTNAMELEN - 1);*/ + } else { + /* strncpy(name, hp->h_name, MAXHOSTNAMELEN - 1);*/ + } + } + return(0); +} + +int BindTcpPort(int sockfd) +{ + struct sockaddr_in sockName; + int port, result; + int randomOffset; + +#define START_PORT (50*1024) +#define END_PORT (0xFFFF) + + /* + * Choose random offset to reduce likelihood of + * collision with last run + */ + randomOffset = (int)(1000.0*drand48()); + + /* Try to find a free port in the range START_PORT+1..END_PORT */ + port = START_PORT+randomOffset; + do { + ++port; + sockName.sin_addr.s_addr = INADDR_ANY; + sockName.sin_family = AF_INET; + sockName.sin_port = 0; //htons(port); + result = bind(sockfd, (struct sockaddr *)&sockName, + sizeof(sockName)); + } while ((result < 0) && (port < END_PORT)); + + + if (result < 0) { + /* No free ports */ + perror("bind"); + port = 0; + } else { + socklen_t len = sizeof(sockName); + result = getsockname(sockfd, (struct sockaddr *)&sockName, &len); + if (result < 0) { + perror("getsockname"); + port = 0; + } else { + port = ntohs(sockName.sin_port); + } + } + return port; + +} + +#define FIREWALL_DEFAULT 0 +#define FIREWALL_SET_ONLY 1 +#define FIREWALL_CLEAR_ONLY 2 +#define FIREWALL_SKIP 3 + +int main(int argc, char **argv) +{ + u_int32_t targetIpAddress = 0; + u_int16_t targetPort = DEFAULT_TARGETPORT; + u_int16_t sourcePort = 0; + u_int32_t sourceIpAddress = 0; + int mss = DEFAULT_MSS; + int mtu = DEFAULT_MTU; + int fd, opt, usedev = 0, rc = 0, path_check = 0; + int syn_test = 0, syn_reply = 0; + struct sockaddr_in saddr; + char dev[11]; /* device name for pcap init */ + struct ifaddrs *ifap, *tmp; + int firewall_mode = FIREWALL_DEFAULT; + + bzero(&session, sizeof(session)); + while ((opt = getopt(argc, argv, "n:p:w:m:M:s:d:f:-CS:vF:")) != -1) { + switch (opt) { + case 'n': + if (strlen(optarg) > (MAXHOSTNAMELEN - 1)) { + printf("Target host name too long, max %u chars\n", MAXHOSTNAMELEN); + Quit(FAIL); + } + strncpy(session.targetHostName, optarg, + MAXHOSTNAMELEN); + strncpy(session.targetName, session.targetHostName, + MAXHOSTNAMELEN); + break; + case 'p': + targetPort = atoi(optarg); + break; + case 'm': + mss = atoi(optarg); + break; + case 'M': + mtu = atoi(optarg); + break; + case 'w': + sourcePort = atoi(optarg); + break; + case 's': + if (strlen(optarg) > (MAXHOSTNAMELEN - 1)) { + printf("Source host name too long, max %u chars\n", MAXHOSTNAMELEN); + Quit(FAIL); + } + strncpy(session.sourceHostName, optarg, + MAXHOSTNAMELEN); + break; + case 'd': + if (strlen(optarg) > (sizeof(dev) - 1)) { + printf("Interface nae is too large, max %lu chars\n", (sizeof(dev) - 1)); + Quit(FAIL); + } + bzero(dev, sizeof(dev)); + strncpy(dev, optarg, (sizeof(dev) - 1)); + usedev = 1; + break; + case 'f': + if (strlen(optarg) > 0) { + session.filename = strndup(optarg, strlen(optarg) + 1); + } else { + printf("Invalid file name \n"); + } + break; + case 'F': + if (strcasecmp(optarg, "default") == 0) + firewall_mode = FIREWALL_DEFAULT; + else if (strcasecmp(optarg, "set") == 0) + firewall_mode = FIREWALL_SET_ONLY; + else if (strcasecmp(optarg, "clear") == 0) + firewall_mode = FIREWALL_CLEAR_ONLY; + else if (strcasecmp(optarg, "skip") == 0) + firewall_mode = FIREWALL_SKIP; + else + printf("firewall mode\n"); + break; + case 'C': + path_check = 1; + break; + case 'S': + syn_test = 1; + if (strcasecmp(optarg, "A") == 0) + syn_reply = TCPFLAGS_ACK; + else if (strcasecmp(optarg, "R") == 0) + syn_reply = TCPFLAGS_RST; + else if (strcasecmp(optarg, "X") == 0) + syn_reply = 0; + else + printf("Invalid SYN reply \n"); + break; + case 'v': + session.debug++; + break; + default: + usage(argv[0]); + exit(1); + } + } + signal(SIGTERM, SigHandle); + signal(SIGINT, SigHandle); + signal(SIGHUP, SigHandle); + + if (GetCannonicalInfo(session.targetHostName, &targetIpAddress) < 0) + { + printf("Failed to convert targetIP address\n"); + Quit(NO_TARGET_CANON_INFO); + } + /* + if (GetCannonicalInfo(session.sourceHostName, &sourceIpAddress) < 0) + { + printf("Failed to convert source IP address\n"); + Quit(NO_TARGET_CANON_INFO); + } + */ + rc = getifaddrs(&ifap); + if (rc != 0 || ifap == NULL) { + printf("Failed to get source addresswith getifaddrs: %d\n", rc); + Quit(FAIL); + } + tmp = ifap; + sourceIpAddress = 0; + bzero(session.sourceHostName, MAXHOSTNAMELEN); + for (tmp = ifap; tmp != NULL; tmp = tmp->ifa_next) { + struct sockaddr_in *sin; + if (tmp->ifa_addr == NULL) + continue; + if (tmp->ifa_addr->sa_family != PF_INET) + continue; + if (usedev == 1) { + /* we know which interface to use */ + if (strcmp(dev, tmp->ifa_name) == 0) { + sin = (struct sockaddr_in *)tmp->ifa_addr; + sourceIpAddress = sin->sin_addr.s_addr; + strncpy(session.sourceHostName, + inet_ntoa(sin->sin_addr), + MAXHOSTNAMELEN); + } else { + continue; + } + } else { + /* pick the first address */ + bzero(dev, sizeof(dev)); + sin = (struct sockaddr_in *)tmp->ifa_addr; + sourceIpAddress = sin->sin_addr.s_addr; + strncpy(session.sourceHostName, + inet_ntoa(sin->sin_addr), + MAXHOSTNAMELEN); + strncpy(dev, tmp->ifa_name, sizeof(dev)); + } + } + freeifaddrs(ifap); + if (sourceIpAddress == 0) { + printf("Failed to get source Ip address\n"); + Quit(FAIL); + } + + if (sourcePort == 0) { + bzero(&saddr, sizeof(saddr)); + saddr.sin_family = AF_INET; + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + printf("Can't open socket\n"); + return (-1); + } + if ((sourcePort = BindTcpPort(fd)) == 0) { + printf("Can't bind to port\n"); + return (-1); + } + } + printf("Source: %s:%d\n", session.sourceHostName, sourcePort); + printf("Destination: %s:%d\n", session.targetHostName, targetPort); + + switch (firewall_mode) { + case FIREWALL_DEFAULT: + SetupFirewall(targetIpAddress, targetPort, dev); + session.initFirewall = 1; + break; + case FIREWALL_SET_ONLY: + SetupFirewall(targetIpAddress, targetPort, dev); + goto done; + case FIREWALL_CLEAR_ONLY: + session.initFirewall = 1; + goto done; + case FIREWALL_SKIP: + break; + } + + CaptureInit(sourceIpAddress, sourcePort, targetIpAddress, + targetPort, dev); + session.initCapture = 1; + + + printf("Starting ECN test\n"); + if (syn_test) { + session.dont_send_reset = 1; + SynTest(sourceIpAddress, sourcePort, targetIpAddress, + targetPort, mss, syn_reply); + } else if (path_check) { + ECNPathCheckTest(sourceIpAddress, sourcePort, targetIpAddress, + targetPort, mss); + } else { + ECNTest(sourceIpAddress, sourcePort, targetIpAddress, + targetPort, mss); + } +done: + Quit(SUCCESS); + close(session.socket); + return (0); +} diff --git a/network_cmds-543/ecnprobe/ecnprobe.1 b/network_cmds-543/ecnprobe/ecnprobe.1 new file mode 100644 index 00000000..76c21737 --- /dev/null +++ b/network_cmds-543/ecnprobe/ecnprobe.1 @@ -0,0 +1,20 @@ +.Dd 5/7/2015 +.Dt ecnprobe 1 +.Os Darwin +.Sh NAME +.Nm ecnprobe +.Nd Tool to probe for TCP ECN related incompatibilities in the network +.Sh SYNOPSIS +.Nm +.Op Fl n Ar target hostname or ipaddress +.Op Fl p Ar target port +.Op Fl m Ar mss +.Op Fl M Ar mtu +.Op Fl w Ar source port +.Op Fl s Ar source ip address +.Op Fl f Ar file name to get +.Op Fl d Ar interface name +.Sh DESCRIPTION +The +.Nm +utility can be used to test TCP ECN related incompatibilities in the network. diff --git a/network_cmds-543/ecnprobe/gmt2local.c b/network_cmds-543/ecnprobe/gmt2local.c new file mode 100644 index 00000000..a6114f37 --- /dev/null +++ b/network_cmds-543/ecnprobe/gmt2local.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 1997 + * 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: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include +#include + +#include +#ifdef TIME_WITH_SYS_TIME +#include +#endif + +#ifdef HAVE_OS_PROTO_H +#include "os-proto.h" +#endif + +#include "gmt2local.h" + +/* + * Returns the difference between gmt and local time in seconds. + * Use gmtime() and localtime() to keep things simple. + */ +int32_t +gmt2local(time_t t) +{ + register int dt, dir; + register struct tm *gmt, *loc; + struct tm sgmt; + + if (t == 0) + t = time(NULL); + gmt = &sgmt; + *gmt = *gmtime(&t); + loc = localtime(&t); + dt = (loc->tm_hour - gmt->tm_hour) * 60 * 60 + + (loc->tm_min - gmt->tm_min) * 60; + + /* + * If the year or julian day is different, we span 00:00 GMT + * and must add or subtract a day. Check the year first to + * avoid problems when the julian day wraps. + */ + dir = loc->tm_year - gmt->tm_year; + if (dir == 0) + dir = loc->tm_yday - gmt->tm_yday; + dt += dir * 24 * 60 * 60; + + return (dt); +} diff --git a/network_cmds-543/ecnprobe/gmt2local.h b/network_cmds-543/ecnprobe/gmt2local.h new file mode 100644 index 00000000..4933b091 --- /dev/null +++ b/network_cmds-543/ecnprobe/gmt2local.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 1997 + * 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: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#) $Header: gmt2local.h,v 1.2 97/01/23 22:31:40 leres Exp $ (LBL) + */ +#ifndef gmt2local_h +#define gmt2local_h + +int32_t gmt2local(time_t); +#endif diff --git a/network_cmds-543/ecnprobe/history.c b/network_cmds-543/ecnprobe/history.c new file mode 100644 index 00000000..e19b0ba5 --- /dev/null +++ b/network_cmds-543/ecnprobe/history.c @@ -0,0 +1,211 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI 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 ICSI 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 +#include +#include +#include +#include +#include +#include "base.h" +#include "inet.h" +#include "session.h" +#include "support.h" +#include "history.h" + +extern struct TcpSession session; +struct History history[MAXHSZ]; + +void StorePacket (struct IPPacket *p) { + + uint32 src, dst, seq, ack ; + uint16 sport, dport, win, urp, datalen, optlen; + uint16 ip_optlen; + uint8 flags; + + ReadIPPacket(p, + &src, &dst, + &sport, &dport, + &seq, + &ack, + &flags, + &win, + &urp, + &datalen, + &ip_optlen, + &optlen); + + if (src == session.src) { + history[session.hsz].type = SENT; + } else { + history[session.hsz].type = RCVD ; + } + + history[session.hsz].timestamp = GetTime () - session.epochTime; + history[session.hsz].seqno = seq; + history[session.hsz].nextbyte = seq + datalen; + history[session.hsz].ackno = ack ; + history[session.hsz].fin = (flags & TCPFLAGS_FIN) ? 1 : 0; + history[session.hsz].syn = (flags & TCPFLAGS_SYN) ? 1 : 0; + history[session.hsz].rst = (flags & TCPFLAGS_RST) ? 1 : 0; + history[session.hsz].psh = (flags & TCPFLAGS_PSH) ? 1 : 0; + history[session.hsz].ack = (flags & TCPFLAGS_ACK) ? 1 : 0; + history[session.hsz].urg = (flags & TCPFLAGS_URG) ? 1 : 0; + history[session.hsz].ecn_echo = (flags & TCPFLAGS_ECN_ECHO) ? 1:0; + history[session.hsz].cwr = (flags & TCPFLAGS_CWR) ? 1 : 0; + history[session.hsz].ip_optlen = ip_optlen; + history[session.hsz].optlen = optlen ; + + /* Grab IP Options from Ip Header - New */ + if (ip_optlen > 0) { + if ((history[session.hsz].ip_opt = calloc(sizeof(uint8), ip_optlen)) == NULL) { + printf("StorePacket Error: Could not allocate history memory\nRETURN CODE: %d\n", ERR_MEM_ALLOC); + Quit (ERR_MEM_ALLOC); + } + memcpy(history[session.hsz].ip_opt, (char *)p->ip + sizeof(struct IpHeader), ip_optlen); + } + + + /* Grab TCP options from TCP Header */ + if (optlen > 0) { + if ((history[session.hsz].opt = calloc(sizeof(uint8), optlen)) == NULL) { + Quit (ERR_MEM_ALLOC); + } + + memcpy(history[session.hsz].opt, (char *)p->tcp + sizeof(struct TcpHeader), optlen); + } + + history[session.hsz].dlen = datalen; + + if ((history[session.hsz].data = calloc(sizeof(uint8), datalen)) == NULL) { + Quit (ERR_MEM_ALLOC); + } + + /* Copy data bytes */ + memcpy(history[session.hsz].data, + (char *)p->tcp + sizeof(struct TcpHeader) + optlen, + datalen); + + session.hsz++; + + if (session.hsz >= MAXHSZ) { + Quit(TOO_MANY_PKTS); + } + +} + +int reordered(struct IPPacket *p) { + + int i; + int count = 0; + double ts = -99999; + + /* + * This might be either an unwanted packet drop, or just a reordering. + * Test: + * If we have not sent three acks for this packet + * AND the gap between this packet and previous one is "small" (i.e. not a timeout) + * then its a reordering, and not a retransmission. + */ + + /* count the number of (dup) ACKs sent */ + for (i = 0; i < session.hsz; i++) { + if ((history[i].type == SENT) && + (history[i].ack)) { + if (history[i].ackno == history[session.hsz - 1].seqno) + count += 1; + } + } + + if (count > 0) { + + session.num_dup_acks += count - 1; + + switch (count) { + case 1: /* no dup acks */ + session.num_pkts_0_dup_acks += 1; + break; + + case 2: /* 1 dup acks */ + session.num_pkts_1_dup_acks += 1; + break; + + case 3: /* 2 dup acks */ + session.num_pkts_2_dup_acks += 1; + break; + + case 4: /* 3 dup acks */ + session.num_pkts_3_dup_acks += 1; + break; + + default: + session.num_pkts_4_or_more_dup_acks += 1; + break; + } + } + + /* 3 dup acks? => Fast retransmission */ + if (count > 3) { + printf("Fast retransmit...\n"); + return 3; + } + + /* Compute elapsed time between this packet and the previously RCVD packet */ + for (i = (session.hsz - 2); i >= 0; i--) { + if ((history[i].type == RCVD) && (history[i].dlen > 0)) { + ts = history[i].timestamp; + break; + } + } + + if ((history[session.hsz - 1].timestamp - ts) > RTT_TO_MULT * (session.rtt + PLOTDIFF)) { + printf ("RTO ===> %f %f\n", history[session.hsz - 1].timestamp, ts); + return 2; + } + + printf ("#### Acks %d\n", count); + printf ("#### reordering detected\n"); + session.num_reordered++; + + return 1; + +} diff --git a/network_cmds-543/ecnprobe/history.h b/network_cmds-543/ecnprobe/history.h new file mode 100644 index 00000000..9e69d44e --- /dev/null +++ b/network_cmds-543/ecnprobe/history.h @@ -0,0 +1,75 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI 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 ICSI 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. +*/ +#define SENT 1 +#define RCVD 2 +#define MAXHSZ 10000 + +struct History { /* store history of each packet as it is seen */ + + int type ; /* sent or received */ + double timestamp; /* when */ + uint32 seqno; + uint32 nextbyte; /* seqno + dlen */ + uint32 ackno; + int hlen; + int ecn_echo; + int cwr; + int urg; + int ack; + int psh; + int rst; + int syn; + int fin; + int ip_optlen; /* added to support IP options */ + uint8 *ip_opt; /* added to support IP options */ + int optlen; + uint8 *opt; + uint8 *data; + int dlen; + + int pkt_num; + +}; + +void StorePacket (struct IPPacket *p); +int reordered (struct IPPacket *p); diff --git a/network_cmds-543/ecnprobe/inet.c b/network_cmds-543/ecnprobe/inet.c new file mode 100644 index 00000000..b145412d --- /dev/null +++ b/network_cmds-543/ecnprobe/inet.c @@ -0,0 +1,503 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI 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 ICSI 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 +#include +#include +#include +#include +#include +#include "base.h" +#include "inet.h" +#include "session.h" +#include "capture.h" +#include "support.h" +#include "history.h" + +extern struct TcpSession session; +extern struct History history[]; + +/* + * Deal with struct in_addr type agreement once and for all + */ +char *InetAddress(uint32 addr) +{ + + struct in_addr s; + s.s_addr = addr; + + //printf("In InetAddress:\n"); + //printf("addr = %s (%0x)\n", inet_ntoa(s), addr); + + return (inet_ntoa(s)); +} + +/* + * Really slow implementation of ip checksum + * ripped off from rfc1071 + */ + +uint16 InetChecksum(uint16 *ip, uint16 *tcp, uint16 ip_len, uint16 tcp_len) { + + uint32 sum = 0; + + uint32 ip_count = ip_len; + uint32 tcp_count = tcp_len; + uint16 *ip_addr = ip; + uint16 *tcp_addr = tcp; + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("In InetChecksum...\n"); + printf("iplen: %d, tcplen: %d\n", ip_len, tcp_len); + } + + + while(ip_count > 1) { + //printf("ip[%d]: %x\n", ip_len - ip_count, htons(*ip_addr)); + sum += *ip_addr++; + ip_count -= 2; + } + + while(tcp_count > 1) { + //printf("tcp[%d]: %x\n", tcp_len - tcp_count, htons(*tcp_addr)); + sum += *tcp_addr++; + tcp_count -= 2; + } + + if(ip_count > 0) { + sum += *(uint8 *)ip_addr; + } + + if(tcp_count > 0) { + sum += *(uint8 *)tcp_addr; + } + + while (sum >> 16) { + sum = (sum & 0xffff) + (sum >> 16); + } + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("Out InetChecksum...\n"); + } + + return(~sum); + +} + + +void WriteIPPacket(struct IPPacket *p, + uint32 src, + uint32 dst, + uint16 sport, + uint16 dport, + uint32 seq, + uint32 ack, + uint8 flags, + uint16 win, + uint16 urp, + uint16 datalen, + uint16 ip_optlen, + uint16 optlen, + uint8 iptos, + uint8 u4tf) +{ + + struct IpHeader *ip = p->ip; + struct TcpHeader *tcp = p->tcp; + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("In WriteIPPacket...\n"); + } + + /* Zero out IpHeader to ensure proper checksum computation */ + bzero((char *)(p->ip), sizeof(struct IpHeader)); + + ip->ip_src = src; + ip->ip_dst = dst; + ip->ip_p = IPPROTOCOL_TCP; + ip->ip_xsum = + htons((uint16)(sizeof(struct TcpHeader) + datalen + optlen)); /* pseudo hdr */ + + tcp->tcp_sport = htons(sport); + tcp->tcp_dport = htons(dport); + tcp->tcp_seq = htonl(seq); + tcp->tcp_ack = htonl(ack); + tcp->tcp_hl = (sizeof(struct TcpHeader) + optlen) << 2; + tcp->tcp_hl = tcp->tcp_hl | u4tf; + tcp->tcp_flags = flags; + + tcp->tcp_win = htons(win); + tcp->tcp_urp = htons(urp); + + tcp->tcp_xsum = 0; + tcp->tcp_xsum = InetChecksum((uint16 *)ip, (uint16 *)tcp, + (uint16)sizeof(struct IpHeader), /* IP Options should aren't included */ + (uint16)(sizeof(struct TcpHeader) + datalen + optlen)); + + /* Fill in real ip header */ + if (session.curr_ttl != 0) { + ip->ip_ttl = session.curr_ttl; + }else { + ip->ip_ttl = 60; + } + + //printf("TTL: %d\n", ip->ip_ttl); + + ip->ip_tos = iptos; + + /* IP Version and Header len field */ + ip->ip_vhl = 0x40 + 0x5 + (int)(ip_optlen/4); + ip->ip_p = IPPROTOCOL_TCP; + + ip->ip_off = IP_DF; + ip->ip_len = (uint16)(sizeof(struct IpHeader) + ip_optlen + sizeof(struct TcpHeader) + optlen + datalen); + + ip->ip_xsum = 0; + ip->ip_xsum = InetChecksum((uint16 *)ip, NULL, + (uint16)sizeof(struct IpHeader) + ip_optlen, /* IP Options should aren't included */ + 0); + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("Out WriteIPPacket...\n"); + } + +} + +void ReadIPPacket(struct IPPacket *p, + uint32 *src, + uint32 *dst, + uint16 *sport, + uint16 *dport, + uint32 *seq, + uint32 *ack, + uint8 *flags, + uint16 *win, + uint16 *urp, + uint16 *datalen, + uint16 *ip_optlen, + uint16 *optlen) +{ + + /* TODO: Add reading of IP options, if any */ + + struct IpHeader *ip = p->ip; + struct TcpHeader *tcp = p->tcp; + + uint16 ip_len; + uint16 ip_hl; + uint16 tcp_hl; + + /* XXX do checksum check? */ + if (ip->ip_p != IPPROTOCOL_TCP && ip->ip_p != IPPROTOCOL_ICMP) { + printf("Unexpected protocol packet: %u\n", ip->ip_p); + Quit(ERR_CHECKSUM); + } + + *src = ip->ip_src; + *dst = ip->ip_dst; + *sport = ntohs(tcp->tcp_sport); + *dport = ntohs(tcp->tcp_dport); + *seq = ntohl(tcp->tcp_seq); + *ack = ntohl(tcp->tcp_ack); + *flags = tcp->tcp_flags; + *win = ntohs(tcp->tcp_win); + *urp = ntohs(tcp->tcp_urp); + + tcp_hl = tcp->tcp_hl >> 2; + ip_len = ntohs(ip->ip_len); + ip_hl = (ip->ip_vhl & 0x0f) << 2; + *datalen = (ip_len - ip_hl) - tcp_hl; + *ip_optlen = ip_hl - (unsigned int)sizeof(struct IpHeader); /* added to support IP Options */ + *optlen = tcp_hl - (unsigned int)sizeof(struct TcpHeader); + +} + +void PrintICMPUnreachableErrorPacket(struct ICMPUnreachableErrorPacket *p) +{ + + struct IpHeader *ip = &p->ip; + struct IcmpHeader *icmp = &p->icmp; + struct IpHeader *off_ip = &p->off_ip; + + printf("IPHdr: "); + printf("%s > ", InetAddress(ip->ip_src)); + printf("%s ", InetAddress(ip->ip_dst)); + printf(" datalen: %u\n", ip->ip_len); + printf("ICMPHdr: "); + printf("Type: %u Code: %u MTU next hop: %u xsum: %x\n", + icmp->icmp_type, + icmp->icmp_code, + ntohs(icmp->icmp_mtu), + icmp->icmp_xsum); + printf("Off IPHdr: "); + printf("%s > ", InetAddress(off_ip->ip_src)); + printf("%s ", InetAddress(off_ip->ip_dst)); + printf(" datalen: %u ", off_ip->ip_len); + printf("tcp sport: %u ", ntohs(p->tcp_sport)); + printf("tcp dport: %u ", ntohs(p->tcp_dport)); + printf("tcp seqno: %u\n", (uint32)ntohl(p->tcp_seqno)); + +} + +void PrintTcpPacket(struct IPPacket *p) +{ + + struct IpHeader *ip = p->ip; + struct TcpHeader *tcp = p->tcp; + + char *opt; + int optlen; + char *ip_opt; + int ip_optlen; + int i; + + printf("%s.%u > ", InetAddress(ip->ip_src), ntohs(tcp->tcp_sport)); + printf("%s.%u ", InetAddress(ip->ip_dst), ntohs(tcp->tcp_dport)); + + if (tcp->tcp_flags & TCPFLAGS_SYN) { + printf("S"); + } + + if (tcp->tcp_flags & TCPFLAGS_ACK) { + printf("A"); + } + + if (tcp->tcp_flags & TCPFLAGS_FIN) { + printf("F"); + } + + if (tcp->tcp_flags & TCPFLAGS_ECN_ECHO) { + printf("E"); + } + + if (tcp->tcp_flags & TCPFLAGS_CWR) { + printf("W"); + } + + if (tcp->tcp_flags & TCPFLAGS_RST) { + printf("R"); + } + if (tcp->tcp_flags & TCPFLAGS_PSH) { + printf("P"); + } + + if (tcp->tcp_flags & TCPFLAGS_URG) { + printf("U"); + } + + if (INSESSION(p,session.src,session.sport,session.dst,session.dport)) { + printf(" seq: %u, ack: %u", (uint32)ntohl(tcp->tcp_seq) - session.iss, (uint32)ntohl(tcp->tcp_ack) - session.irs); + } else { + printf(" seq: %u, ack: %u", (uint32)ntohl(tcp->tcp_seq) - session.irs, (uint32)ntohl(tcp->tcp_ack) - session.iss); + } + + /* IP Options */ + ip_optlen = ((ip->ip_vhl & 0x0f) << 2) - sizeof(struct IpHeader); + ip_opt = (char *)ip + sizeof(struct IpHeader); + + i = 0; + while (i < ip_optlen) { + + switch ((unsigned char)ip_opt[i]) { + case IPOPT_NOP: + printf(" ipopt%d: %s ", i + 1, "IPOPT_NOP"); + i = i + 1; + break; + + case IPOPT_EOL: + printf(" ipopt%d: %s ", i + 1, "IPOPT_EOL"); + i = ip_optlen + 1; + break; + + case IPOPT_RR: + printf(" ipopt%d: %s ", i + 1, "IPOPT_RR"); + i = i + IPOLEN_RR; + break; + + default: + printf("ip_opt%d: UNKNOWN ", i + 1); + i = i + (uint8)ip_opt[i+1] ; + } + } + + printf(" win: %u, urg: %u, ttl: %d", ntohs(tcp->tcp_win), ntohs(tcp->tcp_urp), ip->ip_ttl); + printf(" datalen: %u, optlen: %u ", + ip->ip_len - ((ip->ip_vhl &0x0f) << 2) - (tcp->tcp_hl >> 2), + (tcp->tcp_hl >> 2) - (unsigned int)sizeof(struct TcpHeader)); + + + /* TCP Options */ + optlen = (tcp->tcp_hl >> 2) - (unsigned int)sizeof (struct TcpHeader) ; + opt = (char *)tcp + sizeof(struct TcpHeader); + + i = 0 ; + + while (i < optlen) { + + switch ((unsigned char)opt[i]) { + + case TCPOPT_EOL: + printf (" opt%d: %s ", i + 1, "TCPOPT_EOL"); + i = optlen + 1; + break ; + + case TCPOPT_NOP: + printf (" opt%d: %s ", i + 1, "TCPOPT_NOP"); + i++ ; + break ; + + case TCPOPT_MAXSEG: + printf (" opt%d: %s: %d ", i + 1, "TCPOPT_MAXSEG", ntohs(*(uint16 *)((char *)opt+2))); + i = i + TCPOLEN_MAXSEG ; + break ; + + case TCPOPT_WINDOW: + printf (" opt%d: %s ", i + 1, "TCPOPT_WINDOW"); + i = i + TCPOLEN_WINDOW ; + break ; + + case TCPOPT_SACK_PERMITTED: + printf (" opt%d: %s ", i + 1, "TCPOPT_SACK_PERMITTED"); + i = i + TCPOLEN_SACK_PERMITTED ; + break ; + + case TCPOPT_TIMESTAMP: + printf (" opt%d: %s ", i + 1, "TCPOPT_TIMESTAMP"); + i = i + TCPOLEN_TIMESTAMP ; + break ; + + default: + printf (" opt%d c:%d l:%d: UNKNOWN ", i + 1, (uint8)opt[i], (uint8)opt[i+1]); + if ((uint8)opt[i+1] > 0) { + i = i + (uint8)opt[i+1] ; + } else { + Quit(20); + } + break ; + } + } + printf ("\n"); +} + + +struct IPPacket *FindHeaderBoundaries(char *p) { + + struct IPPacket *packet; + uint16 ip_hl; + + if ((packet = (struct IPPacket *)calloc(1, sizeof(struct IPPacket))) == NULL) { + printf("FindHeaderBoundaries: Cannot allocate memory for read packet\nRETURN CODE: %d\n", ERR_MEM_ALLOC); + Quit(ERR_MEM_ALLOC); + } + + packet->ip = (struct IpHeader *)p; + + if (packet->ip->ip_p != IPPROTOCOL_TCP && + packet->ip->ip_p != IPPROTOCOL_ICMP) { + printf("Error: Unexpected protocol packet: %u \n", packet->ip->ip_p); + Quit(ERR_CHECKSUM); + } + + ip_hl = (packet->ip->ip_vhl & 0x0f) << 2; + + packet->tcp = (struct TcpHeader *)((char *)p + ip_hl); + return packet; + +} + + +struct IPPacket * +AllocateIPPacket(int ip_optlen, int tcp_optlen, int datalen, char *str) +{ + struct IPPacket *p; + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("In AllocateIPPacket: %s...\n", str); + } + + if ((p = (struct IPPacket *)calloc(1, sizeof(struct IPPacket))) + == NULL) { + printf("%s ERROR: No space for packet\nRETURN CODE: %d", + str, ERR_MEM_ALLOC); + Quit(ERR_MEM_ALLOC); + } + + if ((p->ip = (struct IpHeader *)calloc(1, + sizeof(struct IpHeader) + ip_optlen)) == NULL) { + printf("%s ERROR: No IpHeader space for packet\n" + "RETURN CODE: %d", str, ERR_MEM_ALLOC); + Quit(ERR_MEM_ALLOC); + } + + if ((p->tcp = (struct TcpHeader *)calloc(1, + sizeof(struct TcpHeader) + tcp_optlen + datalen)) == NULL) { + printf("%s ERROR: No TcpHeader space for packet\n" + "RETURN CODE: %d", str, ERR_MEM_ALLOC); + Quit(ERR_MEM_ALLOC); + } + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("Out of AllocateIPPacket: %s...\n", str); + } + return(p); +} + +void +FreeIPPacket(struct IPPacket **pkt_p) +{ + struct IPPacket *pkt; + if (pkt_p == NULL) + return; + if ((pkt = *pkt_p) == NULL) + return; + if (pkt->ip != NULL) { + free(pkt->ip); + pkt->ip = NULL; + } + if (pkt->tcp != NULL) { + free(pkt->tcp); + pkt->tcp = NULL; + } + free(pkt); + *pkt_p = NULL; +} + diff --git a/network_cmds-543/ecnprobe/inet.h b/network_cmds-543/ecnprobe/inet.h new file mode 100644 index 00000000..8d1ccff8 --- /dev/null +++ b/network_cmds-543/ecnprobe/inet.h @@ -0,0 +1,206 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI 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 ICSI 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 _INET_H_ +#define _INET_H_ + +/* XXX These are machine/compiler dependent */ +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; + +#define IPPROTOCOL_ICMP 1 +#define IPPROTOCOL_IGMP 2 +#define IPPROTOCOL_TCP 6 +#define IPPROTOCOL_UDP 17 +#define IP_DF 0x4000 + +/* ICMP type */ +#define ICMP_TIMXCEED 11 + +/* TCP Flags */ +#define TCPFLAGS_FIN 0x01 +#define TCPFLAGS_SYN 0x02 +#define TCPFLAGS_RST 0x04 +#define TCPFLAGS_PSH 0x08 +#define TCPFLAGS_ACK 0x10 +#define TCPFLAGS_URG 0x20 +#define TCPFLAGS_ECN_ECHO 0x40 +#define TCPFLAGS_CWR 0x80 + +/* IP Options Parameters -- for IP Options te*/ +#define IPOPT_EOL 0x0 +#define IPOLEN_EOL 0x1 +#define IPOPT_NOP 0x1 +#define IPOLEN_NOP 0x1 +#define IPOPT_RR 0x7 +#define IPOLEN_RR 0x27 /* Maximum length; up to 9 IP addresses */ +#define IPOPT_TS 0x44 +#define IPOLEN_TS 0x28 +#define IPOPT_FAKED 0xff +#define IPOLEN_FAKED 0x4 + +/* TCP Options Parameters */ +#define TCPOPT_EOL 0 +#define TCPOLEN_EOL 1 +#define TCPOPT_NOP 1 +#define TCPOLEN_NOP 1 +#define TCPOPT_MAXSEG 2 +#define TCPOLEN_MAXSEG 4 +#define TCPOPT_WINDOW 3 +#define TCPOLEN_WINDOW 3 +#define TCPOPT_SACK_PERMITTED 4 +#define TCPOLEN_SACK_PERMITTED 2 +#define TCPOPT_SACK 5 +#define TCPOPT_TIMESTAMP 8 +#define TCPOLEN_TIMESTAMP 10 +#define TCPOPT_FAKED 0x19 +#define TCPOLEN_FAKED 0x4 + +struct IpHeader { + uint8 ip_vhl; /* version (4bits) & header length (4 bits) */ + uint8 ip_tos; /* type of service */ + uint16 ip_len; /* length of IP datagram */ + uint16 ip_id; /* identification (for frags) */ + uint16 ip_off; /* offset (within a fragment) and flags (3 bits) */ + uint8 ip_ttl; /* time to live */ + uint8 ip_p; /* protocol number */ + uint16 ip_xsum; /* checksum */ + uint32 ip_src; /* source address */ + uint32 ip_dst; /* destination address */ +}; + +/* Pseudo header for doing TCP checksum calculation */ +struct PseudoIpHeader { + uint32 filler[2]; + uint8 zero; + uint8 ip_p; + uint16 ip_len; + uint32 ip_src; + uint32 ip_dst; +}; + +struct TcpHeader { + uint16 tcp_sport; /* source port */ + uint16 tcp_dport; /* destination port */ + uint32 tcp_seq; /* sequence number */ + uint32 tcp_ack; /* acknoledgement number */ + uint8 tcp_hl; /* header length (4 bits) */ + uint8 tcp_flags; /* flags */ + uint16 tcp_win; /* advertized window size */ + uint16 tcp_xsum; /* checksum */ + uint16 tcp_urp; /* urgent pointer */ +}; + + + +struct IcmpHeader { + uint8 icmp_type; /* ICMP message type */ + uint8 icmp_code; /* Message code */ + uint16 icmp_xsum; /* checksum */ + uint16 icmp_unused; /* unused field */ + uint16 icmp_mtu; /* MTU of limiting interface */ +}; + +struct IPPacket { + struct IpHeader *ip; + struct TcpHeader *tcp; +}; + +struct ICMPUnreachableErrorPacket { + struct IpHeader ip; + struct IcmpHeader icmp; + struct IpHeader off_ip; + /* 8-first bytes of TCP header */ + uint16 tcp_sport; + uint16 tcp_dport; + uint32 tcp_seqno; +}; + +struct ICMPTimeExceededErrorPacket { + struct IpHeader ip; + struct IcmpHeader icmp; + struct IpHeader off_ip; + /* 8-first bytes of Tcpheader */ + uint16 tcp_sport; + uint16 tcp_dport; + uint32 tcp_seqno; +}; + +char *InetAddress(uint32 addr); + +uint16 InetChecksum(uint16 *ip_addr, uint16 *tcp_addr, uint16 ip_len, uint16 tcp_len); + +void PrintTcpPacket(struct IPPacket *p); +void PrintICMPUnreachableErrorPacket(struct ICMPUnreachableErrorPacket *p); + +void WriteIPPacket(struct IPPacket *p, + uint32 src, + uint32 dst, + uint16 sport, + uint16 dport, + uint32 seq, + uint32 ack, + uint8 flags, + uint16 win, + uint16 urp, + uint16 datalen, + uint16 ip_optlen, + uint16 optlen, + uint8 iptos, + uint8 u4tf); + +void ReadIPPacket(struct IPPacket *p, uint32 *src, uint32 *dst, + uint16 *sport, uint16 *dport, uint32 *seq, uint32 *ack, + uint8 *flags, uint16 *win, uint16 *urp, uint16 *datalen, + uint16 *ip_optlen, uint16 *optlen); + +void StorePacket (struct IPPacket *p); + +struct IPPacket *FindHeaderBoundaries(char *p); + +struct IPPacket *AllocateIPPacket(int ip_optlen, int tcp_optlen, int datalen, char *str); + +void FreeIPPacket(struct IPPacket **pkt_p); + +#endif /* _INET_H_ */ diff --git a/network_cmds-543/ecnprobe/session.c b/network_cmds-543/ecnprobe/session.c new file mode 100644 index 00000000..5ca97d85 --- /dev/null +++ b/network_cmds-543/ecnprobe/session.c @@ -0,0 +1,785 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI 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 ICSI 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 +#include +#include +#include +#include +#include +#include +#include "base.h" +#include "inet.h" +#include "session.h" +#include "capture.h" +#include "support.h" +#include "ecn.h" +#include + +struct TcpSession session; + +int EstablishSession(uint32 sourceAddress, + uint16 sourcePort, + uint32 targetAddress, + uint16 targetPort, + int ip_optlen, // AM: add support for IP options + char *ip_opt, // AM: add support for IP options + int mss, + int optlen, + char *opt, + int maxwin, + int maxpkts, + uint8 iptos, + uint8 tcp_flags) // AM: Add a tcp_flags parameter +{ + + int rawSocket; + + struct IPPacket *p = NULL; + struct IPPacket *synPacket; + char *read_packet; + struct pcap_pkthdr pi; + int synAckReceived = 0; + int numRetransmits = 0; + double timeoutTime; + double ts1 = 0, ts2; + int flag = 1; + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("In EstablishSession...\n"); + } + + arc4random_stir(); + + session.src = sourceAddress; + session.sport = sourcePort; + session.dst = targetAddress; + session.dport = targetPort; + session.rcv_wnd = maxwin * mss; + session.snd_nxt = arc4random(); /* random initial sequence number */ + session.iss = session.snd_nxt; + session.rcv_nxt = 0; + session.irs = 0; + session.mss = mss ; + session.maxseqseen = 0 ; + session.epochTime = GetTime(); + session.maxpkts = maxpkts; + session.num_unwanted_drops = 0; + session.num_reordered = 0; + session.num_rtos = 0; + session.num_dup_acks = 0; + session.num_pkts_0_dup_acks = 0; + session.num_pkts_1_dup_acks = 0; + session.num_pkts_2_dup_acks = 0; + session.num_pkts_3_dup_acks = 0; + session.num_pkts_4_or_more_dup_acks = 0; + session.num_dupack_ret = 0; + session.num_reord_ret = 0; + session.num_reordered = 0; + session.num_dup_transmissions = 0; + session.ignore_result = 0; + session.curr_ttl = 0; + + if ((session.mtu < 1) || (session.mtu > 1460)) { + session.mtu = 1500; + } + + if (session.verbose) { + printf("session.MTU = %d\n", session.mtu); + } + + if ((session.dataRcvd = (uint8 *)calloc(sizeof(uint8), mss * session.maxpkts)) == NULL) { + perror("ERROR: no memmory to store data:\n"); + printf("RETURN CODE: %d\n", ERR_MEM_ALLOC); + Quit(ERR_MEM_ALLOC); + } + + /* Now open a raw socket for sending our "fake" TCP segments */ + if ((rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { + perror("ERROR: couldn't open socket:"); + printf("RETURN CODE: %d\n", ERR_SOCKET_OPEN); + Quit(ERR_SOCKET_OPEN); + } + + if (setsockopt(rawSocket, IPPROTO_IP, IP_HDRINCL, (char *)&flag,sizeof(flag)) < 0) { + perror("ERROR: couldn't set raw socket options:"); + printf("RETURN CODE: %d\n", ERR_SOCKOPT); + Quit(ERR_SOCKOPT); + } + + session.socket = rawSocket; + + /* Allocate SYN packet */ + synPacket = AllocateIPPacket(ip_optlen, optlen, 0, "EstablishSession (SYN)"); + + /* Copy IP options at the end of IpHeader structure - New */ + if (ip_optlen > 0) { + memcpy((char *)synPacket->ip + sizeof(struct IpHeader), ip_opt, ip_optlen); + } + + /* Copy TCP options at the end of TcpHeader structure - New */ + if (optlen > 0) { + memcpy((char *)synPacket->tcp + sizeof(struct TcpHeader), opt, optlen); + } + + /* Send SYN Pkt */ + SendSessionPacket(synPacket, + sizeof(struct IpHeader) + ip_optlen + sizeof(struct TcpHeader) + optlen, + TCPFLAGS_SYN | tcp_flags, + ip_optlen, /* IP opt len */ + optlen, /* TCP opt len */ + iptos); + + timeoutTime = GetTime() + SYNTIMEOUT; + + /* + * Wait for SYN/ACK and retransmit SYN if appropriate + * not great, but it gets the job done + */ + + while(!synAckReceived && numRetransmits < MAXSYNRETRANSMITS) { + + while(GetTime() < timeoutTime) { + + /* Have we captured any packets? */ + + if ((read_packet = (char *)CaptureGetPacket(&pi)) != NULL) { + + p = (struct IPPacket *)FindHeaderBoundaries(read_packet); + + /* Received a packet from us to them */ + if (INSESSION(p, session.src, session.sport, session.dst, session.dport)) { + + /* Is it a SYN? */ + if (p->tcp->tcp_flags & TCPFLAGS_SYN) { + + if (session.debug >= SESSION_DEBUG_LOW) { + printf("xmit\n"); + PrintTcpPacket(p); + } + + StorePacket(p); + + ts1 = pi.ts.tv_sec + (double)pi.ts.tv_usec/1000000.0; + session.totSeenSent ++ ; + + } + + free(p); + continue; + + + } + + if (INSESSION(p, session.dst, session.dport, session.src, session.sport)) { + + /* Is it a SYN/ACK? */ + if (p->tcp->tcp_flags & (TCPFLAGS_SYN | TCPFLAGS_ACK)) { + + timeoutTime = GetTime(); /* force exit */ + synAckReceived++; + ts2 = pi.ts.tv_sec + (double)pi.ts.tv_usec/1000000.0; + session.rtt = ts2 - ts1 ; + + if (numRetransmits > 0) { + session.rtt_unreliable = 1; + printf("##### Unreliable\n"); /* ACK for which SYN? */ + } + + if (session.debug >= SESSION_DEBUG_LOW) { + printf("rcvd:\n"); + PrintTcpPacket(p); + printf("Connection setup took %d ms\n",(int)((ts2 - ts1) * 1000.0)); + } + + StorePacket(p); + + /* Save ttl for,admittedly poor,indications of reverse route change */ + session.ttl = p->ip->ip_ttl; + session.snd_wnd = ntohl(p->tcp->tcp_win); + session.totRcvd++; + + free(p); + break ; + + } + + } + + free(p->ip); + free(p->tcp); + free(p); + + } + + } + + if (!synAckReceived) { + + if (session.debug >= SESSION_DEBUG_LOW) { + printf("SYN timeout. Retransmitting\n"); + } + + SendSessionPacket(synPacket, + sizeof(struct IpHeader) + ip_optlen + sizeof(struct TcpHeader) + optlen, + TCPFLAGS_SYN | tcp_flags, + ip_optlen, /* IP opt len */ + optlen, /* TCP opt len */ + iptos); + + timeoutTime = GetTime() + SYNTIMEOUT; + numRetransmits++; + } + } + + if (numRetransmits >= MAXSYNRETRANSMITS) { + printf("ERROR: Could not establish contact after %d retries\nRETURN CODE: %d\n", + numRetransmits, NO_CONNECTION); + Quit(NO_CONNECTION); + } + + /* Update session variables */ + session.irs = ntohl(p->tcp->tcp_seq); + session.dataRcvd[0] = 1 ; + session.rcv_nxt = session.irs + 1; /* SYN/ACK takes up a byte of seq space */ + session.snd_nxt = session.iss + 1; /* SYN takes up a byte of seq space */ + session.snd_una = session.iss + 1; + session.maxseqseen = ntohl(p->tcp->tcp_seq); + + session.initSession = 1; + if (session.debug >= SESSION_DEBUG_LOW) { + printf("src = %s:%d (%u)\n", InetAddress(session.src), session.sport, session.iss); + printf("dst = %s:%d (%u)\n", InetAddress(session.dst), session.dport, session.irs); + } + + free(synPacket->ip); + free(synPacket->tcp); + free(synPacket); + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("Out of EstablishSession...\n"); + } + + session.start_time = GetTime(); + + return 1; + +} + +int PrepareRequest(char *data, char *filename) +{ + + char h1[] = "GET "; + char h2[] = " HTTP/1.1"; + char h3[] = "Host: "; + char h4[] = "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11; DigExt; TBIT)"; + char h5[] = "Accept: */*"; + + /* New */ + char h7[] = "Pragma: no-cache"; + char h8[] = "Cache-control: no-chache"; + char deffile[] = DEFAULT_FILENAME; + + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("In PrepareRequest...\n"); + } + + if (filename == NULL) { + filename = deffile; + } + + + if (strlen(session.targetName) > 0) { + + sprintf(data, + + "%s/%s %s\r\n%s\r\n%s\r\n%s\r\n%s\r\n%s%s\r\n\r\n", + h1, + filename, + h2, + h4, + h7, + h8, + h5, + h3, + session.targetName); + }else { + + sprintf(data, + "%s%s%s\r\n%s\r\n\r\n", + h1, + filename, + h2, + h4); + } + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("Out PrepareRequest...\n"); + } + + return ((int)strlen(data)); + +} + + +void SendRequest(char *filename, void (*ackData)(struct IPPacket *p)) +{ + + struct IPPacket *p, *datapkt; + struct pcap_pkthdr pi; + char *read_packet; + int i; + int sendflag = 1; + double startTime = 0; + char *dataptr; + char data[MAXREQUESTLEN]; + int datalen; + int ipsz; + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("In SendRequest...\n"); + } + + datalen = PrepareRequest(data, filename); + + ipsz = sizeof(struct IpHeader) + sizeof(struct TcpHeader) + datalen + 1; + + /* Allocate space for IP data packet */ + datapkt = AllocateIPPacket(0, 0, datalen + 1, "SendRequest (Data)"); + + dataptr = (char *)datapkt->tcp + sizeof(struct TcpHeader); + memcpy((void *)dataptr, (void *)data, datalen); + + /* Send the data packet. Try to "achieve" reliability by sending the + * packet upto 5 times, wating for 2 seconds between packets (BAD + * busy-wait loop) + */ + + i = 0 ; + while(1) { + + if (sendflag == 1) { + + SendSessionPacket(datapkt, + ipsz, + TCPFLAGS_PSH | TCPFLAGS_ACK, + 0, /* ip opt len */ + 0, /* tcp opt len */ + 0); /* tos */ + + startTime = GetTime(); + sendflag = 0 ; + i++; + + } + + /* Check if we have received any packets */ + if ((read_packet = (char *)CaptureGetPacket(&pi)) != NULL) { + + p = (struct IPPacket *)FindHeaderBoundaries(read_packet); + + /* + * packet that we sent? + */ + + if (INSESSION(p,session.src,session.sport,session.dst,session.dport) && + (p->tcp->tcp_flags == (TCPFLAGS_PSH | TCPFLAGS_ACK)) && + (ntohl(p->tcp->tcp_seq) == session.snd_nxt) && + (ntohl(p->tcp->tcp_ack) <= session.rcv_nxt)) { + + if (session.debug >= SESSION_DEBUG_LOW) { + printf("xmit %d\n", i); + PrintTcpPacket(p); + } + + StorePacket(p); + + free(p); + + //session.snd_nxt += datalen + 1; + session.totSeenSent++; + continue; + + } + /* + * packet from them? + */ + + if (INSESSION(p,session.dst,session.dport,session.src,session.sport) && + (p->tcp->tcp_flags & TCPFLAGS_ACK) && + (ntohl(p->tcp->tcp_seq) == session.rcv_nxt) && + (ntohl(p->tcp->tcp_ack) >= session.snd_una)) { + + + session.snd_una = ntohl(p->tcp->tcp_ack); + + if (p->ip->ip_ttl != session.ttl) { + printf("#### WARNING: route may have changed (ttl was %d, is %d).\n", + session.ttl, p->ip->ip_ttl); + session.ttl = p->ip->ip_ttl; + } + + if (session.debug >= SESSION_DEBUG_LOW) { + printf("rcvd %d\n", i); + PrintTcpPacket(p); + } + + StorePacket(p); + session.totRcvd ++; + session.snd_nxt += datalen + 1; + + /* if the packet also contains data, receive it and send an ack if needed */ + (*ackData)(p); + + free(p); + break; + + } + + free(p); + + } + + if ((GetTime() - startTime >= REXMITDELAY) && + (sendflag == 0) && (i < MAXDATARETRANSMITS)) { + sendflag = 1 ; + } + + if (i >= MAXDATARETRANSMITS) { + printf ("ERROR: sent request 5 times without response\nRETURN CODE: %d\n", + SEND_REQUEST_FAILED); + Quit(SEND_REQUEST_FAILED); + } + + } + + free(datapkt->ip); + free(datapkt->tcp); + free(datapkt); + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("Out of SendRequest...\n"); + } +} + +void SendSessionPacket(struct IPPacket *p, + uint16 ip_len, uint8 tcp_flags, uint16 ip_optlen, uint16 optlen, + uint8 iptos) +{ + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("In SendSessionPacket...\n"); + } + WriteIPPacket(p, + session.src, session.dst, session.sport, session.dport, + session.snd_nxt, session.rcv_nxt, tcp_flags, + session.rcv_wnd, 0, + (ip_len - sizeof(struct IpHeader) - ip_optlen - sizeof(struct TcpHeader) - optlen), + ip_optlen, optlen, iptos, 0); + + + /* Store packet here rather than in rcvData() because otherwise some + * ACKs may not be accounted for upon receiving reordered packets */ + + StorePacket(p); + + SendPkt(p, + ip_len, /* Total IP datagram size */ + ip_optlen, /* ip options len */ + optlen); /* tcp options len */ + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("Out of SendSessionPacket...\n"); + } + +} + + +void SendICMPReply(struct IPPacket *p) +{ + + struct ICMPUnreachableErrorPacket *icmp_pkt; + int icmpsz; + + struct IpHeader *ip = p->ip; + struct TcpHeader *tcp = p->tcp; + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("In SendICMPReply...\n"); + } + + icmpsz = sizeof(struct ICMPUnreachableErrorPacket); + if ((icmp_pkt = (struct ICMPUnreachableErrorPacket *)calloc(icmpsz + 1, 1)) == NULL) { + perror("ERROR: no space for ICMP packet:"); + Quit(ERR_MEM_ALLOC) ; + } + + /* Fill IP Header of ICMP packet */ + bzero((char *)icmp_pkt, sizeof(struct ICMPUnreachableErrorPacket)); + icmp_pkt->ip.ip_src = ip->ip_dst; + icmp_pkt->ip.ip_dst = ip->ip_src; + icmp_pkt->ip.ip_p = IPPROTOCOL_ICMP; + icmp_pkt->ip.ip_xsum = + htons((uint16)(sizeof(struct IcmpHeader) + sizeof(struct IpHeader) + sizeof(struct IpHeader) + 8)); /* pseudo hdr */ + icmp_pkt->ip.ip_ttl = 60; + icmp_pkt->ip.ip_tos = 0x00; + icmp_pkt->ip.ip_vhl = 0x45; +#ifdef __FreeBSD__ + icmp_pkt->ip.ip_off = IP_DF; + icmp_pkt->ip.ip_len = (uint16)(sizeof(struct ICMPUnreachableErrorPacket)); +#else /* __FreeBSD__ */ + icmp_pkt->ip.ip_off = htons(IP_DF); + icmp_pkt->ip.ip_len = htons((uint16)((sizeof (struct ICMPUnreachableErrorPacket) + 8 + 1))); +#endif /* __FreeBSD__ */ + + /* Fill ICMP header */ + icmp_pkt->icmp.icmp_type = 0x3; + icmp_pkt->icmp.icmp_code = 0x4; + icmp_pkt->icmp.icmp_xsum = 0; + icmp_pkt->icmp.icmp_unused = 0; + icmp_pkt->icmp.icmp_mtu = htons(session.mtu); + + /* Fill in ip header of offending packet */ + icmp_pkt->off_ip.ip_src = ip->ip_src; + icmp_pkt->off_ip.ip_dst = ip->ip_dst; + icmp_pkt->off_ip.ip_p = ip->ip_p; + icmp_pkt->off_ip.ip_xsum = ip->ip_xsum; + icmp_pkt->off_ip.ip_ttl = ip->ip_ttl; + icmp_pkt->off_ip.ip_tos = ip->ip_tos; + icmp_pkt->off_ip.ip_vhl = ip->ip_vhl; + icmp_pkt->off_ip.ip_p = ip->ip_p; +#ifdef __FreeBSD__ + icmp_pkt->off_ip.ip_off = ntohs(ip->ip_off); + icmp_pkt->off_ip.ip_len = ntohs(ip->ip_len); +#else /* __FreeBSD__ */ + icmp_pkt->off_ip.ip_off = ip->ip_off; + icmp_pkt->off_ip.ip_len = ip->ip_len; +#endif /* __FreeBSD__ */ + + icmp_pkt->tcp_sport = tcp->tcp_sport; + icmp_pkt->tcp_dport = tcp->tcp_dport; + icmp_pkt->tcp_seqno = (uint32)tcp->tcp_seq; + + icmp_pkt->icmp.icmp_xsum = InetChecksum((uint16 *)(&(icmp_pkt->icmp)), NULL, + (uint16)(sizeof(struct IcmpHeader) + sizeof(struct IpHeader) + 8), 0); + + if (session.verbose) { + printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); + printf("TCP Packet: %lu\n", sizeof(struct IPPacket)); + PrintTcpPacket(p); + printf("ICMP Packet: %lu\n", sizeof(struct ICMPUnreachableErrorPacket)); + PrintICMPUnreachableErrorPacket(icmp_pkt); + printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); + } + + SendICMPPkt(icmp_pkt, sizeof(struct ICMPUnreachableErrorPacket)); + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("Out of SendICMPReply...\n"); + } + +} + +void SendPkt(struct IPPacket *p, uint16 ip_len, int ip_optlen, + int tcp_optlen) { + int nbytes, datalen; + struct sockaddr_in sockAddr; + char *assembled_pkt; + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("In SendPkt...\n"); + } + /* Assemble contiguos packet to be sent */ + if ((assembled_pkt = (char *)calloc(1, ip_len)) == NULL) { + printf("SendPkt: Cannot allocate memory for assembled packet\n"); + Quit(ERR_MEM_ALLOC); + } + /* Copy IP Header and options, if any */ + memcpy((char *)assembled_pkt, (char *)(p->ip), + sizeof(struct IpHeader) + ip_optlen); + + /* Copy TCP Header and options, if any */ + memcpy((char *)(assembled_pkt + sizeof(struct IpHeader) + ip_optlen), + (char *)(p->tcp), + sizeof(struct TcpHeader) + tcp_optlen); + + /* Copy data bytes, if any */ + datalen = ip_len - ((sizeof(struct IpHeader) + ip_optlen + sizeof(struct TcpHeader) + tcp_optlen)); + + if (datalen > 0) { + memcpy((char *)assembled_pkt + sizeof(struct IpHeader) + ip_optlen + sizeof(struct TcpHeader) + tcp_optlen, + (char *)p->tcp + sizeof(struct TcpHeader) + tcp_optlen, datalen); + } + + + sockAddr.sin_family = AF_INET; + sockAddr.sin_addr.s_addr = session.dst; + + if ((nbytes = (int)sendto(session.socket, + (char *)assembled_pkt, + ip_len, + 0, + (struct sockaddr *)&sockAddr, + sizeof(sockAddr))) < ip_len) { + printf("#### WARNING: only sent %d of %d bytes\n", nbytes, ip_len); + perror("here"); + + } + + session.totSent++; + + free(assembled_pkt); + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("Out SendPkt...\n"); + } + +} + + + +void SendICMPPkt(struct ICMPUnreachableErrorPacket *p, uint16 len) { + + ssize_t nbytes; + struct sockaddr_in sockAddr; + + sockAddr.sin_family = AF_INET; + sockAddr.sin_addr.s_addr = session.dst; + + nbytes = sendto(session.socket, (char *)p, len, 0, + (struct sockaddr *)&sockAddr, + sizeof(sockAddr)); + + if (nbytes < len) { + printf("#### WARNING: only sent %zd of %d (errno: %d) bytes\n", + nbytes, len, errno); + perror("here"); + } + + session.totSent++ ; + +} + +void rcvData (void (*ackData)(struct IPPacket *p)) +{ + + struct pcap_pkthdr pi; + struct IPPacket *p; + char *read_packet; + double startTime = GetTime () ; + + if (session.debug >= SESSION_DEBUG_HIGH) { + printf("In rcvData...\n"); + } + + while (1) { + + if ((GetTime() - startTime) > (MAXDATARETRANSMITS * REXMITDELAY)) { + printf ("ERROR: no Data received for %f seconds\nRETURN CODE: %d\n", + (MAXDATARETRANSMITS*REXMITDELAY), NO_DATA_RCVD); + Quit(NO_DATA_RCVD) ; + } + + if ((read_packet = (char *)CaptureGetPacket(&pi)) != NULL) { + + p = (struct IPPacket *)FindHeaderBoundaries(read_packet); + + /* + * Packet that we sent? + */ + + if (INSESSION(p,session.src,session.sport,session.dst,session.dport) && + ((p->tcp->tcp_flags & TCPFLAGS_ACK) || (p->tcp->tcp_flags & TCPFLAGS_FIN)) && + (ntohl(p->tcp->tcp_seq) == session.snd_nxt) && + (ntohl(p->tcp->tcp_ack) <= session.rcv_nxt)) { + + if (session.debug >= SESSION_DEBUG_LOW) { + printf("xmit:\n"); + PrintTcpPacket(p); + } + + session.totSeenSent++ ; + + free(p); + continue; + + } + + /* + * Data that we were expecting? + */ + + if (INSESSION(p,session.dst,session.dport,session.src,session.sport) && + (p->tcp->tcp_flags & TCPFLAGS_ACK) && + (ntohl(p->tcp->tcp_ack) >= session.snd_una)) { + + if (p->ip->ip_ttl != session.ttl) { + printf("#### WARNING: route may have changed (ttl was %d, is %d).\n", + session.ttl, p->ip->ip_ttl); + session.ttl = p->ip->ip_ttl; + } + + if (session.debug >= SESSION_DEBUG_LOW) { + printf("rcvd: \n"); + PrintTcpPacket(p); + } + + session.totRcvd++ ; + startTime = GetTime () ; + StorePacket(p); + + /* if the packet also contains data, receive it, and send an ack if needed */ + ECNAckData(p); + + free(p); + continue ; + + } else { + + free(p); + + } + } + } +} + + diff --git a/network_cmds-543/ecnprobe/session.h b/network_cmds-543/ecnprobe/session.h new file mode 100644 index 00000000..bff32967 --- /dev/null +++ b/network_cmds-543/ecnprobe/session.h @@ -0,0 +1,183 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI 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 ICSI 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. +*/ + +#define MAXREQUESTLEN 1000 + +#define SESSION_DEBUG_LOW 1 +#define SESSION_DEBUG_MEDIUM 2 +#define SESSION_DEBUG_HIGH 3 + +struct TcpSession { + + /* target name, as specified by the user */ + char targetName[MAXHOSTNAMELEN]; + + /* DNS name of hosts */ + char targetHostName[MAXHOSTNAMELEN]; + char sourceHostName[MAXHOSTNAMELEN]; + + /* raw socket we use to send on */ + int socket; + + /* connection endpoint identifiers */ + u_int32_t src; + u_int16_t sport; + u_int32_t dst; + u_int16_t dport; + + /* sender info, from RFC 793 */ + u_int32_t iss; // initial send sequence + u_int32_t snd_una; // sequence numbers of unacknowledged data + u_int32_t snd_nxt; // sequence number to be sent next + u_int16_t snd_wnd; + u_int16_t sndmss; + + /* Receiver info */ + u_int32_t irs; + u_int32_t rcv_wnd; + u_int32_t rcv_nxt; + u_int32_t maxseqseen; + u_int16_t mss; + + /* timing */ + double rtt; + u_int8_t ttl; + double start_time; + + /* data buffer */ + u_int8_t *dataRcvd ; + + /* basic results */ + int totSent; + int totRcvd; + int totSeenSent; + int totDataPktsRcvd; + int totOutofSeq; + int hsz; + + /* basic control*/ + int epochTime; + int debug; + int verbose; + int initSession; + int initCapture; + int initFirewall; + int firewall_rule_number; + char *filename; + int maxpkts; + + /* New loss-rate parameters */ + float loss_rate; + float prop_delay; + + /* results are suspect for various reasons */ + int rtt_unreliable; + int ignore_result; + + /* Drops and reordering startistics */ + int num_reordered; + int num_unwanted_drops; + int num_rtos; + int num_reord_ret; + int num_dup_transmissions; + int num_dup_acks; + int num_pkts_0_dup_acks; + int num_pkts_1_dup_acks; + int num_pkts_2_dup_acks; + int num_pkts_3_dup_acks; + int num_pkts_4_or_more_dup_acks; + int num_dupack_ret; + + /* For PMTUD test */ + int mtu; + + /* For ByteCounting test */ + int bytecounting_type; + int ack_bytes; /* How many bytes covered per ACK */ + int ack_rate; /* ACK [every | every other | every third |...] packet */ + + /* For WindowScale Option test */ + u_int8_t receiving_shift_count; + u_int8_t sending_shift_count; + + /* For MidBoxTTL test */ + int curr_ttl; + + int dont_send_reset; +}; + +//void SendSessionPacket(struct IPPacket *packet, +void SendSessionPacket(struct IPPacket *packet, + u_int16_t ip_len, /* Total size of IP datagram */ + u_int8_t tcp_flags, + u_int16_t ip_optlen, /* IP options len - New */ + u_int16_t optlen, /* TCP options len */ + u_int8_t iptos); + +void SendICMPReply(struct IPPacket *p); + +void SendPkt(struct IPPacket *p, u_int16_t ip_len, int ip_optlen, int tcp_optlen); + +void SendICMPPkt(struct ICMPUnreachableErrorPacket *p, u_int16_t ip_len); + +void StorePacket (struct IPPacket *p); + +int EstablishSession(u_int32_t sourceAddress, \ + u_int16_t sourcePort, \ + u_int32_t targetAddress, + u_int16_t targetPort, \ + int ip_optlen,\ + char *ip_opt,\ + int mss, + int optlen, + char *opt, \ + int maxwin, + int maxpkts, + u_int8_t iptos, + u_int8_t tcp_flags); + +void rcvData (void (*ackData)(struct IPPacket *p)); + +void SendRequest(char *filename, void (*ackData)(struct IPPacket *p)); + +int PrepareRequest(char *data, char *filename) ; diff --git a/network_cmds-543/ecnprobe/support.c b/network_cmds-543/ecnprobe/support.c new file mode 100644 index 00000000..2cdb4057 --- /dev/null +++ b/network_cmds-543/ecnprobe/support.c @@ -0,0 +1,246 @@ +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI 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 ICSI 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 +#include +#include +#include +#include +#include +#include "base.h" +#include "inet.h" +#include "session.h" +#include "capture.h" +#include "support.h" + +extern struct TcpSession session; + +void SendReset() +{ + struct IPPacket *p; + int i; + + if (session.dont_send_reset) + return; + + if ((p = (struct IPPacket *)calloc(1, sizeof(struct IPPacket))) == NULL) { + perror("ERROR: Could not allocate RST packet:") ; + Quit(ERR_MEM_ALLOC) ; + } + + if ((p->ip = (struct IpHeader *)calloc(1, sizeof(struct IpHeader))) == NULL) { + perror("ERROR: Could not allocate IP Header for RST packet:") ; + Quit(ERR_MEM_ALLOC) ; + } + + if ((p->tcp = (struct TcpHeader *)calloc(1, sizeof(struct TcpHeader))) == NULL) { + perror("ERROR: Could not allocate TCP Header for RST packet:") ; + Quit(ERR_MEM_ALLOC) ; + } + + for (i = 0; i < MAXRESETRETRANSMITS; i++) { + SendSessionPacket(p, + //sizeof(struct IPPacket), + sizeof(struct IpHeader) + sizeof(struct TcpHeader), + TCPFLAGS_RST, + 0, + 0, + 0); + } + +/* free(p->ip); + free(p->tcp); + free(p); +*/ + +} + +#if 0 +/* make a clean exit on interrupts */ +void SigHandle (int signo) +{ + Cleanup () ; + fflush(stdout); + fflush(stderr); + exit(-1); +} + + +void Cleanup() +{ + + char ipfw_rule[100]; + int r; + + /* If a firewall rule has been installed then remove it */ + if (session.initFirewall > 0) { + +#ifdef linux +#define IP_FW_DEL (IP_FW_DELETE) +#endif /* linux */ + + sprintf(ipfw_rule, "ipfw del 00%d", session.firewall_rule_number); + r = system(ipfw_rule); + + } + + if (session.initSession > 0) { + + SendReset(); + shutdown(session.socket,2); + + } + + if (session.initCapture > 0) { + CaptureEnd(); + } + +} + +void Quit(int how) +{ + + Cleanup(); + fflush(stdout); + fflush(stderr); + exit(how); + +} +#endif /* 0 */ + +double GetTime() +{ + struct timeval tv; + struct timezone tz; + double postEpochSecs; + + if (gettimeofday(&tv, &tz) < 0) { + perror("GetTime"); + exit(-1); + } + + postEpochSecs = (double)tv.tv_sec + ((double)tv.tv_usec/(double)1000000.0); + return postEpochSecs; +} + +double GetTimeMicroSeconds() +{ + struct timeval tv; + struct timezone tz; + double postEpochMicroSecs; + + if (gettimeofday(&tv, &tz) < 0) { + perror("GetTimeMicroSeconds"); + exit(-1); + } + + postEpochMicroSecs = (double)tv.tv_sec * 1000000 + (double)tv.tv_usec; + return postEpochMicroSecs; + +} + +void PrintTimeStamp(struct timeval *ts) +{ + (void)printf("%02d:%02d:%02d.%06u ", + (unsigned int)ts->tv_sec / 3600, + ((unsigned int)ts->tv_sec % 3600) / 60, + (unsigned int)ts->tv_sec % 60, (unsigned int)ts->tv_usec); +} + +void processBadPacket (struct IPPacket *p) +{ + + if (session.debug == SESSION_DEBUG_HIGH) { + printf("In ProcessBadPacket...\n"); + } + /* + * reset? the other guy does not like us? + */ + if (INSESSION(p,session.dst,session.dport,session.src,session.sport) && (p->tcp->tcp_flags & TCPFLAGS_RST)) { + printf("ERROR: EARLY_RST.\nRETURN CODE: %d\n", EARLY_RST); + Quit(EARLY_RST); + } + /* + * some other packet between us that is none of the above + */ + if (INSESSION(p, session.src, session.sport, session.dst, session.dport) || + INSESSION(p, session.dst, session.dport, session.src, session.sport)) { + + printf("ERROR: Unexpected packet\nRETURN CODE: %d\n", UNEXPECTED_PKT); + printf("Expecting:\n"); + printf("\tsrc = %s:%d (seq=%u, ack=%u)\n", + InetAddress(session.src), session.sport, + session.snd_nxt-session.iss, + session.rcv_nxt-session.irs); + printf("\tdst = %s:%d (seq=%u, ack=%u)\n", + InetAddress(session.dst),session.dport, + session.rcv_nxt-session.irs, session.snd_una-session.iss); + printf("Received:\n\t"); + PrintTcpPacket(p); + printf ("session.snd_nxt=%d, session.rcv_nxt=%d, session.snd_una=%d\n", + session.snd_nxt-session.iss, session.rcv_nxt-session.irs, session.snd_una-session.iss); + Quit(UNEXPECTED_PKT); + } + /* + * none of the above, + * so we must be seeing packets + * from some other flow! + */ + else { + printf("ERRROR: Received packet from different flow\nRETURN CODE: %d\n", DIFF_FLOW); + PrintTcpPacket(p); + Quit(DIFF_FLOW) ; + } + + if (session.debug == SESSION_DEBUG_HIGH) { + printf("Out ProcessBadPacket...\n"); + } +} + +void busy_wait (double wait) +{ + double now = GetTime(); + double x = now ; + while ((x - now) < wait) { + x = GetTime(); + } +} diff --git a/network_cmds-543/ecnprobe/support.h b/network_cmds-543/ecnprobe/support.h new file mode 100644 index 00000000..94be2e56 --- /dev/null +++ b/network_cmds-543/ecnprobe/support.h @@ -0,0 +1,132 @@ + +/* + Copyright (c) 2000 + International Computer Science Institute + All rights reserved. + + This file may contain software code originally developed for the + Sting project. The Sting software carries the following copyright: + + Copyright (c) 1998, 1999 + Stefan Savage and the University of Washington. + 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgment: + This product includes software developed by ACIRI, the AT&T + Center for Internet Research at ICSI (the International Computer + Science Institute). This product may also include software developed + by Stefan Savage at the University of Washington. + 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington + may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ICSI 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 ICSI 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 + +#define MAXRESETRETRANSMITS (3) +/*#define INSESSION(p, src, sport, dst, dport) \ + (((p)->ip.ip_src == (src)) && ((p)->ip.ip_dst == (dst)) && \ + ((p)->ip.ip_p == IPPROTOCOL_TCP) && \ + ((p)->tcp.tcp_sport == htons(sport)) && \ + ((p)->tcp.tcp_dport == htons(dport)))*/ + +#define INSESSION(p, src, sport, dst, dport) \ + (((p)->ip->ip_src == (src)) && ((p)->ip->ip_dst == (dst)) && \ + ((p)->ip->ip_p == IPPROTOCOL_TCP) && \ + ((p)->tcp->tcp_sport == htons(sport)) && \ + ((p)->tcp->tcp_dport == htons(dport))) + +#define SEQ_LT(a,b) ((int)((a)-(b)) < 0) +#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) +#define SEQ_GT(a,b) ((int)((a)-(b)) > 0) +#define SEQ_GEQ(a,b) ((int)((a)-(b)) >= 0) + +#define DEFAULT_TARGETPORT (80) +#define DEFAULT_MSS 1360 +#define DEFAULT_MTU 1500 +#define RTT_TO_MULT 5 +#define PLOTDIFF 0.00009 + +/* Response codes */ +#define FAIL -1 +#define SUCCESS 0 +#define NO_TARGET_CANON_INFO 1 +#define NO_LOCAL_HOSTNAME 2 +#define NO_SRC_CANON_INFO 3 +#define NO_SESSION_ESTABLISH 4 +#define MSS_TOO_SMALL 5 +#define BAD_ARGS 6 +#define FIREWALL_ERR 7 +#define ERR_SOCKET_OPEN 8 +#define ERR_SOCKOPT 9 +#define ERR_MEM_ALLOC 10 +#define NO_CONNECTION 11 +#define MSS_ERR 12 +#define BUFFER_OVERFLOW 13 +#define UNWANTED_PKT_DROP 14 +#define EARLY_RST 15 +#define UNEXPECTED_PKT 16 +#define DIFF_FLOW 17 +#define ERR_CHECKSUM 18 +#define NOT_ENOUGH_PKTS 19 +#define BAD_OPT_LEN 20 +#define TOO_MANY_PKTS 21 +#define NO_DATA_RCVD 22 +#define NO_TRGET_SPECIFIED 23 +#define BAD_OPTIONS 24 +#define TOO_MANY_TIMEOUTS 25 +#define TOO_MANY_RXMTS 26 +#define NO_SACK 27 +#define ERR_IN_SB_CALC 28 +#define TOO_MANY_HOLES 29 +#define TOO_MANY_DROPS 30 +#define UNWANTED_PKT_REORDER 31 +#define NO_PMTUD_ENABLED 32 +#define UNKNOWN_BEHAVIOR 33 +#define NO_SYNACK_RCVD 34 +#define SEND_REQUEST_FAILED 35 +#define PKT_SIZE_CHANGED 36 +#define ECN_SYN_DROP 37 + +#define DEFAULT_FILENAME "/" + +#define RTT_TO_MULT 5 +#define SYNTIMEOUT (2.0) +#define REXMITDELAY (2.0) +#define MAXSYNRETRANSMITS (6) +#define MAXDATARETRANSMITS (6) + +/* HTTP Response Codes */ +#define HTTP_OK "200" + + +void SendReset(); +void SigHandle (int signo); +void Cleanup(); +void Quit(int how); +double GetTime(); +double GetTimeMicroSeconds(); +void PrintTimeStamp(struct timeval *ts); +void processBadPacket (struct IPPacket *p); +void busy_wait (double wait); diff --git a/network_cmds-543/frame_delay/frame_delay.8 b/network_cmds-543/frame_delay/frame_delay.8 new file mode 100644 index 00000000..0f454d59 --- /dev/null +++ b/network_cmds-543/frame_delay/frame_delay.8 @@ -0,0 +1,45 @@ +.Dd October 12, 2015 +.Dt FRAME_DELAY 8 +.Os Darwin +.Sh NAME +.Nm frame_delay +.Nd Utility to measure TCP/UDP frame delay + +.Sh DESCRIPTION +.Pp +The +.Nm +utility is designed to measure the effect of latency on +delivery of evenly spaced TCP/UDP frames. This can be latency induced +due to buffering or protocol stack or network drivers. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl m +Server/Client mode. Should be "server" or "client". +.It Fl t +TCP/UDP frame. Should be either "tcp" or "udp". +.It Fl i +(Client Only) Server ip address. +.It Fl p +Port number. +.It Fl n +Number of frames. +.It Fl f +Frame size. +.It Fl d +(Client only) Delay traffic class. Pick one from {BK_SYS, BK, BE, RD, QAM, AV, RV, VI, VO, CTL}. +.El + +.Sh EXAMPLES +.Pp +Setup TCP server: +.Dl "frame_delay -m server -t tcp -p 10010 -n 10 -f 1000" +.Pp +Setup corresponding TCP client: +.Dl "frame_delay -m client -t tcp -i 127.0.0.1 -p 10010 -n 10 -f 1000 -d 2000 -k RD" + +.Sh AUTHORS +.An Padma Bhooma , +.An Kang Sun , +.An Vincent Lubet . \ No newline at end of file diff --git a/network_cmds-543/frame_delay/frame_delay.c b/network_cmds-543/frame_delay/frame_delay.c new file mode 100644 index 00000000..37a58b76 --- /dev/null +++ b/network_cmds-543/frame_delay/frame_delay.c @@ -0,0 +1,595 @@ +/* + * Copyright (c) 2009-2015 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Usage for frame_delay + * + * Server + * ./frame_delay -m server -t -p -n -f + * + * Client + * ./frame_delay -m client -t -i -p -n -f -d -k + */ + +/* + * TODO list : + * 1. UDP fragmentation and reassembly + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Server Static variable */ +static int so, srv_so; +static int srv_port = 0; +static struct sockaddr_in laddr, dst_addr; +/* Client Static variable */ +static struct sockaddr_in srv_addr; +static uint32_t tc = 0; +/* Usage */ +void ErrorUsage(void); +/* str2svc */ +uint32_t str2svc(const char *str); +/* Show Stastics */ +void ShowStastics(int64_t *DiffsBuf, int num_frames); +/* Returns difference between two timevals in microseconds */ +int64_t time_diff(struct timeval *b, struct timeval *a); +/* tcp server */ +void tcpServer(int frame_size, int num_frames, char *buf, int64_t *DiffsBuf); +/* udp server */ +void udpServer(int frame_size, int num_frames, char *buf, int64_t *DiffsBuf); +/* tcp server */ +void tcpClient(int num_frames, int frame_size, + const char *buf, struct timespec sleep_time); +/* udp server */ +void udpClient(int num_frames, int frame_size, + const char *buf, struct timespec sleep_time); + +/* Main function */ +int +main(int argc, char *argv[]) +{ + int num_frames = 0, frame_size = 0, delay_ms = 0, rc = 0; + char *buf = NULL, ch, *type = NULL, *mode = NULL, *ip_addr = NULL; + int64_t *DiffsBuf; + struct timespec sleep_time; + + while ((ch = getopt(argc, argv, "m:p:f:n:t:d:i:k:")) != -1) { + switch (ch) { + case 'm': { + mode = optarg; + break; + } + case 'p': { + srv_port = atoi(optarg); + break; + } + case 'f' : { + frame_size = atoi(optarg); + break; + } + case 'n' : { + num_frames = atoi(optarg); + break; + } + case 'i': { + ip_addr = optarg; + bzero(&srv_addr, sizeof(srv_addr)); + rc = inet_aton(optarg, &srv_addr.sin_addr); + if (rc == 0) { + perror("inet_ntoa failed"); + exit(1); + } + } + case 'd': { + delay_ms = atoi(optarg); + break; + } + case 't' : { + type = optarg; + break; + } + case 'k': { + tc = str2svc(optarg); + break; + } + default: { + printf("Invalid option: %c\n", ch); + ErrorUsage(); + } + } + } + /* General check for both server and client */ + if (srv_port <= 0 || frame_size <= 0 || num_frames <= 0 || !mode || !type) { + ErrorUsage(); + } + if ( strcmp(type, "tcp") != 0 && strcmp(type, "udp") != 0 ) { + ErrorUsage(); + } + /* Allocate memory for buf */ + buf = calloc(1, frame_size); + if (buf == NULL) { + printf("malloc failed\n"); + exit(1); + } + if ( strcmp(mode, "server") == 0 ) { + /* Server */ + printf(" : Start %s server on port %d with expected frame size of %d\n", + type, srv_port, frame_size); + DiffsBuf = (int64_t *)calloc(num_frames, sizeof(int64_t)); + if (DiffsBuf == NULL) { + printf("malloc failed\n"); + exit(1); + } + if( strcmp(type, "tcp") == 0) { + /* tcpServer */ + tcpServer(frame_size, num_frames, buf, DiffsBuf); + } else { + /* updServer */ + udpServer(frame_size, num_frames, buf, DiffsBuf); + } + } + else if ( strcmp(mode, "client") == 0 ){ + if ( !ip_addr || (tc > 0 && (tc < SO_TC_BK_SYS || tc > SO_TC_CTL)) ){ + ErrorUsage(); + } + /* Client */ + printf(" : Start sending %d %s frames to %s:%d with a frame size of %d\n", + num_frames, type, ip_addr, srv_port, frame_size); + /* Resolving sleep time bug : delay_ms should just be calculated once */ + bzero(&sleep_time, sizeof(sleep_time)); + while (delay_ms >= 1000) { + sleep_time.tv_sec++; + delay_ms -= 1000; + } + sleep_time.tv_nsec = delay_ms * 1000 * 1000; + if( strcmp(type, "tcp") == 0) { + /* Call TCP client */ + tcpClient(num_frames, frame_size, buf, sleep_time); + } else { + /* Call UDP client */ + udpClient(num_frames, frame_size, buf, sleep_time); + } + } else { + ErrorUsage(); + } +} + +/* Error usage */ +void +ErrorUsage(void) { + printf("Correct Usage"); + printf("Server : frame_delay -m server -t -p -n -f \n"); + printf("Client : frame_delay -m client -t -i -p -n -f -d -k \n"); + exit(1); +} + +/* str2svc */ +uint32_t +str2svc(const char *str) +{ + uint32_t svc; + char *endptr; + + if (str == NULL || *str == '\0') + svc = UINT32_MAX; + else if (strcasecmp(str, "BK_SYS") == 0) + return SO_TC_BK_SYS; + else if (strcasecmp(str, "BK") == 0) + return SO_TC_BK; + else if (strcasecmp(str, "BE") == 0) + return SO_TC_BE; + else if (strcasecmp(str, "RD") == 0) + return SO_TC_RD; + else if (strcasecmp(str, "OAM") == 0) + return SO_TC_OAM; + else if (strcasecmp(str, "AV") == 0) + return SO_TC_AV; + else if (strcasecmp(str, "RV") == 0) + return SO_TC_RV; + else if (strcasecmp(str, "VI") == 0) + return SO_TC_VI; + else if (strcasecmp(str, "VO") == 0) + return SO_TC_VO; + else if (strcasecmp(str, "CTL") == 0) + return SO_TC_CTL; + else { + svc = (uint32_t)strtoul(str, &endptr, 0); + if (*endptr != '\0') + svc = UINT32_MAX; + } + return (svc); +} + +/* Show Stastics */ +void +ShowStastics(int64_t *DiffsBuf, int num_frames) { + int i = 0; + int64_t sum = 0, mean = 0; + + /* Mean */ + while(i < num_frames) + sum += DiffsBuf[i++]; + mean = sum / num_frames; + printf(" : Mean: %.2f usecs\n", sum / (double)num_frames); + /* Popular Standard Deviation */ + i = 0; + sum = 0; + while(i < num_frames) { + sum += (DiffsBuf[i]-mean)*(DiffsBuf[i]-mean); + i++; + } + printf(" : Popular Standard Deviation: %.2f usecs\n", + sqrt(sum/(double)num_frames)); +} + +/* Returns difference between two timevals in microseconds */ +int64_t +time_diff(struct timeval *b, struct timeval *a) +{ + int64_t usecs; + usecs = (a->tv_sec - b->tv_sec) * 1000 * 1000; + usecs += (int64_t)(a->tv_usec - b->tv_usec); + return(usecs); +} + +/* Server */ + +/* tcp server */ +void +tcpServer(int frame_size, int num_frames, char *buf, int64_t *DiffsBuf) { + int rc = 0, i = 0, ignore_count = 0; + uint32_t dst_len = 0; + struct timeval before, after; + ssize_t bytes; + int64_t usecs; + /* New change from Padama */ + uint64_t prev_frame_ts = 0, prev_recv = 0, frame_ts = 0, cur_recv = 0; + uint64_t min_variation = 0, max_variation = 0, avg_variation = 0; + + printf(" : TCP Server\n"); + so = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (so == -1) { + perror("failed to create socket"); + exit(1); + } + bzero(&laddr, sizeof(laddr)); + laddr.sin_family = AF_INET; + laddr.sin_port = htons(srv_port); + rc = bind(so, (const struct sockaddr *)&laddr, sizeof(laddr)); + if (rc != 0) { + perror("failed to bind"); + exit(1); + } + rc = listen(so, 10); + if (rc != 0) { + perror("failed to listen"); + exit(1); + } + srv_so = accept(so, (struct sockaddr *)&dst_addr, &dst_len); + if (srv_so == -1) { + perror("failed to accept"); + exit(1); + } + while (1) { + if ( i == num_frames ) { + printf(" : Completed\n"); + break; + } + printf(" : Waiting for receiving\n"); + bzero(&before, sizeof(before)); + bzero(&after, sizeof(after)); + rc = gettimeofday(&before, NULL); + if (rc == -1) { + perror("gettimeofday failed"); + exit(1); + } + bytes = recv(srv_so, buf, frame_size, MSG_WAITALL); + if (bytes == -1) { + perror("recv failed"); + exit(1); + } + else if (bytes > 0 && bytes != frame_size) { + printf("Client exited\n"); + printf("Didn't recv the complete frame, bytes %ld\n", + bytes); + exit(1); + } + else if (bytes == 0) { + break; + } + rc = gettimeofday(&after, NULL); + if (rc == -1) { + perror("gettimeofday failed"); + exit(1); + } + cur_recv = after.tv_sec * 1000 * 1000 + after.tv_usec; + memcpy((void *)&frame_ts, buf, sizeof(frame_ts)); + if (prev_frame_ts > 0) { + int64_t d_variation = 0; + d_variation = (int64_t)((cur_recv - prev_recv) - + (frame_ts - prev_frame_ts)); + /* printf("Frame %u ts %llu d_variation %lld usecs\n", + i, frame_ts, d_variation);*/ + if (d_variation > 0) { + if (min_variation == 0) + min_variation = d_variation; + else + min_variation = ((min_variation <= d_variation) ? + min_variation : d_variation); + max_variation = ((max_variation >= d_variation) ? + max_variation : d_variation); + avg_variation += d_variation; + } else { + ignore_count++; + } + } + prev_recv = cur_recv; + prev_frame_ts = frame_ts; + ++i; + /* Compute the time differenc */ + usecs = time_diff(&before, &after); + DiffsBuf[i] = usecs; + printf(" : Frame %d received after %lld usecs\n", i, usecs); + } + if (i != ignore_count) + avg_variation = avg_variation / (i - ignore_count); + else + avg_variation = 0; + + printf(" : Received frames: %u\n", i); + printf(" : Ignored frames: %u\n", ignore_count); + printf(" : Minimum delay variation: %llu usecs\n", min_variation); + printf(" : Maximum delay variation: %llu usecs\n", max_variation); + printf(" : Average delay variation: %llu usecs\n", avg_variation); + ShowStastics(DiffsBuf, num_frames); +} + +/* udp server */ +void +udpServer(int frame_size, int num_frames, char *buf, int64_t *DiffsBuf) { + int rc = 0, i = 0, ignore_count = 0; + uint32_t dst_len = 0; + ssize_t bytes; + struct timeval before, after; + int64_t usecs; + /* New change from Padama */ + uint64_t prev_frame_ts = 0, prev_recv = 0, frame_ts = 0, cur_recv = 0; + uint64_t min_variation = 0, max_variation = 0, avg_variation = 0; + + printf(" : UDP Server\n"); + so = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (so == -1) { + perror("failed to create socket"); + exit(1); + } + bzero(&laddr,sizeof(laddr)); + laddr.sin_family = AF_INET; + laddr.sin_addr.s_addr=htonl(INADDR_ANY); + laddr.sin_port=htons(srv_port); + rc = bind(so, (struct sockaddr *)&laddr,sizeof(laddr)); + if (rc != 0) { + perror("failed to bind"); + exit(1); + } + while (1) { + if ( i == num_frames ) { + printf(" : Completed\n"); + break; + } + printf(" : Waiting for receiving\n"); + bzero(&before, sizeof(before)); + bzero(&after, sizeof(after)); + rc = gettimeofday(&before, NULL); + if (rc == -1) { + perror("gettimeofday failed"); + exit(1); + } + bytes = recvfrom(so, buf, frame_size, 0, (struct sockaddr *)&dst_addr, &dst_len); + if (bytes == -1) { + perror("recv failed"); + exit(1); + } + else if (bytes > 0 && bytes != frame_size) { + printf("Client exited\n"); + printf("Didn't recv the complete frame, bytes %ld\n", + bytes); + exit(1); + } + else if (bytes == 0) { + break; + } + rc = gettimeofday(&after, NULL); + if (rc == -1) { + perror("gettimeofday failed"); + exit(1); + } + cur_recv = after.tv_sec * 1000 * 1000 + after.tv_usec; + memcpy((void *)&frame_ts, buf, sizeof(frame_ts)); + if (prev_frame_ts > 0) { + int64_t d_variation = 0; + + d_variation = (int64_t)((cur_recv - prev_recv) - + (frame_ts - prev_frame_ts)); + /* printf("Frame %u ts %llu d_variation %lld usecs\n", + i, frame_ts, d_variation);*/ + if (d_variation > 0) { + if (min_variation == 0) + min_variation = d_variation; + else + min_variation = ((min_variation <= d_variation) ? + min_variation : d_variation); + max_variation = ((max_variation >= d_variation) ? + max_variation : d_variation); + avg_variation += d_variation; + } else { + ignore_count++; + } + } + prev_recv = cur_recv; + prev_frame_ts = frame_ts; + ++i; + /* Compute the time differenc */ + usecs = time_diff(&before, &after); + DiffsBuf[i] = usecs; + printf(" : Frame %d received after %lld usecs\n", i, usecs); + } + if (i != ignore_count) + avg_variation = avg_variation / (i - ignore_count); + else + avg_variation = 0; + printf(" : Received frames: %u\n", i); + printf(" : Ignored frames: %u\n", ignore_count); + printf(" : Minimum delay variation: %llu usecs\n", min_variation); + printf(" : Maximum delay variation: %llu usecs\n", max_variation); + printf(" : Average delay variation: %llu usecs\n", avg_variation); + ShowStastics(DiffsBuf, num_frames); +} + +/* Client */ +void +tcpClient(int num_frames, int frame_size, + const char *buf, struct timespec sleep_time){ + int rc = 0, i = 0; + ssize_t bytes; + + printf(" : TCP Client\n"); + so = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (so <= 0) { + perror("creating socket failed"); + exit(1); + } + srv_addr.sin_port = htons(srv_port); + srv_addr.sin_len = sizeof(srv_addr); + srv_addr.sin_family = AF_INET; + rc = connect(so, (const struct sockaddr *)&srv_addr, + sizeof(srv_addr)); + if (rc != 0) { + perror("connect failed"); + exit(1); + } + if (tc > 0) { + rc = setsockopt(so, SOL_SOCKET, SO_TRAFFIC_CLASS, &tc, + sizeof(tc)); + if (rc == -1) { + perror("failed to set traffic class"); + exit(1); + } + } + for (i = 0; i < num_frames; ++i) { + struct timeval fts; + uint64_t frame_ts; + /* Add a timestamp to the frame */ + rc = gettimeofday(&fts, NULL); + if (rc == -1) { + perror("faile to get time of day"); + exit(1); + } + frame_ts = fts.tv_sec * 1000 * 1000 + fts.tv_usec; + memcpy((void *)buf, (const void *)&frame_ts, sizeof(frame_ts)); + bytes = send(so, buf, frame_size, 0); + if (bytes == -1) { + perror("send failed \n"); + exit(1); + } + if (bytes != frame_size) { + printf("failed to send all bytes, sent %ld\n", bytes); + exit (1); + } + rc = nanosleep(&sleep_time, NULL); + if (rc == -1) { + perror("sleep failed"); + exit(1); + } + printf(" : Sent %u frames as a whole\n", (i + 1)); + } +} + +void +udpClient(int num_frames, int frame_size, + const char *buf, struct timespec sleep_time){ + int rc = 0, i = 0; + ssize_t bytes; + + printf(" : UDP Client\n"); + so = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (so <= 0) { + perror("creating socket failed"); + exit(1); + } + srv_addr.sin_port = htons(srv_port); + srv_addr.sin_len = sizeof(srv_addr); + srv_addr.sin_family = AF_INET; + if (tc > 0) { + rc = setsockopt(so, SOL_SOCKET, SO_TRAFFIC_CLASS, &tc, + sizeof(tc)); + if (rc == -1) { + perror("failed to set traffic class"); + exit(1); + } + } + for (i = 0; i < num_frames; ++i) { + struct timeval fts; + uint64_t frame_ts; + /* Add a timestamp to the frame */ + rc = gettimeofday(&fts, NULL); + if (rc == -1) { + perror("faile to get time of day"); + exit(1); + } + frame_ts = fts.tv_sec * 1000 * 1000 + fts.tv_usec; + memcpy((void *)buf, (const void *)&frame_ts, sizeof(frame_ts)); + bytes = sendto(so, buf, frame_size, 0, (struct sockaddr *)&srv_addr, sizeof(srv_addr)); + if (bytes == -1) { + perror("send failed \n"); + exit(1); + } + if (bytes != frame_size) { + printf("failed to send all bytes, sent %ld\n", bytes); + exit (1); + } + rc = nanosleep(&sleep_time, NULL); + if (rc == -1) { + perror("sleep failed"); + exit(1); + } + printf(" : Sent %u frames as a whole\n", (i + 1)); + } +} + + diff --git a/network_cmds-543/ifconfig.tproj/af_inet.c b/network_cmds-543/ifconfig.tproj/af_inet.c new file mode 100644 index 00000000..ae059704 --- /dev/null +++ b/network_cmds-543/ifconfig.tproj/af_inet.c @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2009-2011 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Copyright (c) 1983, 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. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include /* for struct ifaddr */ +#include +#include +#include + +#include "ifconfig.h" + +static struct ifaliasreq in_addreq; +static struct ifreq in_ridreq; + +static void +in_status(int s __unused, const struct ifaddrs *ifa) +{ + struct sockaddr_in *sin, null_sin; + + memset(&null_sin, 0, sizeof(null_sin)); + + sin = (struct sockaddr_in *)ifa->ifa_addr; + if (sin == NULL) + return; + + printf("\tinet %s ", inet_ntoa(sin->sin_addr)); + + if (ifa->ifa_flags & IFF_POINTOPOINT) { + sin = (struct sockaddr_in *)ifa->ifa_dstaddr; + if (sin == NULL) + sin = &null_sin; + printf("--> %s ", inet_ntoa(sin->sin_addr)); + } + + sin = (struct sockaddr_in *)ifa->ifa_netmask; + if (sin == NULL) + sin = &null_sin; + printf("netmask 0x%lx ", (unsigned long)ntohl(sin->sin_addr.s_addr)); + + if (ifa->ifa_flags & IFF_BROADCAST) { + sin = (struct sockaddr_in *)ifa->ifa_broadaddr; + if (sin != NULL && sin->sin_addr.s_addr != 0) + printf("broadcast %s", inet_ntoa(sin->sin_addr)); + } + putchar('\n'); +} + +#define SIN(x) ((struct sockaddr_in *) &(x)) +static struct sockaddr_in *sintab[] = { + SIN(in_ridreq.ifr_addr), SIN(in_addreq.ifra_addr), + SIN(in_addreq.ifra_mask), SIN(in_addreq.ifra_broadaddr) +}; + +static void +in_getaddr(const char *s, int which) +{ +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif /* MIN */ + struct sockaddr_in *sin = sintab[which]; + struct hostent *hp; + struct netent *np; + + sin->sin_len = sizeof(*sin); + if (which != MASK) + sin->sin_family = AF_INET; + + if (which == ADDR) { + char *p = NULL; + + if((p = strrchr(s, '/')) != NULL) { + /* address is `name/masklen' */ + int masklen; + int ret; + struct sockaddr_in *min = sintab[MASK]; + *p = '\0'; + ret = sscanf(p+1, "%u", &masklen); + if(ret != 1 || (masklen < 0 || masklen > 32)) { + *p = '/'; + errx(1, "%s: bad value", s); + } + min->sin_len = sizeof(*min); + min->sin_addr.s_addr = htonl(~((1LL << (32 - masklen)) - 1) & + 0xffffffff); + } + } + + if (inet_aton(s, &sin->sin_addr)) + return; + if ((hp = gethostbyname(s)) != 0) + bcopy(hp->h_addr, (char *)&sin->sin_addr, + MIN(hp->h_length, sizeof(sin->sin_addr))); + else if ((np = getnetbyname(s)) != 0) + sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY); + else + errx(1, "%s: bad value", s); +#undef MIN +} + +static void +in_status_tunnel(int s) +{ + char src[NI_MAXHOST]; + char dst[NI_MAXHOST]; + struct ifreq ifr; + const struct sockaddr *sa = (const struct sockaddr *) &ifr.ifr_addr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, name, IFNAMSIZ); + + if (ioctl(s, SIOCGIFPSRCADDR, (caddr_t)&ifr) < 0) + return; + if (sa->sa_family != AF_INET) + return; + if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, NI_NUMERICHOST) != 0) + src[0] = '\0'; + + if (ioctl(s, SIOCGIFPDSTADDR, (caddr_t)&ifr) < 0) + return; + if (sa->sa_family != AF_INET) + return; + if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, NI_NUMERICHOST) != 0) + dst[0] = '\0'; + + printf("\ttunnel inet %s --> %s\n", src, dst); +} + +static void +in_set_tunnel(int s, struct addrinfo *srcres, struct addrinfo *dstres) +{ + struct ifaliasreq addreq; + + memset(&addreq, 0, sizeof(addreq)); + strncpy(addreq.ifra_name, name, IFNAMSIZ); + memcpy(&addreq.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len); + memcpy(&addreq.ifra_dstaddr, dstres->ai_addr, dstres->ai_addr->sa_len); + + if (ioctl(s, SIOCSIFPHYADDR, &addreq) < 0) + warn("SIOCSIFPHYADDR"); +} + +static void +in_set_router(int s, int enable) +{ + struct ifreq ifr; + + bzero(&ifr, sizeof (ifr)); + strncpy(ifr.ifr_name, name, IFNAMSIZ); + ifr.ifr_intval = enable; + + if (ioctl(s, SIOCSETROUTERMODE, &ifr) < 0) + warn("SIOCSETROUTERMODE"); +} + +static struct afswtch af_inet = { + .af_name = "inet", + .af_af = AF_INET, + .af_status = in_status, + .af_getaddr = in_getaddr, + .af_status_tunnel = in_status_tunnel, + .af_settunnel = in_set_tunnel, + .af_setrouter = in_set_router, + .af_difaddr = SIOCDIFADDR, + .af_aifaddr = SIOCAIFADDR, + .af_ridreq = &in_ridreq, + .af_addreq = &in_addreq, +}; + +static __constructor void +inet_ctor(void) +{ + af_register(&af_inet); +} diff --git a/network_cmds-543/ifconfig.tproj/af_inet6.c b/network_cmds-543/ifconfig.tproj/af_inet6.c new file mode 100644 index 00000000..d2a5b1be --- /dev/null +++ b/network_cmds-543/ifconfig.tproj/af_inet6.c @@ -0,0 +1,660 @@ +/* + * Copyright (c) 2009-2014 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Copyright (c) 1983, 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. + */ + + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include /* for struct ifaddr */ +#include +#include +#include + +#include /* Define ND6_INFINITE_LIFETIME */ + +#include "ifconfig.h" + +#define ND6BITS "\020\001PERFORMNUD\002ACCEPT_RTADV\003PREFER_SOURCE" \ + "\004IFDISABLED\005DONT_SET_IFROUTE\006PROXY_PREFIXES" \ + "\007IGNORE_NA\010INSECURE\011REPLICATED\012DAD" + +static struct in6_ifreq in6_ridreq; +static struct in6_aliasreq in6_addreq = + { { 0 }, + { 0 }, + { 0 }, + { 0 }, + 0, + { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME } }; +static int ip6lifetime; + +static void in6_fillscopeid(struct sockaddr_in6 *sin6); +static int prefix(void *, int); +static char *sec2str(time_t); +static int explicit_prefix = 0; + +static char addr_buf[MAXHOSTNAMELEN *2 + 1]; /*for getnameinfo()*/ + +static void +setifprefixlen(const char *addr, int dummy __unused, int s, + const struct afswtch *afp) +{ + if (afp->af_getprefix != NULL) + afp->af_getprefix(addr, MASK); + explicit_prefix = 1; +} + +static void +setnd6flags(const char *dummyaddr __unused, int d, int s, + const struct afswtch *afp) +{ + struct in6_ndireq nd; + int error; + + memset(&nd, 0, sizeof(nd)); + strncpy(nd.ifname, ifr.ifr_name, sizeof(nd.ifname)); + error = ioctl(s, SIOCGIFINFO_IN6, &nd); + if (error) { + warn("ioctl(SIOCGIFINFO_IN6)"); + return; + } + if (d < 0) + nd.ndi.flags &= ~(-d); + else + nd.ndi.flags |= d; + error = ioctl(s, SIOCSIFINFO_FLAGS, (caddr_t)&nd); + if (error) + warn("ioctl(SIOCSIFINFO_FLAGS)"); +} + +static void +setip6flags(const char *dummyaddr __unused, int flag, int dummysoc __unused, + const struct afswtch *afp) +{ + if (afp->af_af != AF_INET6) + err(1, "address flags can be set only for inet6 addresses"); + + if (flag < 0) + in6_addreq.ifra_flags &= ~(-flag); + else + in6_addreq.ifra_flags |= flag; +} + +static void +setip6lifetime(const char *cmd, const char *val, int s, + const struct afswtch *afp) +{ + time_t newval, t; + char *ep; + + t = time(NULL); + newval = (time_t)strtoul(val, &ep, 0); + if (val == ep) + errx(1, "invalid %s", cmd); + if (afp->af_af != AF_INET6) + errx(1, "%s not allowed for the AF", cmd); + if (strcmp(cmd, "vltime") == 0) { + in6_addreq.ifra_lifetime.ia6t_expire = t + newval; + in6_addreq.ifra_lifetime.ia6t_vltime = newval; + } else if (strcmp(cmd, "pltime") == 0) { + in6_addreq.ifra_lifetime.ia6t_preferred = t + newval; + in6_addreq.ifra_lifetime.ia6t_pltime = newval; + } +} + +static void +setip6pltime(const char *seconds, int dummy __unused, int s, + const struct afswtch *afp) +{ + setip6lifetime("pltime", seconds, s, afp); +} + +static void +setip6vltime(const char *seconds, int dummy __unused, int s, + const struct afswtch *afp) +{ + setip6lifetime("vltime", seconds, s, afp); +} + +static void +setip6eui64(const char *cmd, int dummy __unused, int s, + const struct afswtch *afp) +{ + struct ifaddrs *ifap, *ifa; + const struct sockaddr_in6 *sin6 = NULL; + const struct in6_addr *lladdr = NULL; + struct in6_addr *in6; + + if (afp->af_af != AF_INET6) + errx(EXIT_FAILURE, "%s not allowed for the AF", cmd); + in6 = (struct in6_addr *)&in6_addreq.ifra_addr.sin6_addr; + if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0) + errx(EXIT_FAILURE, "interface index is already filled"); + if (getifaddrs(&ifap) != 0) + err(EXIT_FAILURE, "getifaddrs"); + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family == AF_INET6 && + strcmp(ifa->ifa_name, name) == 0) { + sin6 = (const struct sockaddr_in6 *)ifa->ifa_addr; + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { + lladdr = &sin6->sin6_addr; + break; + } + } + } + if (!lladdr) + errx(EXIT_FAILURE, "could not determine link local address"); + + memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8); + + freeifaddrs(ifap); +} + +static void +in6_fillscopeid(struct sockaddr_in6 *sin6) +{ +#if defined(__KAME__) && defined(KAME_SCOPEID) + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { + sin6->sin6_scope_id = + ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]); + sin6->sin6_addr.s6_addr[2] = sin6->sin6_addr.s6_addr[3] = 0; + } +#endif +} + +static void +in6_status(int s __unused, const struct ifaddrs *ifa) +{ + struct sockaddr_in6 *sin, null_sin; + struct in6_ifreq ifr6; + int s6; + u_int32_t flags6; + struct in6_addrlifetime lifetime; + time_t t = time(NULL); + int error; + u_int32_t scopeid; + + memset(&null_sin, 0, sizeof(null_sin)); + + sin = (struct sockaddr_in6 *)ifa->ifa_addr; + if (sin == NULL) + return; + + strncpy(ifr6.ifr_name, ifr.ifr_name, sizeof(ifr.ifr_name)); + if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + warn("socket(AF_INET6,SOCK_DGRAM)"); + return; + } + ifr6.ifr_addr = *sin; + if (ioctl(s6, SIOCGIFAFLAG_IN6, &ifr6) < 0) { + warn("ioctl(SIOCGIFAFLAG_IN6)"); + close(s6); + return; + } + flags6 = ifr6.ifr_ifru.ifru_flags6; + memset(&lifetime, 0, sizeof(lifetime)); + ifr6.ifr_addr = *sin; + if (ioctl(s6, SIOCGIFALIFETIME_IN6, &ifr6) < 0) { + warn("ioctl(SIOCGIFALIFETIME_IN6)"); + close(s6); + return; + } + lifetime = ifr6.ifr_ifru.ifru_lifetime; + close(s6); + + /* XXX: embedded link local addr check */ + if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) && + *(u_short *)&sin->sin6_addr.s6_addr[2] != 0) { + u_short index; + + index = *(u_short *)&sin->sin6_addr.s6_addr[2]; + *(u_short *)&sin->sin6_addr.s6_addr[2] = 0; + if (sin->sin6_scope_id == 0) + sin->sin6_scope_id = ntohs(index); + } + scopeid = sin->sin6_scope_id; + + error = getnameinfo((struct sockaddr *)sin, sin->sin6_len, addr_buf, + sizeof(addr_buf), NULL, 0, NI_NUMERICHOST); + if (error != 0) + inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, + sizeof(addr_buf)); + printf("\tinet6 %s ", addr_buf); + + if (ifa->ifa_flags & IFF_POINTOPOINT) { + sin = (struct sockaddr_in6 *)ifa->ifa_dstaddr; + /* + * some of the interfaces do not have valid destination + * address. + */ + if (sin != NULL && sin->sin6_family == AF_INET6) { + int error; + + /* XXX: embedded link local addr check */ + if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) && + *(u_short *)&sin->sin6_addr.s6_addr[2] != 0) { + u_short index; + + index = *(u_short *)&sin->sin6_addr.s6_addr[2]; + *(u_short *)&sin->sin6_addr.s6_addr[2] = 0; + if (sin->sin6_scope_id == 0) + sin->sin6_scope_id = ntohs(index); + } + + error = getnameinfo((struct sockaddr *)sin, + sin->sin6_len, addr_buf, + sizeof(addr_buf), NULL, 0, + NI_NUMERICHOST); + if (error != 0) + inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, + sizeof(addr_buf)); + printf("--> %s ", addr_buf); + } + } + + sin = (struct sockaddr_in6 *)ifa->ifa_netmask; + if (sin == NULL) + sin = &null_sin; + printf("prefixlen %d ", prefix(&sin->sin6_addr, + sizeof(struct in6_addr))); + + if ((flags6 & IN6_IFF_ANYCAST) != 0) + printf("anycast "); + if ((flags6 & IN6_IFF_TENTATIVE) != 0) + printf("tentative "); + if ((flags6 & IN6_IFF_OPTIMISTIC) != 0) + printf("optimistic "); + if ((flags6 & IN6_IFF_DUPLICATED) != 0) + printf("duplicated "); + if ((flags6 & IN6_IFF_DETACHED) != 0) + printf("detached "); + if ((flags6 & IN6_IFF_DEPRECATED) != 0) + printf("deprecated "); + if ((flags6 & IN6_IFF_AUTOCONF) != 0) + printf("autoconf "); + if ((flags6 & IN6_IFF_TEMPORARY) != 0) + printf("temporary "); + if ((flags6 & IN6_IFF_DYNAMIC) != 0) + printf("dynamic "); + if ((flags6 & IN6_IFF_SECURED) != 0) + printf("secured "); + + if (scopeid) + printf("scopeid 0x%x ", scopeid); + + if (ip6lifetime && (lifetime.ia6t_preferred || lifetime.ia6t_expire)) { + printf("pltime "); + if (lifetime.ia6t_preferred) { + printf("%s ", lifetime.ia6t_preferred < t + ? "0" : sec2str(lifetime.ia6t_preferred - t)); + } else + printf("infty "); + + printf("vltime "); + if (lifetime.ia6t_expire) { + printf("%s ", lifetime.ia6t_expire < t + ? "0" : sec2str(lifetime.ia6t_expire - t)); + } else + printf("infty "); + } + + putchar('\n'); +} + +#define SIN6(x) ((struct sockaddr_in6 *) &(x)) +static struct sockaddr_in6 *sin6tab[] = { + SIN6(in6_ridreq.ifr_addr), SIN6(in6_addreq.ifra_addr), + SIN6(in6_addreq.ifra_prefixmask), SIN6(in6_addreq.ifra_dstaddr) +}; + +static void +in6_getprefix(const char *plen, int which) +{ + struct sockaddr_in6 *sin = sin6tab[which]; + u_char *cp; + int len = atoi(plen); + + if ((len < 0) || (len > 128)) + errx(1, "%s: bad value", plen); + sin->sin6_len = sizeof(*sin); + if (which != MASK) + sin->sin6_family = AF_INET6; + if ((len == 0) || (len == 128)) { + memset(&sin->sin6_addr, 0xff, sizeof(struct in6_addr)); + return; + } + memset((void *)&sin->sin6_addr, 0x00, sizeof(sin->sin6_addr)); + for (cp = (u_char *)&sin->sin6_addr; len > 7; len -= 8) + *cp++ = 0xff; + *cp = 0xff << (8 - len); +} + +static void +in6_getaddr(const char *s, int which) +{ + struct sockaddr_in6 *sin = sin6tab[which]; + struct addrinfo hints, *res; + int error = -1; + + newaddr &= 1; + + sin->sin6_len = sizeof(*sin); + if (which != MASK) + sin->sin6_family = AF_INET6; + + if (which == ADDR) { + char *p = NULL; + if((p = strrchr(s, '/')) != NULL) { + *p = '\0'; + in6_getprefix(p + 1, MASK); + explicit_prefix = 1; + } + } + + if (sin->sin6_family == AF_INET6) { + bzero(&hints, sizeof(struct addrinfo)); + hints.ai_family = AF_INET6; + error = getaddrinfo(s, NULL, &hints, &res); + } + if (error != 0) { + if (inet_pton(AF_INET6, s, &sin->sin6_addr) != 1) + errx(1, "%s: bad value", s); + } else + bcopy(res->ai_addr, sin, res->ai_addrlen); +} + +static int +prefix(void *val, int size) +{ + u_char *name = (u_char *)val; + int byte, bit, plen = 0; + + for (byte = 0; byte < size; byte++, plen += 8) + if (name[byte] != 0xff) + break; + if (byte == size) + return (plen); + for (bit = 7; bit != 0; bit--, plen++) + if (!(name[byte] & (1 << bit))) + break; + for (; bit != 0; bit--) + if (name[byte] & (1 << bit)) + return(0); + byte++; + for (; byte < size; byte++) + if (name[byte]) + return(0); + return (plen); +} + +static char * +sec2str(time_t total) +{ + static char result[256]; + int days, hours, mins, secs; + int first = 1; + char *p = result; + + if (0) { + days = total / 3600 / 24; + hours = (total / 3600) % 24; + mins = (total / 60) % 60; + secs = total % 60; + + if (days) { + first = 0; + p += snprintf(p, sizeof(result) - (p - result), "%dd", days); + } + if (!first || hours) { + first = 0; + p += snprintf(p, sizeof(result) - (p - result), "%dh", hours); + } + if (!first || mins) { + first = 0; + p += snprintf(p, sizeof(result) - (p - result), "%dm", mins); + } + snprintf(p, sizeof(result) - (p - result), "%ds", secs); + } else + snprintf(result, sizeof(result), "%lu", (unsigned long)total); + + return(result); +} + +static void +in6_postproc(int s, const struct afswtch *afp) +{ + if (explicit_prefix == 0) { + /* Aggregatable address architecture defines all prefixes + are 64. So, it is convenient to set prefixlen to 64 if + it is not specified. */ + setifprefixlen("64", 0, s, afp); + /* in6_getprefix("64", MASK) if MASK is available here... */ + } +} + +static void +in6_status_tunnel(int s) +{ + char src[NI_MAXHOST]; + char dst[NI_MAXHOST]; + struct in6_ifreq in6_ifr; + const struct sockaddr *sa = (const struct sockaddr *) &in6_ifr.ifr_addr; + + memset(&in6_ifr, 0, sizeof(in6_ifr)); + strncpy(in6_ifr.ifr_name, name, IFNAMSIZ); + + if (ioctl(s, SIOCGIFPSRCADDR_IN6, (caddr_t)&in6_ifr) < 0) + return; + if (sa->sa_family != AF_INET6) + return; + in6_fillscopeid(&in6_ifr.ifr_addr); + if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, + NI_NUMERICHOST) != 0) + src[0] = '\0'; + + if (ioctl(s, SIOCGIFPDSTADDR_IN6, (caddr_t)&in6_ifr) < 0) + return; + if (sa->sa_family != AF_INET6) + return; + in6_fillscopeid(&in6_ifr.ifr_addr); + if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, + NI_NUMERICHOST) != 0) + dst[0] = '\0'; + + printf("\ttunnel inet6 %s --> %s\n", src, dst); +} + +static void +nd6_status(int s) +{ + struct in6_ndireq nd; + int s6; + int error; + + memset(&nd, 0, sizeof(nd)); + strncpy(nd.ifname, ifr.ifr_name, sizeof(nd.ifname)); + if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + if (errno != EPROTONOSUPPORT) + warn("socket(AF_INET6, SOCK_DGRAM)"); + return; + } + error = ioctl(s6, SIOCGIFINFO_IN6, &nd); + if (error) { + if (errno != EPFNOSUPPORT && errno != EINVAL) + warn("ioctl(SIOCGIFINFO_IN6)"); + close(s6); + return; + } + close(s6); + if (nd.ndi.flags == 0) + return; + printb("\tnd6 options", (unsigned int)nd.ndi.flags, ND6BITS); + putchar('\n'); +} + +static void +in6_set_tunnel(int s, struct addrinfo *srcres, struct addrinfo *dstres) +{ + struct in6_aliasreq in6_addreq; + + memset(&in6_addreq, 0, sizeof(in6_addreq)); + strncpy(in6_addreq.ifra_name, name, IFNAMSIZ); + memcpy(&in6_addreq.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len); + memcpy(&in6_addreq.ifra_dstaddr, dstres->ai_addr, + dstres->ai_addr->sa_len); + + if (ioctl(s, SIOCSIFPHYADDR_IN6, &in6_addreq) < 0) + warn("SIOCSIFPHYADDR_IN6"); +} + +static void +in6_set_router(int s, int enable) +{ + struct ifreq ifr; + + bzero(&ifr, sizeof (ifr)); + strncpy(ifr.ifr_name, name, IFNAMSIZ); + ifr.ifr_intval = enable; + + if (ioctl(s, SIOCSETROUTERMODE_IN6, &ifr) < 0) + warn("SIOCSETROUTERMODE_IN6"); +} + +static struct cmd inet6_cmds[] = { + DEF_CMD_ARG("prefixlen", setifprefixlen), + DEF_CMD("anycast", IN6_IFF_ANYCAST, setip6flags), + DEF_CMD("tentative", IN6_IFF_TENTATIVE, setip6flags), + DEF_CMD("-tentative", -IN6_IFF_TENTATIVE, setip6flags), + /* RFC 4429, section 3.1, says: + * "Optimistic DAD SHOULD NOT be used for manually entered + * addresses." + * it's not a MUST... + */ + DEF_CMD("optimistic", IN6_IFF_OPTIMISTIC, setip6flags), + DEF_CMD("-optimistic", -IN6_IFF_OPTIMISTIC, setip6flags), + DEF_CMD("deprecated", IN6_IFF_DEPRECATED, setip6flags), + DEF_CMD("-deprecated", -IN6_IFF_DEPRECATED, setip6flags), + DEF_CMD("autoconf", IN6_IFF_AUTOCONF, setip6flags), + DEF_CMD("-autoconf", -IN6_IFF_AUTOCONF, setip6flags), + DEF_CMD("nud", ND6_IFF_PERFORMNUD, setnd6flags), + DEF_CMD("-nud", -ND6_IFF_PERFORMNUD, setnd6flags), + DEF_CMD("ifdisabled", ND6_IFF_IFDISABLED, setnd6flags), + DEF_CMD("-ifdisabled", -ND6_IFF_IFDISABLED, setnd6flags), + DEF_CMD("replicated", ND6_IFF_REPLICATED, setnd6flags), + DEF_CMD("-replicated", -ND6_IFF_REPLICATED, setnd6flags), + DEF_CMD("proxy_prefixes", ND6_IFF_PROXY_PREFIXES, setnd6flags), + DEF_CMD("-proxy_prefixes", -ND6_IFF_PROXY_PREFIXES, setnd6flags), + DEF_CMD("insecure", ND6_IFF_INSECURE, setnd6flags), + DEF_CMD("-insecure", -ND6_IFF_INSECURE, setnd6flags), + DEF_CMD_ARG("pltime", setip6pltime), + DEF_CMD_ARG("vltime", setip6vltime), + DEF_CMD("eui64", 0, setip6eui64), + DEF_CMD("secured", IN6_IFF_SECURED, setip6flags), + DEF_CMD("-secured", -IN6_IFF_SECURED, setip6flags), + DEF_CMD("dad", ND6_IFF_DAD, setnd6flags), + DEF_CMD("-dad", -ND6_IFF_DAD, setnd6flags), +}; + +static struct afswtch af_inet6 = { + .af_name = "inet6", + .af_af = AF_INET6, + .af_status = in6_status, + .af_getaddr = in6_getaddr, + .af_getprefix = in6_getprefix, + .af_other_status = nd6_status, + .af_postproc = in6_postproc, + .af_status_tunnel = in6_status_tunnel, + .af_settunnel = in6_set_tunnel, + .af_setrouter = in6_set_router, + .af_difaddr = SIOCDIFADDR_IN6, + .af_aifaddr = SIOCAIFADDR_IN6, + .af_ridreq = &in6_ridreq, + .af_addreq = &in6_addreq, +}; + +static void +in6_Lopt_cb(const char *optarg __unused) +{ + ip6lifetime++; /* print IPv6 address lifetime */ +} +static struct option in6_Lopt = { "L", "[-L]", in6_Lopt_cb }; + +static __constructor void +inet6_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(inet6_cmds); i++) + cmd_register(&inet6_cmds[i]); + af_register(&af_inet6); + opt_register(&in6_Lopt); +#undef N +} diff --git a/network_cmds-543/ifconfig.tproj/af_link.c b/network_cmds-543/ifconfig.tproj/af_link.c new file mode 100644 index 00000000..ca9444c8 --- /dev/null +++ b/network_cmds-543/ifconfig.tproj/af_link.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Copyright (c) 1983, 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. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ifconfig.h" + +static struct ifreq link_ridreq; + +static void +link_status(int s __unused, const struct ifaddrs *ifa) +{ + /* XXX no const 'cuz LLADDR is defined wrong */ + struct sockaddr_dl *sdl = (struct sockaddr_dl *) ifa->ifa_addr; + + if (sdl != NULL && sdl->sdl_alen > 0) { +#ifdef notyet + if (sdl->sdl_type == IFT_ETHER && + sdl->sdl_alen == ETHER_ADDR_LEN) + printf("\tether %s\n", + ether_ntoa((struct ether_addr *)LLADDR(sdl))); + else { + int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; + + printf("\tlladdr %s\n", link_ntoa(sdl) + n); + } +#else + char *cp = (char *)LLADDR(sdl); + int n = sdl->sdl_alen; + + if (sdl->sdl_type == IFT_ETHER) + printf ("\tether "); + else + printf ("\tlladdr "); + while (--n >= 0) + printf("%02x%c",*cp++ & 0xff, n>0? ':' : ' '); + putchar('\n'); +#endif + } +} + +static void +link_getaddr(const char *addr, int which) +{ + char *temp; + struct sockaddr_dl sdl; + struct sockaddr *sa = &link_ridreq.ifr_addr; + size_t slen = strlen(addr); + + if (which != ADDR) + errx(1, "can't set link-level netmask or broadcast"); + if ((temp = malloc(slen + 2)) == NULL) + errx(1, "malloc failed"); + temp[0] = ':'; + strlcpy(temp + 1, addr, slen + 1); + sdl.sdl_len = sizeof(sdl); + link_addr(temp, &sdl); + free(temp); + if (sdl.sdl_alen > sizeof(sa->sa_data)) + errx(1, "malformed link-level address"); + sa->sa_family = AF_LINK; + sa->sa_len = sdl.sdl_alen; + bcopy(LLADDR(&sdl), sa->sa_data, sdl.sdl_alen); +} + +static struct afswtch af_link = { + .af_name = "link", + .af_af = AF_LINK, + .af_status = link_status, + .af_getaddr = link_getaddr, + .af_aifaddr = SIOCSIFLLADDR, + .af_addreq = &link_ridreq, +}; +static struct afswtch af_ether = { + .af_name = "ether", + .af_af = AF_LINK, + .af_status = link_status, + .af_getaddr = link_getaddr, + .af_aifaddr = SIOCSIFLLADDR, + .af_addreq = &link_ridreq, +}; +static struct afswtch af_lladdr = { + .af_name = "lladdr", + .af_af = AF_LINK, + .af_status = link_status, + .af_getaddr = link_getaddr, + .af_aifaddr = SIOCSIFLLADDR, + .af_addreq = &link_ridreq, +}; + +static __constructor void +link_ctor(void) +{ + af_register(&af_link); + af_register(&af_ether); + af_register(&af_lladdr); +} diff --git a/network_cmds-543/ifconfig.tproj/ifbond.c b/network_cmds-543/ifconfig.tproj/ifbond.c new file mode 100644 index 00000000..b8bcdfe0 --- /dev/null +++ b/network_cmds-543/ifconfig.tproj/ifbond.c @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * ifbond.c + * - add and remove interfaces from a bond interface + */ + +/* + * Modification History: + * + * July 14, 2004 Dieter Siegmund (dieter@apple.com) + * - created + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ifconfig.h" +extern int bond_details; + +#define EA_FORMAT "%02x:%02x:%02x:%02x:%02x:%02x" +#define EA_CH(e, i) ((u_char)((u_char *)(e))[(i)]) +#define EA_LIST(ea) EA_CH(ea,0),EA_CH(ea,1),EA_CH(ea,2),EA_CH(ea,3),EA_CH(ea,4),EA_CH(ea,5) + +static __inline__ const char * +selected_state_string(u_char s) +{ + static const char * names[] = { "unselected", "selected", "standby" }; + + if (s <= IF_BOND_STATUS_SELECTED_STATE_STANDBY) { + return (names[s]); + } + return (""); +} + +static void +bond_print_details(struct if_bond_status * ibs_p, int count) + +{ + int i; + struct if_bond_status * scan_p = ibs_p; + + for (i = 0; i < count; i++, scan_p++) { + struct if_bond_partner_state * ps; + ps = &scan_p->ibs_partner_state; + printf("\tbond interface: %s priority: 0x%04x " + "state: 0x%02x partner system: 0x%04x," + EA_FORMAT " " + "key: 0x%04x port: 0x%04x priority: 0x%04x " + "state: 0x%02x\n", + scan_p->ibs_if_name, scan_p->ibs_port_priority, + scan_p->ibs_state, ps->ibps_system_priority, + EA_LIST(&ps->ibps_system), ps->ibps_key, + ps->ibps_port, ps->ibps_port_priority, + ps->ibps_state); + } + return; +} + +void +bond_status(int s) +{ + int i; + struct if_bond_req ibr; + struct if_bond_status * ibs_p; + struct if_bond_status_req * ibsr_p; + char mode_buf[16]; + const char * mode_str; + + bzero((char *)&ibr, sizeof(ibr)); + ibr.ibr_op = IF_BOND_OP_GET_STATUS; + ibsr_p = &ibr.ibr_ibru.ibru_status; + ibsr_p->ibsr_version = IF_BOND_STATUS_REQ_VERSION; + ifr.ifr_data = (caddr_t)&ibr; + + /* how many of them are there? */ + if (ioctl(s, SIOCGIFBOND, (caddr_t)&ifr) < 0) { + return; + } + switch (ibsr_p->ibsr_mode) { + case IF_BOND_MODE_LACP: + mode_str = "lacp"; + break; + case IF_BOND_MODE_STATIC: + mode_str = "static"; + break; + default: + snprintf(mode_buf, sizeof(mode_buf), "%d", ibsr_p->ibsr_mode); + mode_str = mode_buf; + break; + } + if (ibsr_p->ibsr_total == 0) { + if (bond_details) { + printf("\tbond mode: %s\n" + "\tbond key: 0x%04x interfaces: ", + mode_str, ibsr_p->ibsr_key); + } + else { + printf("\tbond interfaces: \n"); + } + return; + } + ibsr_p->ibsr_buffer + = (char *)malloc(sizeof(struct if_bond_status) + * ibsr_p->ibsr_total); + ibsr_p->ibsr_count = ibsr_p->ibsr_total; + + /* get the list */ + if (ioctl(s, SIOCGIFBOND, (caddr_t)&ifr) < 0) { + goto done; + } + if (ibsr_p->ibsr_total > 0) { + if (bond_details) { + printf("\tbond mode: %s\n" + "\tbond key: 0x%04x interfaces:", + mode_str, ibsr_p->ibsr_key); + } + else { + printf("\tbond interfaces:"); + } + ibs_p = (struct if_bond_status *)ibsr_p->ibsr_buffer; + for (i = 0; i < ibsr_p->ibsr_total; i++, ibs_p++) { + printf(" %s", ibs_p->ibs_if_name); + if (bond_details) { + u_char s = ibs_p->ibs_selected_state; + printf(" (%s)", selected_state_string(s)); + } + } + printf("\n"); + if (bond_details) { + bond_print_details((struct if_bond_status *) + ibsr_p->ibsr_buffer, + ibsr_p->ibsr_total); + } + } + else if (bond_details) { + printf("\tbond mode: %s\n" + "\tbond key: 0x%04x interfaces: \n", + mode_str, ibsr_p->ibsr_key); + } + else { + printf("\tbond interfaces: \n"); + } + + done: + free(ibsr_p->ibsr_buffer); + return; +} + +static +DECL_CMD_FUNC(setbonddev, val, d) +{ + struct if_bond_req ibr; + + bzero((char *)&ibr, sizeof(ibr)); + if ((unsigned int)snprintf(ibr.ibr_ibru.ibru_if_name, + sizeof(ibr.ibr_ibru.ibru_if_name), + "%s", val) >= IFNAMSIZ) { + errx(1, "interface name too long"); + } + ibr.ibr_op = IF_BOND_OP_ADD_INTERFACE; + ifr.ifr_data = (caddr_t)&ibr; + if (ioctl(s, SIOCSIFBOND, (caddr_t)&ifr) == -1) + err(1, "SIOCSIFBOND add interface"); + + return; +} + +static +DECL_CMD_FUNC(unsetbonddev, val, d) +{ + struct if_bond_req ibr; + + bzero((char *)&ibr, sizeof(ibr)); + if ((unsigned int)snprintf(ibr.ibr_ibru.ibru_if_name, + sizeof(ibr.ibr_ibru.ibru_if_name), + "%s", val) >= IFNAMSIZ) { + errx(1, "interface name too long"); + } + ibr.ibr_op = IF_BOND_OP_REMOVE_INTERFACE; + ifr.ifr_data = (caddr_t)&ibr; + if (ioctl(s, SIOCSIFBOND, (caddr_t)&ifr) == -1) + err(1, "SIOCSIFBOND remove interface"); + + return; +} + +static +DECL_CMD_FUNC(setbondmode, val, d) +{ + struct if_bond_req ibr; + int mode; + + if (strcmp(val, "lacp") == 0) { + mode = IF_BOND_MODE_LACP; + } + else if (strcmp(val, "static") == 0) { + mode = IF_BOND_MODE_STATIC; + } + else { + mode = strtoul(val, NULL, 0); + if (errno != 0) { + errx(1, "invalid mode value " + "(must be either \"lacp\" or \"static\")"); + } + } + + bzero((char *)&ibr, sizeof(ibr)); + if ((unsigned int)snprintf(ibr.ibr_ibru.ibru_if_name, + sizeof(ibr.ibr_ibru.ibru_if_name), + "%s", val) >= IFNAMSIZ) { + errx(1, "interface name too long"); + } + ibr.ibr_op = IF_BOND_OP_SET_MODE; + ibr.ibr_ibru.ibru_int_val = mode; + ifr.ifr_data = (caddr_t)&ibr; + if (ioctl(s, SIOCSIFBOND, (caddr_t)&ifr) == -1) + err(1, "SIOCSIFBOND set mode"); + + return; +} + +static struct cmd bond_cmds[] = { + DEF_CLONE_CMD_ARG("bonddev", setbonddev), + DEF_CLONE_CMD_ARG("-bonddev", unsetbonddev), + DEF_CMD_ARG("bondmode", setbondmode), +}; +static struct afswtch af_bond = { + .af_name = "af_bond", + .af_af = AF_UNSPEC, + .af_other_status = bond_status, +}; + +static __constructor void +bond_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(bond_cmds); i++) + cmd_register(&bond_cmds[i]); + af_register(&af_bond); +#undef N +} + diff --git a/network_cmds-543/ifconfig.tproj/ifbridge.c b/network_cmds-543/ifconfig.tproj/ifbridge.c new file mode 100644 index 00000000..0b610cea --- /dev/null +++ b/network_cmds-543/ifconfig.tproj/ifbridge.c @@ -0,0 +1,885 @@ +/* + * Copyright (c) 2009-2014 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/*- + * Copyright 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Jason R. Thorpe for Wasabi Systems, Inc. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * 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 +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ifconfig.h" + +#define PV2ID(pv, epri, eaddr) do { \ + epri = pv >> 48; \ + eaddr[0] = pv >> 40; \ + eaddr[1] = pv >> 32; \ + eaddr[2] = pv >> 24; \ + eaddr[3] = pv >> 16; \ + eaddr[4] = pv >> 8; \ + eaddr[5] = pv >> 0; \ +} while (0) + +static const char *stpstates[] = { + "disabled", + "listening", + "learning", + "forwarding", + "blocking", + "discarding" +}; +static const char *stpproto[] = { + "stp", + "-", + "rstp" +}; +static const char *stproles[] = { + "disabled", + "root", + "designated", + "alternate", + "backup" +}; + +static int +get_val(const char *cp, u_long *valp) +{ + char *endptr; + u_long val; + + errno = 0; + val = strtoul(cp, &endptr, 0); + if (cp[0] == '\0' || endptr[0] != '\0' || errno == ERANGE) + return (-1); + + *valp = val; + return (0); +} + +static int +do_cmd(int sock, u_long op, void *arg, size_t argsize, int set) +{ + struct ifdrv ifd; + + memset(&ifd, 0, sizeof(ifd)); + + strlcpy(ifd.ifd_name, ifr.ifr_name, sizeof(ifd.ifd_name)); + ifd.ifd_cmd = op; + ifd.ifd_len = argsize; + ifd.ifd_data = arg; + + return (ioctl(sock, set ? SIOCSDRVSPEC : SIOCGDRVSPEC, &ifd)); +} + +static void +do_bridgeflag(int sock, const char *ifs, int flag, int set) +{ + struct ifbreq req; + + strlcpy(req.ifbr_ifsname, ifs, sizeof(req.ifbr_ifsname)); + + if (do_cmd(sock, BRDGGIFFLGS, &req, sizeof(req), 0) < 0) + err(1, "unable to get bridge flags"); + + if (set) + req.ifbr_ifsflags |= flag; + else + req.ifbr_ifsflags &= ~flag; + + if (do_cmd(sock, BRDGSIFFLGS, &req, sizeof(req), 1) < 0) + err(1, "unable to set bridge flags"); +} + +static void +bridge_interfaces(int s, const char *prefix) +{ + struct ifbifconf bifc; + struct ifbreq *req; + char *inbuf = NULL, *ninbuf; + char *p, *pad; + int i, len = 8192; + + pad = strdup(prefix); + if (pad == NULL) + err(1, "strdup"); + /* replace the prefix with whitespace */ + for (p = pad; *p != '\0'; p++) { + if(isprint(*p)) + *p = ' '; + } + + for (;;) { + ninbuf = realloc(inbuf, len); + if (ninbuf == NULL) + err(1, "unable to allocate interface buffer"); + bifc.ifbic_len = len; + bifc.ifbic_buf = inbuf = ninbuf; + if (do_cmd(s, BRDGGIFS, &bifc, sizeof(bifc), 0) < 0) + err(1, "unable to get interface list"); + if ((bifc.ifbic_len + sizeof(*req)) < len) + break; + len *= 2; + } + + for (i = 0; i < bifc.ifbic_len / sizeof(*req); i++) { + req = bifc.ifbic_req + i; + printf("%s%s ", prefix, req->ifbr_ifsname); + printb("flags", req->ifbr_ifsflags, IFBIFBITS); + printf("\n"); + + printf("%s", pad); + printf("ifmaxaddr %u", req->ifbr_addrmax); + printf(" port %u priority %u", req->ifbr_portno, + req->ifbr_priority); + printf(" path cost %u", req->ifbr_path_cost); + + if (req->ifbr_ifsflags & IFBIF_STP) { + if (req->ifbr_proto < + sizeof(stpproto) / sizeof(stpproto[0])) + printf(" proto %s", stpproto[req->ifbr_proto]); + else + printf(" ", + req->ifbr_proto); + + printf("\n%s", pad); + if (req->ifbr_role < + sizeof(stproles) / sizeof(stproles[0])) + printf("role %s", stproles[req->ifbr_role]); + else + printf("", + req->ifbr_role); + if (req->ifbr_state < + sizeof(stpstates) / sizeof(stpstates[0])) + printf(" state %s", stpstates[req->ifbr_state]); + else + printf(" ", + req->ifbr_state); + } + printf("\n"); + + if (verbose) { + struct ifbrhostfilter ifbrfh; + struct in_addr in; + struct ether_addr ea; + + bzero(&ifbrfh, sizeof(struct ifbrhostfilter)); + strlcpy(ifbrfh.ifbrhf_ifsname, req->ifbr_ifsname, sizeof(ifbrfh.ifbrhf_ifsname)); + if (do_cmd(s, BRDGGHOSTFILTER, &ifbrfh, sizeof(ifbrfh), 0) < 0) + err(1, "unable to get host filter settings for %s", + ifbrfh.ifbrhf_ifsname); + + if (ifbrfh.ifbrhf_flags & IFBRHF_ENABLED) { + in.s_addr = ifbrfh.ifbrhf_ipsrc; + bcopy(ifbrfh.ifbrhf_hwsrca, ea.octet, ETHER_ADDR_LEN); + } else { + in.s_addr = INADDR_ANY; + bzero(ea.octet, ETHER_ADDR_LEN); + } + printf("%s", pad); + printf("hostfilter %d hw: %s ip: %s", + ifbrfh.ifbrhf_flags & IFBRHF_ENABLED ? 1 : 0, + ether_ntoa(&ea), inet_ntoa(in)); + + printf("\n"); + } + } + + free(inbuf); + free(pad); +} + +static void +bridge_addresses(int s, const char *prefix) +{ + struct ifbaconf ifbac; + struct ifbareq *ifba; + char *inbuf = NULL, *ninbuf; + int i, len = 8192; + struct ether_addr ea; + + for (;;) { + ninbuf = realloc(inbuf, len); + if (ninbuf == NULL) + err(1, "unable to allocate address buffer"); + ifbac.ifbac_len = len; + ifbac.ifbac_buf = inbuf = ninbuf; + if (do_cmd(s, BRDGRTS, &ifbac, sizeof(ifbac), 0) < 0) + err(1, "unable to get address cache"); + if ((ifbac.ifbac_len + sizeof(*ifba)) < len) + break; + len *= 2; + } + + for (i = 0; i < ifbac.ifbac_len / sizeof(*ifba); i++) { + ifba = ifbac.ifbac_req + i; + memcpy(ea.octet, ifba->ifba_dst, + sizeof(ea.octet)); + printf("%s%s Vlan%d %s %lu ", prefix, ether_ntoa(&ea), + ifba->ifba_vlan, ifba->ifba_ifsname, ifba->ifba_expire); + printb("flags", ifba->ifba_flags, IFBAFBITS); + printf("\n"); + } + + free(inbuf); +} + +static void +bridge_status(int s) +{ + struct ifbropreq ifbp; + struct ifbrparam param; + u_int16_t pri; + u_int8_t ht, fd, ma, hc, pro; + u_int8_t lladdr[ETHER_ADDR_LEN]; + u_int16_t bprio; + u_int32_t csize, ctime; + u_int32_t ipfflags; + + if (do_cmd(s, BRDGGCACHE, ¶m, sizeof(param), 0) < 0) + return; + csize = param.ifbrp_csize; + if (do_cmd(s, BRDGGTO, ¶m, sizeof(param), 0) < 0) + return; + ctime = param.ifbrp_ctime; + if (do_cmd(s, BRDGGFILT, ¶m, sizeof(param), 0) < 0) + return; + ipfflags = param.ifbrp_filter; + if (do_cmd(s, BRDGPARAM, &ifbp, sizeof(ifbp), 0) < 0) + return; + pri = ifbp.ifbop_priority; + pro = ifbp.ifbop_protocol; + ht = ifbp.ifbop_hellotime; + fd = ifbp.ifbop_fwddelay; + hc = ifbp.ifbop_holdcount; + ma = ifbp.ifbop_maxage; + + printf("\tConfiguration:\n"); + PV2ID(ifbp.ifbop_bridgeid, bprio, lladdr); + printf("\t\tid %s priority %u hellotime %u fwddelay %u\n", + ether_ntoa((struct ether_addr *)lladdr), pri, ht, fd); + printf("\t\tmaxage %u holdcnt %u proto %s maxaddr %u timeout %u\n", + ma, hc, stpproto[pro], csize, ctime); + + PV2ID(ifbp.ifbop_designated_root, bprio, lladdr); + printf("\t\troot id %s priority %d ifcost %u port %u\n", + ether_ntoa((struct ether_addr *)lladdr), bprio, + ifbp.ifbop_root_path_cost, ifbp.ifbop_root_port & 0xfff); + + printf("\t\tipfilter %s flags 0x%x\n", + (ipfflags & IFBF_FILT_USEIPF) ? "enabled" : "disabled", ipfflags); + + bridge_interfaces(s, "\tmember: "); + + if (!all || verbose > 1) { + printf("\tAddress cache:\n"); + bridge_addresses(s, "\t\t"); + } + return; + +} + +static void +setbridge_add(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); + if (do_cmd(s, BRDGADD, &req, sizeof(req), 1) < 0) + err(1, "BRDGADD %s", val); +} + +static void +setbridge_delete(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); + if (do_cmd(s, BRDGDEL, &req, sizeof(req), 1) < 0) + err(1, "BRDGDEL %s", val); +} + +static void +setbridge_discover(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_DISCOVER, 1); +} + +static void +unsetbridge_discover(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_DISCOVER, 0); +} + +static void +setbridge_learn(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_LEARNING, 1); +} + +static void +unsetbridge_learn(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_LEARNING, 0); +} + +#ifdef notdef +static void +setbridge_sticky(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_STICKY, 1); +} + +static void +unsetbridge_sticky(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_STICKY, 0); +} + +static void +setbridge_span(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); + if (do_cmd(s, BRDGADDS, &req, sizeof(req), 1) < 0) + err(1, "BRDGADDS %s", val); +} + +static void +unsetbridge_span(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); + if (do_cmd(s, BRDGDELS, &req, sizeof(req), 1) < 0) + err(1, "BRDGDELS %s", val); +} +#endif + +static void +setbridge_stp(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_STP, 1); +} + +static void +unsetbridge_stp(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_STP, 0); +} + +#ifdef notdef +static void +setbridge_edge(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_EDGE, 1); +} + +static void +unsetbridge_edge(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_EDGE, 0); +} + +static void +setbridge_autoedge(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_AUTOEDGE, 1); +} + +static void +unsetbridge_autoedge(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_AUTOEDGE, 0); +} + +static void +setbridge_ptp(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_PTP, 1); +} + +static void +unsetbridge_ptp(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_PTP, 0); +} + +static void +setbridge_autoptp(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_AUTOPTP, 1); +} + +static void +unsetbridge_autoptp(const char *val, int d, int s, const struct afswtch *afp) +{ + do_bridgeflag(s, val, IFBIF_BSTP_AUTOPTP, 0); +} +#endif + +static void +setbridge_flush(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + req.ifbr_ifsflags = IFBF_FLUSHDYN; + if (do_cmd(s, BRDGFLUSH, &req, sizeof(req), 1) < 0) + err(1, "BRDGFLUSH"); +} + +static void +setbridge_flushall(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + req.ifbr_ifsflags = IFBF_FLUSHALL; + if (do_cmd(s, BRDGFLUSH, &req, sizeof(req), 1) < 0) + err(1, "BRDGFLUSH"); +} + +static void +setbridge_static(const char *val, const char *mac, int s, + const struct afswtch *afp) +{ + struct ifbareq req; + struct ether_addr *ea; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifba_ifsname, val, sizeof(req.ifba_ifsname)); + + ea = ether_aton(mac); + if (ea == NULL) + errx(1, "%s: invalid address: %s", val, mac); + + memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst)); + req.ifba_flags = IFBAF_STATIC; + req.ifba_vlan = 1; /* XXX allow user to specify */ + + if (do_cmd(s, BRDGSADDR, &req, sizeof(req), 1) < 0) + err(1, "BRDGSADDR %s", val); +} + +static void +setbridge_deladdr(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbareq req; + struct ether_addr *ea; + + memset(&req, 0, sizeof(req)); + + ea = ether_aton(val); + if (ea == NULL) + errx(1, "invalid address: %s", val); + + memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst)); + + if (do_cmd(s, BRDGDADDR, &req, sizeof(req), 1) < 0) + err(1, "BRDGDADDR %s", val); +} + +static void +setbridge_addr(const char *val, int d, int s, const struct afswtch *afp) +{ + + bridge_addresses(s, ""); +} + +static void +setbridge_maxaddr(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_csize = val & 0xffffffff; + + if (do_cmd(s, BRDGSCACHE, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSCACHE %s", arg); +} + +static void +setbridge_hellotime(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_hellotime = val & 0xff; + + if (do_cmd(s, BRDGSHT, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSHT %s", arg); +} + +static void +setbridge_fwddelay(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_fwddelay = val & 0xff; + + if (do_cmd(s, BRDGSFD, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSFD %s", arg); +} + +static void +setbridge_maxage(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_maxage = val & 0xff; + + if (do_cmd(s, BRDGSMA, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSMA %s", arg); +} + +static void +setbridge_priority(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xffff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_prio = val & 0xffff; + + if (do_cmd(s, BRDGSPRI, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSPRI %s", arg); +} + +#ifdef notdef +static void +setbridge_protocol(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + + if (strcasecmp(arg, "stp") == 0) { + param.ifbrp_proto = 0; + } else if (strcasecmp(arg, "rstp") == 0) { + param.ifbrp_proto = 2; + } else { + errx(1, "unknown stp protocol"); + } + + if (do_cmd(s, BRDGSPROTO, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSPROTO %s", arg); +} + +static void +setbridge_holdcount(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_txhc = val & 0xff; + + if (do_cmd(s, BRDGSTXHC, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSTXHC %s", arg); +} +#endif + +static void +setbridge_ifpriority(const char *ifn, const char *pri, int s, + const struct afswtch *afp) +{ + struct ifbreq req; + u_long val; + + memset(&req, 0, sizeof(req)); + + if (get_val(pri, &val) < 0 || (val & ~0xff) != 0) + errx(1, "invalid value: %s", pri); + + strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); + req.ifbr_priority = val & 0xff; + + if (do_cmd(s, BRDGSIFPRIO, &req, sizeof(req), 1) < 0) + err(1, "BRDGSIFPRIO %s", pri); +} + +static void +setbridge_ifpathcost(const char *ifn, const char *cost, int s, + const struct afswtch *afp) +{ + struct ifbreq req; + u_long val; + + memset(&req, 0, sizeof(req)); + + if (get_val(cost, &val) < 0) + errx(1, "invalid value: %s", cost); + + strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); + req.ifbr_path_cost = val; + + if (do_cmd(s, BRDGSIFCOST, &req, sizeof(req), 1) < 0) + err(1, "BRDGSIFCOST %s", cost); +} + +#ifdef notdef +static void +setbridge_ifmaxaddr(const char *ifn, const char *arg, int s, + const struct afswtch *afp) +{ + struct ifbreq req; + u_long val; + + memset(&req, 0, sizeof(req)); + + if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0) + errx(1, "invalid value: %s", arg); + + strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); + req.ifbr_addrmax = val & 0xffffffff; + + if (do_cmd(s, BRDGSIFAMAX, &req, sizeof(req), 1) < 0) + err(1, "BRDGSIFAMAX %s", arg); +} +#endif + +static void +setbridge_timeout(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_ctime = val & 0xffffffff; + + if (do_cmd(s, BRDGSTO, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSTO %s", arg); +} + +#ifdef notdef +static void +setbridge_private(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_PRIVATE, 1); +} + +static void +unsetbridge_private(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_PRIVATE, 0); +} +#endif + + +static void +setbridge_hostfilter(const char *ifn, const char *addr, int s, + const struct afswtch *afp) +{ + struct ifbrhostfilter req; + struct ether_addr *ea; + struct in_addr in; + + memset(&req, 0, sizeof(req)); + req.ifbrhf_flags = IFBRHF_ENABLED; + + strlcpy(req.ifbrhf_ifsname, ifn, sizeof(req.ifbrhf_ifsname)); + + ea = ether_aton(addr); + if (ea != NULL) { + req.ifbrhf_flags |= IFBRHF_HWSRC; + bcopy(ea, req.ifbrhf_hwsrca, sizeof(req.ifbrhf_hwsrca)); + } else if (inet_aton(addr, &in) != 0) { + req.ifbrhf_flags |= IFBRHF_IPSRC; + req.ifbrhf_ipsrc = in.s_addr; + } else + errx(1, "invalid address: %s", addr); + + if (do_cmd(s, BRDGSHOSTFILTER, &req, sizeof(req), 1) < 0) + err(1, "BRDGSHOSTFILTER %s %s", ifn, addr); +} + +static void +unsetbridge_hostfilter(const char *ifn, int d, int s, const struct afswtch *afp) +{ + struct ifbrhostfilter req; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifbrhf_ifsname, ifn, sizeof(req.ifbrhf_ifsname)); + + if (do_cmd(s, BRDGSHOSTFILTER, &req, sizeof(req), 1) < 0) + err(1, "BRDGSHOSTFILTER"); +} + +static struct cmd bridge_cmds[] = { + DEF_CMD_ARG("addm", setbridge_add), + DEF_CMD_ARG("deletem", setbridge_delete), + DEF_CMD_ARG("discover", setbridge_discover), + DEF_CMD_ARG("-discover", unsetbridge_discover), + DEF_CMD_ARG("learn", setbridge_learn), + DEF_CMD_ARG("-learn", unsetbridge_learn), +#ifdef notdef + DEF_CMD_ARG("sticky", setbridge_sticky), + DEF_CMD_ARG("-sticky", unsetbridge_sticky), + DEF_CMD_ARG("span", setbridge_span), + DEF_CMD_ARG("-span", unsetbridge_span), +#endif + DEF_CMD_ARG("stp", setbridge_stp), + DEF_CMD_ARG("-stp", unsetbridge_stp), +#ifdef notdef + DEF_CMD_ARG("edge", setbridge_edge), + DEF_CMD_ARG("-edge", unsetbridge_edge), + DEF_CMD_ARG("autoedge", setbridge_autoedge), + DEF_CMD_ARG("-autoedge", unsetbridge_autoedge), + DEF_CMD_ARG("ptp", setbridge_ptp), + DEF_CMD_ARG("-ptp", unsetbridge_ptp), + DEF_CMD_ARG("autoptp", setbridge_autoptp), + DEF_CMD_ARG("-autoptp", unsetbridge_autoptp), +#endif + DEF_CMD("flush", 0, setbridge_flush), + DEF_CMD("flushall", 0, setbridge_flushall), + DEF_CMD_ARG2("static", setbridge_static), + DEF_CMD_ARG("deladdr", setbridge_deladdr), + DEF_CMD("addr", 1, setbridge_addr), + DEF_CMD_ARG("maxaddr", setbridge_maxaddr), + DEF_CMD_ARG("hellotime", setbridge_hellotime), + DEF_CMD_ARG("fwddelay", setbridge_fwddelay), + DEF_CMD_ARG("maxage", setbridge_maxage), + DEF_CMD_ARG("priority", setbridge_priority), +#ifdef notdef + DEF_CMD_ARG("proto", setbridge_protocol), + DEF_CMD_ARG("holdcnt", setbridge_holdcount), +#endif + DEF_CMD_ARG2("ifpriority", setbridge_ifpriority), + DEF_CMD_ARG2("ifpathcost", setbridge_ifpathcost), +#ifdef notdef + DEF_CMD_ARG2("ifmaxaddr", setbridge_ifmaxaddr), +#endif + DEF_CMD_ARG("timeout", setbridge_timeout), +#ifdef notdef + DEF_CMD_ARG("private", setbridge_private), + DEF_CMD_ARG("-private", unsetbridge_private), +#endif + DEF_CMD_ARG2("hostfilter", setbridge_hostfilter), + DEF_CMD_ARG("-hostfilter", unsetbridge_hostfilter), +}; +static struct afswtch af_bridge = { + .af_name = "af_bridge", + .af_af = AF_UNSPEC, + .af_other_status = bridge_status, +}; + +static __constructor void +bridge_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(bridge_cmds); i++) + cmd_register(&bridge_cmds[i]); + af_register(&af_bridge); +#undef N +} diff --git a/network_cmds-543/ifconfig.tproj/ifclone.c b/network_cmds-543/ifconfig.tproj/ifclone.c new file mode 100644 index 00000000..f6b8c072 --- /dev/null +++ b/network_cmds-543/ifconfig.tproj/ifclone.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 1983, 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. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ifconfig.h" + +static void +list_cloners(void) +{ + struct if_clonereq ifcr; + char *cp, *buf; + int idx; + int s; + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s == -1) + err(1, "socket(AF_INET,SOCK_DGRAM)"); + + memset(&ifcr, 0, sizeof(ifcr)); + + if (ioctl(s, SIOCIFGCLONERS, &ifcr) < 0) + err(1, "SIOCIFGCLONERS for count"); + + buf = malloc(ifcr.ifcr_total * IFNAMSIZ); + if (buf == NULL) + err(1, "unable to allocate cloner name buffer"); + + ifcr.ifcr_count = ifcr.ifcr_total; + ifcr.ifcr_buffer = buf; + + if (ioctl(s, SIOCIFGCLONERS, &ifcr) < 0) + err(1, "SIOCIFGCLONERS for names"); + + /* + * In case some disappeared in the mean time, clamp it down. + */ + if (ifcr.ifcr_count > ifcr.ifcr_total) + ifcr.ifcr_count = ifcr.ifcr_total; + + for (cp = buf, idx = 0; idx < ifcr.ifcr_count; idx++, cp += IFNAMSIZ) { + if (idx > 0) + putchar(' '); + printf("%s", cp); + } + + putchar('\n'); + free(buf); +} + +static clone_callback_func *clone_cb = NULL; + +void +clone_setcallback(clone_callback_func *p) +{ + if (clone_cb != NULL && clone_cb != p) + errx(1, "conflicting device create parameters"); + clone_cb = p; +} + +/* + * Do the actual clone operation. Any parameters must have been + * setup by now. If a callback has been setup to do the work + * then defer to it; otherwise do a simple create operation with + * no parameters. + */ +static void +ifclonecreate(int s, void *arg) +{ + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + (void) strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + if (clone_cb == NULL) { +#ifdef SIOCIFCREATE2 + /* NB: no parameters */ + if (ioctl(s, SIOCIFCREATE2, &ifr) < 0) + err(1, "SIOCIFCREATE2"); +#else + if (ioctl(s, SIOCIFCREATE, &ifr) < 0) + err(1, "SIOCIFCREATE"); +#endif + } else { + clone_cb(s, &ifr); + } + + /* + * If we get a different name back than we put in, print it. + */ + if (strncmp(name, ifr.ifr_name, sizeof(name)) != 0) { + strlcpy(name, ifr.ifr_name, sizeof(name)); + printf("%s\n", name); + } +} + +static +DECL_CMD_FUNC(clone_create, arg, d) +{ + callback_register(ifclonecreate, NULL); +} + +static +DECL_CMD_FUNC(clone_destroy, arg, d) +{ + (void) strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) + err(1, "SIOCIFDESTROY"); +} + +static struct cmd clone_cmds[] = { + DEF_CLONE_CMD("create", 0, clone_create), + DEF_CMD("destroy", 0, clone_destroy), + DEF_CLONE_CMD("plumb", 0, clone_create), + DEF_CMD("unplumb", 0, clone_destroy), +}; + +static void +clone_Copt_cb(const char *optarg __unused) +{ + list_cloners(); + exit(0); +} +static struct option clone_Copt = { "C", "[-C]", clone_Copt_cb }; + +static __constructor void +clone_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(clone_cmds); i++) + cmd_register(&clone_cmds[i]); + opt_register(&clone_Copt); +#undef N +} diff --git a/network_cmds-543/ifconfig.tproj/ifconfig.8 b/network_cmds-543/ifconfig.tproj/ifconfig.8 new file mode 100644 index 00000000..9b06f04f --- /dev/null +++ b/network_cmds-543/ifconfig.tproj/ifconfig.8 @@ -0,0 +1,1109 @@ +.\" Copyright (c) 2013 Apple Inc. All rights reserved. +.\" +.\" @APPLE_OSREFERENCE_LICENSE_HEADER_START@ +.\" +.\" This file contains Original Code and/or Modifications of Original Code +.\" as defined in and that are subject to the Apple Public Source License +.\" Version 2.0 (the 'License'). You may not use this file except in +.\" compliance with the License. The rights granted to you under the License +.\" may not be used to create, or enable the creation or redistribution of, +.\" unlawful or unlicensed copies of an Apple operating system, or to +.\" circumvent, violate, or enable the circumvention or violation of, any +.\" terms of an Apple operating system software license agreement. +.\" +.\" Please obtain a copy of the License at +.\" http://www.opensource.apple.com/apsl/ and read it before using this file. +.\" +.\" The Original Code and all software distributed under the License are +.\" distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +.\" EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +.\" INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +.\" FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +.\" Please see the License for the specific language governing rights and +.\" limitations under the License. +.\" +.\" @APPLE_OSREFERENCE_LICENSE_HEADER_END@ +.\" +.\" Copyright (c) 1983, 1991, 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. +.\" +.\" From: @(#)ifconfig.8 8.3 (Berkeley) 1/5/94 +.\" $FreeBSD: src/sbin/ifconfig/ifconfig.8,v 1.142.2.6.2.1 2008/11/25 02:59:29 kensmith Exp $ +.\" +.Dd June 20, 2008 +.Dt IFCONFIG 8 +.Os +.Sh NAME +.Nm ifconfig +.Nd configure network interface parameters +.Sh SYNOPSIS +.Nm +.Op Fl L +.Op Fl m +.Op Fl r +.Ar interface +.Op Cm create +.Op Ar address_family +.Oo +.Ar address +.Op Ar dest_address +.Oc +.Op Ar parameters +.Nm +.Ar interface +.Cm destroy +.Nm +.Fl a +.Op Fl L +.Op Fl d +.Op Fl m +.Op Fl r +.Op Fl u +.Op Fl v +.Op Ar address_family +.Nm +.Fl l +.Op Fl d +.Op Fl u +.Op Ar address_family +.Nm +.Op Fl L +.Op Fl d +.Op Fl m +.Op Fl r +.Op Fl u +.Op Fl v +.Op Fl C +.Nm +.Ar interface +.Cm vlan +.Ar vlan-tag +.Cm vlandev +.Ar iface +.Nm +.Ar interface +.Cm -vlandev +.Ar iface +.Nm +.Ar interface +.Cm bonddev +.Ar iface +.Nm +.Ar interface +.Cm -bonddev +.Ar iface +.Nm +.Ar interface +.Cm bondmode +.Ar lacp | static +.Sh DESCRIPTION +The +.Nm +utility is used to assign an address +to a network interface and/or configure +network interface parameters. +.Pp +The following options are available: +.Bl -tag -width indent +.It Ar address +For the +.Tn DARPA Ns -Internet +family, +the address is either a host name present in the host name data +base, +.Xr hosts 5 , +or a +.Tn DARPA +Internet address expressed in the Internet standard +.Dq dot notation . +.Pp +It is also possible to use the CIDR notation (also known as the +slash notation) to include the netmask. +That is, one can specify an address like +.Li 192.168.0.1/16 . +.Pp +For the +.Dq inet6 +family, it is also possible to specify the prefix length using the slash +notation, like +.Li ::1/128 . +See the +.Cm prefixlen +parameter below for more information. +.\" For the Xerox Network Systems(tm) family, +.\" addresses are +.\" .Ar net:a.b.c.d.e.f , +.\" where +.\" .Ar net +.\" is the assigned network number (in decimal), +.\" and each of the six bytes of the host number, +.\" .Ar a +.\" through +.\" .Ar f , +.\" are specified in hexadecimal. +.\" The host number may be omitted on IEEE 802 protocol +.\" (Ethernet, FDDI, and Token Ring) interfaces, +.\" which use the hardware physical address, +.\" and on interfaces other than the first. +.\" For the +.\" .Tn ISO +.\" family, addresses are specified as a long hexadecimal string, +.\" as in the Xerox family. +.\" However, two consecutive dots imply a zero +.\" byte, and the dots are optional, if the user wishes to (carefully) +.\" count out long strings of digits in network byte order. +.Pp +The link-level +.Pq Dq link +address +is specified as a series of colon-separated hex digits. +This can be used to +e.g.\& set a new MAC address on an ethernet interface, though the +mechanism used is not ethernet-specific. +If the interface is already +up when this option is used, it will be briefly brought down and +then brought back up again in order to ensure that the receive +filter in the underlying ethernet hardware is properly reprogrammed. +.It Ar address_family +Specify the +address family +which affects interpretation of the remaining parameters. +Since an interface can receive transmissions in differing protocols +with different naming schemes, specifying the address family is recommended. +The address or protocol families currently +supported are +.Dq inet , +.Dq inet6 , +.\".Dq atalk , +.\".Dq ipx , +.\" .Dq iso , +and +.Dq link . +.\" and +.\" .Dq ns . +The default is +.Dq inet . +.Dq ether +and +.Dq lladdr +are synonyms for +.Dq link . +.It Ar dest_address +Specify the address of the correspondent on the other end +of a point to point link. +.It Ar interface +This +parameter is a string of the form +.Dq name unit , +for example, +.Dq Li en0 . +\.El +.Pp +The following parameters may be set with +.Nm : +.Bl -tag -width indent +.It Cm add +Another name for the +.Cm alias +parameter. +Introduced for compatibility +with +.Bsx . +.It Cm alias +Establish an additional network address for this interface. +This is sometimes useful when changing network numbers, and +one wishes to accept packets addressed to the old interface. +If the address is on the same subnet as the first network address +for this interface, a non-conflicting netmask must be given. +Usually +.Li 0xffffffff +is most appropriate. +.It Fl alias +Remove the network address specified. +This would be used if you incorrectly specified an alias, or it +was no longer needed. +If you have incorrectly set an NS address having the side effect +of specifying the host portion, removing all NS addresses will +allow you to respecify the host portion. +.It Cm anycast +(Inet6 only.) +Specify that the address configured is an anycast address. +Based on the current specification, +only routers may configure anycast addresses. +Anycast address will not be used as source address of any of outgoing +IPv6 packets. +.It Cm arp +Enable the use of the Address Resolution Protocol +.Pq Xr arp 4 +in mapping +between network level addresses and link level addresses (default). +This is currently implemented for mapping between +.Tn DARPA +Internet +addresses and +.Tn IEEE +802 48-bit MAC addresses (Ethernet, FDDI, and Token Ring addresses). +.It Fl arp +Disable the use of the Address Resolution Protocol +.Pq Xr arp 4 . +.It Cm broadcast +(Inet only.) +Specify the address to use to represent broadcasts to the +network. +The default broadcast address is the address with a host part of all 1's. +.It Cm debug +Enable driver dependent debugging code; usually, this turns on +extra console error logging. +.It Fl debug +Disable driver dependent debugging code. +.It Cm delete +Another name for the +.Fl alias +parameter. +.It Cm down +Mark an interface +.Dq down . +When an interface is marked +.Dq down , +the system will not attempt to +transmit messages through that interface. +If possible, the interface will be reset to disable reception as well. +.It Cm ether +Another name for the +.Cm lladdr +parameter. +.\" .It Cm ipdst +.\" This is used to specify an Internet host who is willing to receive +.\" ip packets encapsulating NS packets bound for a remote network. +.\" An apparent point to point link is constructed, and +.\" the address specified will be taken as the NS address and network +.\" of the destination. +.\" IP encapsulation of +.\" .Tn CLNP +.\" packets is done differently. +.It Cm lladdr Ar addr +Set the link-level address on an interface. +This can be used to +e.g. set a new MAC address on an ethernet interface, though the +mechanism used is not ethernet-specific. +The address +.Ar addr +is specified as a series of colon-separated hex digits. +If the interface is already +up when this option is used, it will be briefly brought down and +then brought back up again in order to ensure that the receive +filter in the underlying ethernet hardware is properly reprogrammed. +.It Cm media Ar type +If the driver supports the media selection system, set the media type +of the interface to +.Ar type . +Some interfaces support the mutually exclusive use of one of several +different physical media connectors. +For example, a 10Mbit/s Ethernet +interface might support the use of either +.Tn AUI +or twisted pair connectors. +Setting the media type to +.Cm 10base5/AUI +would change the currently active connector to the AUI port. +Setting it to +.Cm 10baseT/UTP +would activate twisted pair. +Refer to the interfaces' driver +specific documentation or man page for a complete list of the +available types. +.It Cm mediaopt Ar opts +If the driver supports the media selection system, set the specified +media options on the interface. +The +.Ar opts +argument +is a comma delimited list of options to apply to the interface. +Refer to the interfaces' driver specific man page for a complete +list of available options. +.It Fl mediaopt Ar opts +If the driver supports the media selection system, disable the +specified media options on the interface. +.It Cm rxcsum , txcsum +If the driver supports user-configurable checksum offloading, +enable receive (or transmit) checksum offloading on the interface. +Some drivers may not be able to enable these flags independently +of each other, so setting one may also set the other. +The driver will offload as much checksum work as it can reliably +support, the exact level of offloading varies between drivers. +.It Fl rxcsum , txcsum +If the driver supports user-configurable checksum offloading, +disable receive (or transmit) checksum offloading on the interface. +These settings may not always be independent of each other. +.It Cm tso +If the driver supports +.Xr tcp 4 +segmentation offloading, enable TSO on the interface. +Some drivers may not be able to support TSO for +.Xr ip 4 +and +.Xr ip6 4 +packets, so they may enable only one of them. +.It Fl tso +If the driver supports +.Xr tcp 4 +segmentation offloading, disable TSO on the interface. +It will always disable TSO for +.Xr ip 4 +and +.Xr ip6 4 . +.It Cm lro +If the driver supports +.Xr tcp 4 +large receive offloading, enable LRO on the interface. +.It Fl lro +If the driver supports +.Xr tcp 4 +large receive offloading, disable LRO on the interface. +.It Cm av +If supported by the driver, enable 802.1 AVB on the interface. +.It Fl av +If supported by the driver, disable 802.1 AVB on the interface. +.It Cm vlanmtu , vlanhwtag +If the driver offers user-configurable VLAN support, enable +reception of extended frames or tag processing in hardware, +respectively. +Note that this must be issued on a physical interface associated with +.Xr vlan 4 , +not on a +.Xr vlan 4 +interface itself. +.It Fl vlanmtu , vlanhwtag +If the driver offers user-configurable VLAN support, disable +reception of extended frames or tag processing in hardware, +respectively. +.It Cm create +Create the specified network pseudo-device. +If the interface is given without a unit number, try to create a new +device with an arbitrary unit number. +If creation of an arbitrary device is successful, the new device name is +printed to standard output unless the interface is renamed or destroyed +in the same +.Nm +invocation. +.It Cm destroy +Destroy the specified network pseudo-device. +.It Cm plumb +Another name for the +.Cm create +parameter. +Included for +.Tn Solaris +compatibility. +.It Cm unplumb +Another name for the +.Cm destroy +parameter. +Included for +.Tn Solaris +compatibility. +.It Cm metric Ar n +Set the routing metric of the interface to +.Ar n , +default 0. +The routing metric is used by the routing protocol +.Pq Xr routed 8 . +Higher metrics have the effect of making a route +less favorable; metrics are counted as additional hops +to the destination network or host. +.It Cm mtu Ar n +Set the maximum transmission unit of the interface to +.Ar n , +default is interface specific. +The MTU is used to limit the size of packets that are transmitted on an +interface. +Not all interfaces support setting the MTU, and some interfaces have +range restrictions. +.It Cm netmask Ar mask +.\" (Inet and ISO.) +(Inet only.) +Specify how much of the address to reserve for subdividing +networks into sub-networks. +The mask includes the network part of the local address +and the subnet part, which is taken from the host field of the address. +The mask can be specified as a single hexadecimal number +with a leading +.Ql 0x , +with a dot-notation Internet address, +or with a pseudo-network name listed in the network table +.Xr networks 5 . +The mask contains 1's for the bit positions in the 32-bit address +which are to be used for the network and subnet parts, +and 0's for the host part. +The mask should contain at least the standard network portion, +and the subnet field should be contiguous with the network +portion. +.Pp +The netmask can also be specified in CIDR notation after the address. +See the +.Ar address +option above for more information. +.It Cm prefixlen Ar len +(Inet6 only.) +Specify that +.Ar len +bits are reserved for subdividing networks into sub-networks. +The +.Ar len +must be integer, and for syntactical reason it must be between 0 to 128. +It is almost always 64 under the current IPv6 assignment rule. +If the parameter is omitted, 64 is used. +.Pp +The prefix can also be specified using the slash notation after the address. +See the +.Ar address +option above for more information. +.\" see +.\" Xr eon 5 . +.\" .It Cm nsellength Ar n +.\" .Pf ( Tn ISO +.\" only) +.\" This specifies a trailing number of bytes for a received +.\" .Tn NSAP +.\" used for local identification, the remaining leading part of which is +.\" taken to be the +.\" .Tn NET +.\" (Network Entity Title). +.\" The default value is 1, which is conformant to US +.\" .Tn GOSIP . +.\" When an ISO address is set in an ifconfig command, +.\" it is really the +.\" .Tn NSAP +.\" which is being specified. +.\" For example, in +.\" .Tn US GOSIP , +.\" 20 hex digits should be +.\" specified in the +.\" .Tn ISO NSAP +.\" to be assigned to the interface. +.\" There is some evidence that a number different from 1 may be useful +.\" for +.\" .Tn AFI +.\" 37 type addresses. +.It Cm remove +Another name for the +.Fl alias +parameter. +Introduced for compatibility +with +.Bsx . +.Sm off +.It Cm link Op Cm 0 No - Cm 2 +.Sm on +Enable special processing of the link level of the interface. +These three options are interface specific in actual effect, however, +they are in general used to select special modes of operation. +An example +of this is to enable SLIP compression, or to select the connector type +for some Ethernet cards. +Refer to the man page for the specific driver +for more information. +.Sm off +.It Fl link Op Cm 0 No - Cm 2 +.Sm on +Disable special processing at the link level with the specified interface. +.It Cm up +Mark an interface +.Dq up . +This may be used to enable an interface after an +.Dq Nm Cm down . +It happens automatically when setting the first address on an interface. +If the interface was reset when previously marked down, +the hardware will be re-initialized. +.El +.Pp +The following parameters are for ICMPv6 Neighbor Discovery Protocol. +Note that the address family keyword +.Dq Li inet6 +is needed for them: +.Bl -tag -width indent +.It Cm nud +Perform network unreachability detection (NUD). +.It Cm -nud +Do not perform network unreachability detection (NUD). +.It Cm ifdisabled +Disable all IPv6 communication on the interface. +.It Cm -ifdisabled +Do not disable all IPv6 communication on the interface. +.It Cm insecure +Disable the processing of Secure Neighbor Discovery (SEND). +.It Cm -insecure +Do not disabled the processing of Secure Neighbor Discovery (SEND). +.It Cm dad +Perform duplicate address detection (DAD). +.It Cm -dad +Do not perform duplicate address detection (DAD). +.It Cm replicated +Modify duplicate address detection (DAD) protocol to expect that interface +configuration is replicated at a network sleep proxy. Ignores certain NA +messages and disables optimistic DAD. +.It Cm -replicated +Do not use modified duplicated address detection (DAD) protocol. +.El +.Pp +The following parameters are specific to link aggregate interfaces: +.Bl -tag -width indent +.It Cm bonddev Ar iface +If the interface is a bond pseudo device, associate physical interface +.Ar iface +with it. By default, the bond pseudo device is in LACP +(Link Aggregation Control Protocol) mode (see \fBbondmode\fR below). In +this mode, the device conforms to the IEEE 802.3ad Link Aggregation +specification. +.Pp +If this is the first physical interface to be associated with the bond +interface, the bond interface inherits the ethernet address from the +physical interface. Physical interfaces that are added to the bond have +their ethernet address re-programmed so that all members of the bond have +the same ethernet address. If the physical interface is subsequently +removed from the bond using +.Fl bonddev , +a new ethernet address is chosen from the remaining interfaces, and all +interfaces are re-programmed again with the new ethernet address. If no +remaining interfaces exist, the bond interface's ethernet address is cleared. +.Pp +If the specified physical interface +.Ar iface +is not capable of having its ethernet address re-programmed, the +.Cm bonddev +command will fail. +.Pp +Once the physical interface +.Ar iface +is successfully associated with the bond interface, all received packets +are diverted to the bond interface. The physical interface is no longer +useable on its own, and remains that way until it is removed from the bond using +.Fl bonddev . +.Pp +It is possible that the specified interface +.Ar iface +is not capable of aggregating, and may remain unused until the operating +conditions change. +.Pp +The link status of the bond interface depends on the state of link aggregation. +If no active partner is detected, the link status will remain inactive. +.Pp +To monitor the 802.3ad Link Aggregation state, use the +.Fl b +option. +.Pp +A physical interface that is associated with a vlan pseudo device cannot +at the same time be associated with a bond pseudo device. A physical interface +cannot be associated with more than one bond pseudo device at the same time. +.Pp +It is not possible to associate a bond with pseudo interfaces such as vlan. +Only physical ethernet interfaces may be associated with a bond. +.It Fl bonddev Ar iface +If the interface is a bond pseudo device, disassociate the physical interface +.Ar iface +from it. Before the interface is removed from the bond, the bond device +announces to the link partner that the interface is now individual and +no longer aggregatable. +If the physical +.Ar iface +is the last interface in the bond, the bond interface clears its link address. +.It Cm bondmode Ar lacp | static +If the interface is a bond pseudo device, this option will set the \fImode\fR +on the bond interface. The two currently supported modes are +.Ar lacp +and +.Ar static . +The default mode is +.Ar lacp . +.Pp +To enable static mode (and turn off LACP), specify +.Ar static . +In static mode, a member interface is made an active part of the +link aggregate as long as the link status is active. +.Pp +To re-enable LACP mode, specify +.Ar lacp . +.El +.Pp +The following parameters are specific to IP tunnel interfaces, +.Xr gif 4 : +.Bl -tag -width indent +.It Cm tunnel Ar src_addr dest_addr +Configure the physical source and destination address for IP tunnel +interfaces. +The arguments +.Ar src_addr +and +.Ar dest_addr +are interpreted as the outer source/destination for the encapsulating +IPv4/IPv6 header. +.It Fl tunnel +Unconfigure the physical source and destination address for IP tunnel +interfaces previously configured with +.Cm tunnel . +.It Cm deletetunnel +Another name for the +.Fl tunnel +parameter. +.El +.Pp +The following parameters are specific to bridge interfaces: +.Bl -tag -width indent +.It Cm addm Ar interface +Add the interface named by +.Ar interface +as a member of the bridge. +The interface is put into promiscuous mode +so that it can receive every packet sent on the network. +.It Cm deletem Ar interface +Remove the interface named by +.Ar interface +from the bridge. +Promiscuous mode is disabled on the interface when +it is removed from the bridge. +.It Cm maxaddr Ar size +Set the size of the bridge address cache to +.Ar size . +The default is 100 entries. +.It Cm timeout Ar seconds +Set the timeout of address cache entries to +.Ar seconds +seconds. +If +.Ar seconds +is zero, then address cache entries will not be expired. +The default is 240 seconds. +.It Cm addr +Display the addresses that have been learned by the bridge. +.It Cm static Ar interface-name Ar address +Add a static entry into the address cache pointing to +.Ar interface-name . +Static entries are never aged out of the cache or re-placed, even if the +address is seen on a different interface. +.It Cm deladdr Ar address +Delete +.Ar address +from the address cache. +.It Cm flush +Delete all dynamically-learned addresses from the address cache. +.It Cm flushall +Delete all addresses, including static addresses, from the address cache. +.It Cm discover Ar interface +Mark an interface as a +.Dq discovering +interface. +When the bridge has no address cache entry +(either dynamic or static) +for the destination address of a packet, +the bridge will forward the packet to all +member interfaces marked as +.Dq discovering . +This is the default for all interfaces added to a bridge. +.It Cm -discover Ar interface +Clear the +.Dq discovering +attribute on a member interface. +For packets without the +.Dq discovering +attribute, the only packets forwarded on the interface are broadcast +or multicast packets and packets for which the destination address +is known to be on the interface's segment. +.It Cm learn Ar interface +Mark an interface as a +.Dq learning +interface. +When a packet arrives on such an interface, the source +address of the packet is entered into the address cache as being a +destination address on the interface's segment. +This is the default for all interfaces added to a bridge. +.It Cm -learn Ar interface +Clear the +.Dq learning +attribute on a member interface. +.\".It Cm sticky Ar interface +.\"Mark an interface as a +.\".Dq sticky +.\"interface. +.\"Dynamically learned address entries are treated at static once entered into +.\"the cache. +.\"Sticky entries are never aged out of the cache or replaced, even if the +.\"address is seen on a different interface. +.\".It Cm -sticky Ar interface +.\"Clear the +.\".Dq sticky +.\"attribute on a member interface. +.\".It Cm private Ar interface +.\"Mark an interface as a +.\".Dq private +.\"interface. +.\"A private interface does not forward any traffic to any other port that is also +.\"a private interface. +.\".It Cm -private Ar interface +.\"Clear the +.\".Dq private +.\"attribute on a member interface. +.\".It Cm span Ar interface +.\"Add the interface named by +.\".Ar interface +.\"as a span port on the bridge. +.\"Span ports transmit a copy of every frame received by the bridge. +.\"This is most useful for snooping a bridged network passively on +.\"another host connected to one of the span ports of the bridge. +.\".It Cm -span Ar interface +.\"Delete the interface named by +.\".Ar interface +.\"from the list of span ports of the bridge. +.It Cm stp Ar interface +Enable Spanning Tree protocol on +.Ar interface . +The +.Xr if_bridge 4 +driver has support for the IEEE 802.1D Spanning Tree protocol (STP). +Spanning Tree is used to detect and remove loops in a network topology. +.It Cm -stp Ar interface +Disable Spanning Tree protocol on +.Ar interface . +This is the default for all interfaces added to a bridge. +.\".It Cm edge Ar interface +.\"Set +.\".Ar interface +.\"as an edge port. +.\"An edge port connects directly to end stations cannot create bridging +.\"loops in the network, this allows it to transition straight to forwarding. +.\".It Cm -edge Ar interface +.\"Disable edge status on +.\".Ar interface . +.\".It Cm autoedge Ar interface +.\"Allow +.\".Ar interface +.\"to automatically detect edge status. +.\"This is the default for all interfaces added to a bridge. +.\".It Cm -autoedge Ar interface +.\"Disable automatic edge status on +.\".Ar interface . +.\".It Cm ptp Ar interface +.\"Set the +.\".Ar interface +.\"as a point to point link. +.\"This is required for straight transitions to forwarding and +.\"should be enabled on a direct link to another RSTP capable switch. +.\".It Cm -ptp Ar interface +.\"Disable point to point link status on +.\".Ar interface . +.\"This should be disabled for a half duplex link and for an interface +.\"connected to a shared network segment, +.\"like a hub or a wireless network. +.\".It Cm autoptp Ar interface +.\"Automatically detect the point to point status on +.\".Ar interface +.\"by checking the full duplex link status. +.\"This is the default for interfaces added to the bridge. +.\".It Cm -autoptp Ar interface +.\"Disable automatic point to point link detection on +.\".Ar interface . +.It Cm maxage Ar seconds +Set the time that a Spanning Tree protocol configuration is valid. +The default is 20 seconds. +The minimum is 6 seconds and the maximum is 40 seconds. +.It Cm fwddelay Ar seconds +Set the time that must pass before an interface begins forwarding +packets when Spanning Tree is enabled. +The default is 15 seconds. +The minimum is 4 seconds and the maximum is 30 seconds. +.It Cm hellotime Ar seconds +Set the time between broadcasting of Spanning Tree protocol +configuration messages. +The hello time may only be changed when operating in legacy stp mode. +The default is 2 seconds. +The minimum is 1 second and the maximum is 2 seconds. +.It Cm priority Ar value +Set the bridge priority for Spanning Tree. +The default is 32768. +The minimum is 0 and the maximum is 61440. +.\".It Cm proto Ar value +.\"Set the Spanning Tree protocol. +.\"The default is rstp. +.\"The available options are stp and rstp. +.\".It Cm holdcnt Ar value +.\"Set the transmit hold count for Spanning Tree. +.\"This is the number of packets transmitted before being rate limited. +.\"The default is 6. +.\"The minimum is 1 and the maximum is 10. +.It Cm ifpriority Ar interface Ar value +Set the Spanning Tree priority of +.Ar interface +to +.Ar value . +The default is 128. +The minimum is 0 and the maximum is 240. +.It Cm ifpathcost Ar interface Ar value +Set the Spanning Tree path cost of +.Ar interface +to +.Ar value . +The default is calculated from the link speed. +To change a previously selected path cost back to automatic, set the +cost to 0. +The minimum is 1 and the maximum is 200000000. +.It Cm ifmaxaddr Ar interface Ar size +Set the maximum number of hosts allowed from an interface, packets with unknown +source addresses are dropped until an existing host cache entry expires or is +removed. +Set to 0 to disable. +.It Cm hostfilter Ar interface Ar address +Configure the bridge to accept incoming packet on the interface +only if they match the given MAC address and IP address +-- use the command twice to set both type of addresses. +Other filtering restrictions apply. +.It Cm -hostfilter Ar interface +Allow traffic from any host on that interface. +.El +.Pp +The following parameters are specific to vlan interfaces: +.Bl -tag -width indent +.It Cm vlan Ar vlan_tag +Set the VLAN tag value to +.Ar vlan_tag . +This value is a 16-bit number which is used to create an 802.1Q +VLAN header for packets sent from the +.Xr vlan 4 +interface. +Note that +.Cm vlan +and +.Cm vlandev +must both be set at the same time. +.It Cm vlandev Ar iface +Associate the physical interface +.Ar iface +with a +.Xr vlan 4 +interface. +Packets transmitted through the +.Xr vlan 4 +interface will be +diverted to the specified physical interface +.Ar iface +with 802.1Q VLAN encapsulation. +Packets with 802.1Q encapsulation received +by the parent interface with the correct VLAN tag will be diverted to +the associated +.Xr vlan 4 +pseudo-interface. +The +.Xr vlan 4 +interface is assigned a +copy of the parent interface's flags and the parent's ethernet address. +The +.Cm vlandev +and +.Cm vlan +must both be set at the same time. +If the +.Xr vlan 4 +interface already has +a physical interface associated with it, this command will fail. +To +change the association to another physical interface, the existing +association must be cleared first. +.Pp +Note: if the hardware tagging capability +is set on the parent interface, the +.Xr vlan 4 +pseudo +interface's behavior changes: +the +.Xr vlan 4 +interface recognizes that the +parent interface supports insertion and extraction of VLAN tags on its +own (usually in firmware) and that it should pass packets to and from +the parent unaltered. +.It Fl vlandev Op Ar iface +If the driver is a +.Xr vlan 4 +pseudo device, disassociate the parent interface from it. +This breaks the link between the +.Xr vlan 4 +interface and its parent, +clears its VLAN tag, flags and its link address and shuts the interface down. +The +.Ar iface +argument is useless and hence deprecated. +.El +.Pp +The +.Nm +utility displays the current configuration for a network interface +when no optional parameters are supplied. +If a protocol family is specified, +.Nm +will report only the details specific to that protocol family. +.Pp +If the +.Fl m +flag is passed before an interface name, +.Nm +will display the capability list and all +of the supported media for the specified interface. +.Pp +If +.Fl L +flag is supplied, address lifetime is displayed for IPv6 addresses, +as time offset string. +.Pp +Optionally, the +.Fl a +flag may be used instead of an interface name. +This flag instructs +.Nm +to display information about all interfaces in the system. +The +.Fl d +flag limits this to interfaces that are down, and +.Fl u +limits this to interfaces that are up. +When no arguments are given, +.Fl a +is implied. +.Pp +The +.Fl l +flag may be used to list all available interfaces on the system, with +no other additional information. +Use of this flag is mutually exclusive +with all other flags and commands, except for +.Fl d +(only list interfaces that are down) +and +.Fl u +(only list interfaces that are up). +.Pp +The +.Fl v +flag may be used to get more verbose status for an interface. +.Pp +The +.Fl C +flag may be used to list all of the interface cloners available on +the system, with no additional information. +Use of this flag is mutually exclusive with all other flags and commands. +.Pp +The +.Fl r +flag may be used to show additional information related to the count of route references on the network interface. +.Pp +For bridge interfaces, the list of addresses learned by the bridge is not shown when displaying information about +all interfaces except when the +.Fl v +flag is used. +.Pp +Only the super-user may modify the configuration of a network interface. +.Sh NOTES +The media selection system is relatively new and only some drivers support +it (or have need for it). +.Sh EXAMPLES +Assign the IPv4 address +.Li 192.0.2.10 , +with a network mask of +.Li 255.255.255.0 , +to the interface +.Li en0 : +.Dl # ifconfig en0 inet 192.0.2.10 netmask 255.255.255.0 +.Pp +Add the IPv4 address +.Li 192.0.2.45 , +with the CIDR network prefix +.Li /28 , +to the interface +.Li en0 , +using +.Cm add +as a synonym for the canonical form of the option +.Cm alias : +.Dl # ifconfig en0 inet 192.0.2.45/28 add +.Pp +Remove the IPv4 address +.Li 192.0.2.45 +from the interface +.Li en0 : +.Dl # ifconfig en0 inet 192.0.2.45 -alias +.Pp +Add the IPv6 address +.Li 2001:DB8:DBDB::123/48 +to the interface +.Li en0 : +.Dl # ifconfig en0 inet6 2001:db8:bdbd::123 prefixlen 48 alias +Note that lower case hexadecimal IPv6 addresses are acceptable. +.Pp +Remove the IPv6 address added in the above example, +using the +.Li / +character as shorthand for the network prefix, +and using +.Cm delete +as a synonym for the canonical form of the option +.Fl alias : +.Dl # ifconfig en0 inet6 2001:db8:bdbd::123/48 delete +.Pp +Configure the interface +.Li en1 , +to use 100baseTX, full duplex Ethernet media options: +.Dl # ifconfig en1 media 100baseTX mediaopt full-duplex +.Pp +Create the software network interface +.Li gif1 : +.Dl # ifconfig gif1 create +.Pp +Destroy the software network interface +.Li gif1 : +.Dl # ifconfig gif1 destroy +.Sh DIAGNOSTICS +Messages indicating the specified interface does not exist, the +requested address is unknown, or the user is not privileged and +tried to alter an interface's configuration. +.Sh SEE ALSO +.Xr netstat 1 , +.Xr netintro 4 , +.Xr sysctl 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Bx 4.2 . +.Sh BUGS +Basic IPv6 node operation requires a link-local address on each +interface configured for IPv6. +Normally, such an address is automatically configured by the +kernel on each interface added to the system; this behaviour may +be disabled by setting the sysctl MIB variable +.Va net.inet6.ip6.auto_linklocal +to 0. +.Pp +If you delete such an address using +.Nm , +the kernel may act very odd. +Do this at your own risk. diff --git a/network_cmds-543/ifconfig.tproj/ifconfig.c b/network_cmds-543/ifconfig.tproj/ifconfig.c new file mode 100644 index 00000000..33af6fbe --- /dev/null +++ b/network_cmds-543/ifconfig.tproj/ifconfig.c @@ -0,0 +1,2050 @@ +/* + * Copyright (c) 2009-2017 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Copyright (c) 1983, 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. + */ + +#include + +#ifndef lint +__unused static const char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#ifndef __APPLE__ +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* IP */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ifconfig.h" + +/* + * Since "struct ifreq" is composed of various union members, callers + * should pay special attention to interprete the value. + * (.e.g. little/big endian difference in the structure.) + */ +struct ifreq ifr; + +char name[IFNAMSIZ]; +int setaddr; +int setmask; +int doalias; +int clearaddr; +int newaddr = 1; +int noload; +int all; + +int bond_details = 0; +int supmedia = 0; +#if TARGET_OS_EMBEDDED +int verbose = 1; +int showrtref = 1; +#else /* !TARGET_OS_EMBEDDED */ +int verbose = 0; +int showrtref = 0; +#endif /* !TARGET_OS_EMBEDDED */ +int printkeys = 0; /* Print keying material for interfaces. */ + +static int ifconfig(int argc, char *const *argv, int iscreate, + const struct afswtch *afp); +static void status(const struct afswtch *afp, const struct sockaddr_dl *sdl, + struct ifaddrs *ifa); +static char *bytes_to_str(unsigned long long bytes); +static char *bps_to_str(unsigned long long rate); +static char *ns_to_str(unsigned long long nsec); +static void tunnel_status(int s); +static void usage(void); +static char *sched2str(unsigned int s); +static char *tl2str(unsigned int s); +static char *ift2str(unsigned int t, unsigned int f, unsigned int sf); + +static struct afswtch *af_getbyname(const char *name); +static struct afswtch *af_getbyfamily(int af); +static void af_other_status(int); + +static struct option *opts = NULL; + +void +opt_register(struct option *p) +{ + p->next = opts; + opts = p; +} + +static void +usage(void) +{ + char options[1024]; + struct option *p; + + /* XXX not right but close enough for now */ + options[0] = '\0'; + for (p = opts; p != NULL; p = p->next) { + strlcat(options, p->opt_usage, sizeof(options)); + strlcat(options, " ", sizeof(options)); + } + + fprintf(stderr, + "usage: ifconfig %sinterface address_family [address [dest_address]]\n" + " [parameters]\n" + " ifconfig interface create\n" + " ifconfig -a %s[-d] [-m] [-u] [-v] [address_family]\n" + " ifconfig -l [-d] [-u] [address_family]\n" + " ifconfig %s[-d] [-m] [-u] [-v]\n", + options, options, options); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + int c, namesonly, downonly, uponly; + const struct afswtch *afp = NULL; + int ifindex; + struct ifaddrs *ifap, *ifa; + struct ifreq paifr; + const struct sockaddr_dl *sdl; + char options[1024], *cp; + const char *ifname; + struct option *p; + size_t iflen; + + all = downonly = uponly = namesonly = noload = 0; + + /* Parse leading line options */ +#ifndef __APPLE__ + strlcpy(options, "adklmnuv", sizeof(options)); +#else + strlcpy(options, "abdlmruv", sizeof(options)); +#endif + for (p = opts; p != NULL; p = p->next) + strlcat(options, p->opt, sizeof(options)); + while ((c = getopt(argc, argv, options)) != -1) { + switch (c) { + case 'a': /* scan all interfaces */ + all++; + break; + case 'b': /* bond detailed output */ + bond_details++; + break; + case 'd': /* restrict scan to "down" interfaces */ + downonly++; + break; +#ifndef __APPLE__ + case 'k': + printkeys++; + break; +#endif + case 'l': /* scan interface names only */ + namesonly++; + break; + case 'm': /* show media choices in status */ + supmedia = 1; + break; +#ifndef __APPLE__ + case 'n': /* suppress module loading */ + noload++; + break; +#endif + case 'r': + showrtref++; + break; + case 'u': /* restrict scan to "up" interfaces */ + uponly++; + break; + case 'v': + verbose++; + break; + default: + for (p = opts; p != NULL; p = p->next) + if (p->opt[0] == c) { + p->cb(optarg); + break; + } + if (p == NULL) + usage(); + break; + } + } + argc -= optind; + argv += optind; + + /* -l cannot be used with -a or -q or -m or -b */ + if (namesonly && + (all || supmedia || bond_details)) + usage(); + + /* nonsense.. */ + if (uponly && downonly) + usage(); + + /* no arguments is equivalent to '-a' */ + if (!namesonly && argc < 1) + all = 1; + + /* -a and -l allow an address family arg to limit the output */ + if (all || namesonly) { + if (argc > 1) + usage(); + + ifname = NULL; + if (argc == 1) { + afp = af_getbyname(*argv); + if (afp == NULL) + usage(); + if (afp->af_name != NULL) + argc--, argv++; + /* leave with afp non-zero */ + } + } else { + /* not listing, need an argument */ + if (argc < 1) + usage(); + + ifname = *argv; + argc--, argv++; + +#ifdef notdef + /* check and maybe load support for this interface */ + ifmaybeload(ifname); +#endif + ifindex = if_nametoindex(ifname); + if (ifindex == 0) { + /* + * NOTE: We must special-case the `create' command + * right here as we would otherwise fail when trying + * to find the interface. + */ + if (argc > 0 && (strcmp(argv[0], "create") == 0 || + strcmp(argv[0], "plumb") == 0)) { + iflen = strlcpy(name, ifname, sizeof(name)); + if (iflen >= sizeof(name)) + errx(1, "%s: cloning name too long", + ifname); + ifconfig(argc, argv, 1, NULL); + exit(0); + } + errx(1, "interface %s does not exist", ifname); + } + } + + /* Check for address family */ + if (argc > 0) { + afp = af_getbyname(*argv); + if (afp != NULL) + argc--, argv++; + } + + if (getifaddrs(&ifap) != 0) + err(EXIT_FAILURE, "getifaddrs"); + cp = NULL; + ifindex = 0; + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + memset(&paifr, 0, sizeof(paifr)); + strncpy(paifr.ifr_name, ifa->ifa_name, sizeof(paifr.ifr_name)); + if (sizeof(paifr.ifr_addr) >= ifa->ifa_addr->sa_len) { + memcpy(&paifr.ifr_addr, ifa->ifa_addr, + ifa->ifa_addr->sa_len); + } + + if (ifname != NULL && strcmp(ifname, ifa->ifa_name) != 0) + continue; + if (ifa->ifa_addr->sa_family == AF_LINK) + sdl = (const struct sockaddr_dl *) ifa->ifa_addr; + else + sdl = NULL; + if (cp != NULL && strcmp(cp, ifa->ifa_name) == 0) + continue; + iflen = strlcpy(name, ifa->ifa_name, sizeof(name)); + if (iflen >= sizeof(name)) { + warnx("%s: interface name too long, skipping", + ifa->ifa_name); + continue; + } + cp = ifa->ifa_name; + + if (downonly && (ifa->ifa_flags & IFF_UP) != 0) + continue; + if (uponly && (ifa->ifa_flags & IFF_UP) == 0) + continue; + ifindex++; + /* + * Are we just listing the interfaces? + */ + if (namesonly) { + if (ifindex > 1) + printf(" "); + fputs(name, stdout); + continue; + } + + if (argc > 0) + ifconfig(argc, argv, 0, afp); + else + status(afp, sdl, ifa); + } + if (namesonly) + printf("\n"); + freeifaddrs(ifap); + + exit(0); +} + +static struct afswtch *afs = NULL; + +void +af_register(struct afswtch *p) +{ + p->af_next = afs; + afs = p; +} + +static struct afswtch * +af_getbyname(const char *name) +{ + struct afswtch *afp; + + for (afp = afs; afp != NULL; afp = afp->af_next) + if (strcmp(afp->af_name, name) == 0) + return afp; + return NULL; +} + +static struct afswtch * +af_getbyfamily(int af) +{ + struct afswtch *afp; + + for (afp = afs; afp != NULL; afp = afp->af_next) + if (afp->af_af == af) + return afp; + return NULL; +} + +static void +af_other_status(int s) +{ + struct afswtch *afp; + uint8_t afmask[howmany(AF_MAX, NBBY)]; + + memset(afmask, 0, sizeof(afmask)); + for (afp = afs; afp != NULL; afp = afp->af_next) { + if (afp->af_other_status == NULL) + continue; + if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af)) + continue; + afp->af_other_status(s); + setbit(afmask, afp->af_af); + } +} + +static void +af_all_tunnel_status(int s) +{ + struct afswtch *afp; + uint8_t afmask[howmany(AF_MAX, NBBY)]; + + memset(afmask, 0, sizeof(afmask)); + for (afp = afs; afp != NULL; afp = afp->af_next) { + if (afp->af_status_tunnel == NULL) + continue; + if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af)) + continue; + afp->af_status_tunnel(s); + setbit(afmask, afp->af_af); + } +} + +static struct cmd *cmds = NULL; + +void +cmd_register(struct cmd *p) +{ + p->c_next = cmds; + cmds = p; +} + +static const struct cmd * +cmd_lookup(const char *name) +{ +#define N(a) (sizeof(a)/sizeof(a[0])) + const struct cmd *p; + + for (p = cmds; p != NULL; p = p->c_next) + if (strcmp(name, p->c_name) == 0) + return p; + return NULL; +#undef N +} + +struct callback { + callback_func *cb_func; + void *cb_arg; + struct callback *cb_next; +}; +static struct callback *callbacks = NULL; + +void +callback_register(callback_func *func, void *arg) +{ + struct callback *cb; + + cb = malloc(sizeof(struct callback)); + if (cb == NULL) + errx(1, "unable to allocate memory for callback"); + cb->cb_func = func; + cb->cb_arg = arg; + cb->cb_next = callbacks; + callbacks = cb; +} + +/* specially-handled commands */ +static void setifaddr(const char *, int, int, const struct afswtch *); +static const struct cmd setifaddr_cmd = DEF_CMD("ifaddr", 0, setifaddr); + +static void setifdstaddr(const char *, int, int, const struct afswtch *); +static const struct cmd setifdstaddr_cmd = + DEF_CMD("ifdstaddr", 0, setifdstaddr); + +static int +ifconfig(int argc, char *const *argv, int iscreate, const struct afswtch *afp) +{ + const struct afswtch *nafp; + struct callback *cb; + int s; + + strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); +top: + if (afp == NULL) + afp = af_getbyname("inet"); + ifr.ifr_addr.sa_family = + afp->af_af == AF_LINK || afp->af_af == AF_UNSPEC ? + AF_INET : afp->af_af; + + if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0) + err(1, "socket(family %u,SOCK_DGRAM", ifr.ifr_addr.sa_family); + + while (argc > 0) { + const struct cmd *p; + + p = cmd_lookup(*argv); + if (p == NULL) { + /* + * Not a recognized command, choose between setting + * the interface address and the dst address. + */ + p = (setaddr ? &setifdstaddr_cmd : &setifaddr_cmd); + } + if (p->c_u.c_func || p->c_u.c_func2) { + if (iscreate && !p->c_iscloneop) { + /* + * Push the clone create callback so the new + * device is created and can be used for any + * remaining arguments. + */ + cb = callbacks; + if (cb == NULL) + errx(1, "internal error, no callback"); + callbacks = cb->cb_next; + cb->cb_func(s, cb->cb_arg); + iscreate = 0; + /* + * Handle any address family spec that + * immediately follows and potentially + * recreate the socket. + */ + nafp = af_getbyname(*argv); + if (nafp != NULL) { + argc--, argv++; + if (nafp != afp) { + close(s); + afp = nafp; + goto top; + } + } + } + if (p->c_parameter == NEXTARG) { + if (argv[1] == NULL) + errx(1, "'%s' requires argument", + p->c_name); + p->c_u.c_func(argv[1], 0, s, afp); + argc--, argv++; + } else if (p->c_parameter == OPTARG) { + p->c_u.c_func(argv[1], 0, s, afp); + if (argv[1] != NULL) + argc--, argv++; + } else if (p->c_parameter == NEXTARG2) { + if (argc < 3) + errx(1, "'%s' requires 2 arguments", + p->c_name); + p->c_u.c_func2(argv[1], argv[2], s, afp); + argc -= 2, argv += 2; + } else + p->c_u.c_func(*argv, p->c_parameter, s, afp); + } + argc--, argv++; + } + + /* + * Do any post argument processing required by the address family. + */ + if (afp->af_postproc != NULL) + afp->af_postproc(s, afp); + /* + * Do deferred callbacks registered while processing + * command-line arguments. + */ + for (cb = callbacks; cb != NULL; cb = cb->cb_next) + cb->cb_func(s, cb->cb_arg); + /* + * Do deferred operations. + */ + if (clearaddr) { + if (afp->af_ridreq == NULL || afp->af_difaddr == 0) { + warnx("interface %s cannot change %s addresses!", + name, afp->af_name); + clearaddr = 0; + } + } + if (clearaddr) { + int ret; + strncpy(afp->af_ridreq, name, sizeof ifr.ifr_name); + ret = ioctl(s, afp->af_difaddr, afp->af_ridreq); + if (ret < 0) { + if (errno == EADDRNOTAVAIL && (doalias >= 0)) { + /* means no previous address for interface */ + } else + Perror("ioctl (SIOCDIFADDR)"); + } + } + if (newaddr) { + if (afp->af_addreq == NULL || afp->af_aifaddr == 0) { + warnx("interface %s cannot change %s addresses!", + name, afp->af_name); + newaddr = 0; + } + } + if (newaddr && (setaddr || setmask)) { + strncpy(afp->af_addreq, name, sizeof ifr.ifr_name); + if (ioctl(s, afp->af_aifaddr, afp->af_addreq) < 0) + Perror("ioctl (SIOCAIFADDR)"); + } + + close(s); + return(0); +} + +/*ARGSUSED*/ +static void +setifaddr(const char *addr, int param, int s, const struct afswtch *afp) +{ + if (afp->af_getaddr == NULL) + return; + /* + * Delay the ioctl to set the interface addr until flags are all set. + * The address interpretation may depend on the flags, + * and the flags may change when the address is set. + */ + setaddr++; + if (doalias == 0 && afp->af_af != AF_LINK) + clearaddr = 1; + afp->af_getaddr(addr, (doalias >= 0 ? ADDR : RIDADDR)); +} + +static void +settunnel(const char *src, const char *dst, int s, const struct afswtch *afp) +{ + struct addrinfo *srcres, *dstres; + int ecode; + + if (afp->af_settunnel == NULL) { + warn("address family %s does not support tunnel setup", + afp->af_name); + return; + } + + if ((ecode = getaddrinfo(src, NULL, NULL, &srcres)) != 0) + errx(1, "error in parsing address string: %s", + gai_strerror(ecode)); + + if ((ecode = getaddrinfo(dst, NULL, NULL, &dstres)) != 0) + errx(1, "error in parsing address string: %s", + gai_strerror(ecode)); + + if (srcres->ai_addr->sa_family != dstres->ai_addr->sa_family) + errx(1, + "source and destination address families do not match"); + + afp->af_settunnel(s, srcres, dstres); + + freeaddrinfo(srcres); + freeaddrinfo(dstres); +} + +/* ARGSUSED */ +static void +deletetunnel(const char *vname, int param, int s, const struct afswtch *afp) +{ + + if (ioctl(s, SIOCDIFPHYADDR, &ifr) < 0) + err(1, "SIOCDIFPHYADDR"); +} + +static void +setifnetmask(const char *addr, int dummy __unused, int s, + const struct afswtch *afp) +{ + if (afp->af_getaddr != NULL) { + setmask++; + afp->af_getaddr(addr, MASK); + } +} + +static void +setifbroadaddr(const char *addr, int dummy __unused, int s, + const struct afswtch *afp) +{ + if (afp->af_getaddr != NULL) + afp->af_getaddr(addr, DSTADDR); +} + +static void +setifipdst(const char *addr, int dummy __unused, int s, + const struct afswtch *afp) +{ + const struct afswtch *inet; + + inet = af_getbyname("inet"); + if (inet == NULL) + return; + inet->af_getaddr(addr, DSTADDR); + clearaddr = 0; + newaddr = 0; +} + +static void +notealias(const char *addr, int param, int s, const struct afswtch *afp) +{ +#define rqtosa(x) (&(((struct ifreq *)(afp->x))->ifr_addr)) + if (setaddr && doalias == 0 && param < 0) + if (afp->af_addreq != NULL && afp->af_ridreq != NULL) + bcopy((caddr_t)rqtosa(af_addreq), + (caddr_t)rqtosa(af_ridreq), + rqtosa(af_addreq)->sa_len); + doalias = param; + if (param < 0) { + clearaddr = 1; + newaddr = 0; + } else + clearaddr = 0; +#undef rqtosa +} + +/*ARGSUSED*/ +static void +setifdstaddr(const char *addr, int param __unused, int s, + const struct afswtch *afp) +{ + if (afp->af_getaddr != NULL) + afp->af_getaddr(addr, DSTADDR); +} + +/* + * Note: doing an SIOCIGIFFLAGS scribbles on the union portion + * of the ifreq structure, which may confuse other parts of ifconfig. + * Make a private copy so we can avoid that. + */ +static void +setifflags(const char *vname, int value, int s, const struct afswtch *afp) +{ + struct ifreq my_ifr; + int flags; + + bcopy((char *)&ifr, (char *)&my_ifr, sizeof(struct ifreq)); + + if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&my_ifr) < 0) { + Perror("ioctl (SIOCGIFFLAGS)"); + exit(1); + } + strncpy(my_ifr.ifr_name, name, sizeof (my_ifr.ifr_name)); + flags = my_ifr.ifr_flags; + + if (value < 0) { + value = -value; + flags &= ~value; + } else + flags |= value; + my_ifr.ifr_flags = flags & 0xffff; + if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&my_ifr) < 0) + Perror(vname); +} + +void +setifcap(const char *vname, int value, int s, const struct afswtch *afp) +{ + int flags; + + if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) < 0) { + Perror("ioctl (SIOCGIFCAP)"); + exit(1); + } + flags = ifr.ifr_curcap; + if (value < 0) { + value = -value; + flags &= ~value; + } else + flags |= value; + flags &= ifr.ifr_reqcap; + ifr.ifr_reqcap = flags; + if (ioctl(s, SIOCSIFCAP, (caddr_t)&ifr) < 0) + Perror(vname); +} + +static void +setifmetric(const char *val, int dummy __unused, int s, + const struct afswtch *afp) +{ + strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + ifr.ifr_metric = atoi(val); + if (ioctl(s, SIOCSIFMETRIC, (caddr_t)&ifr) < 0) + warn("ioctl (set metric)"); +} + +static void +setifmtu(const char *val, int dummy __unused, int s, + const struct afswtch *afp) +{ + strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + ifr.ifr_mtu = atoi(val); + if (ioctl(s, SIOCSIFMTU, (caddr_t)&ifr) < 0) + warn("ioctl (set mtu)"); +} + +#ifndef __APPLE__ +static void +setifname(const char *val, int dummy __unused, int s, + const struct afswtch *afp) +{ + char *newname; + + newname = strdup(val); + if (newname == NULL) { + warn("no memory to set ifname"); + return; + } + ifr.ifr_data = newname; + if (ioctl(s, SIOCSIFNAME, (caddr_t)&ifr) < 0) { + warn("ioctl (set name)"); + free(newname); + return; + } + strlcpy(name, newname, sizeof(name)); + free(newname); +} +#endif + +static void +setrouter(const char *vname, int value, int s, const struct afswtch *afp) +{ + if (afp->af_setrouter == NULL) { + warn("address family %s does not support router mode", + afp->af_name); + return; + } + + afp->af_setrouter(s, value); +} + +static void +setifdesc(const char *val, int dummy __unused, int s, const struct afswtch *afp) +{ + struct if_descreq ifdr; + + bzero(&ifdr, sizeof (ifdr)); + strncpy(ifdr.ifdr_name, name, sizeof (ifdr.ifdr_name)); + ifdr.ifdr_len = strlen(val); + strncpy((char *)ifdr.ifdr_desc, val, sizeof (ifdr.ifdr_desc)); + + if (ioctl(s, SIOCSIFDESC, (caddr_t)&ifdr) < 0) { + warn("ioctl (set desc)"); + } +} + +static void +settbr(const char *val, int dummy __unused, int s, const struct afswtch *afp) +{ + struct if_linkparamsreq iflpr; + long double bps; + u_int64_t rate; + u_int32_t percent = 0; + char *cp; + + errno = 0; + bzero(&iflpr, sizeof (iflpr)); + strncpy(iflpr.iflpr_name, name, sizeof (iflpr.iflpr_name)); + + bps = strtold(val, &cp); + if (val == cp || errno != 0) { + warn("Invalid value '%s'", val); + return; + } + rate = (u_int64_t)bps; + if (cp != NULL) { + if (!strcmp(cp, "b") || !strcmp(cp, "bps")) { + ; /* nothing */ + } else if (!strcmp(cp, "Kb") || !strcmp(cp, "Kbps")) { + rate *= 1000; + } else if (!strcmp(cp, "Mb") || !strcmp(cp, "Mbps")) { + rate *= 1000 * 1000; + } else if (!strcmp(cp, "Gb") || !strcmp(cp, "Gbps")) { + rate *= 1000 * 1000 * 1000; + } else if (!strcmp(cp, "%")) { + percent = rate; + if (percent == 0 || percent > 100) { + printf("Value out of range '%s'", val); + return; + } + } else if (*cp != '\0') { + printf("Unknown unit '%s'", cp); + return; + } + } + iflpr.iflpr_output_tbr_rate = rate; + iflpr.iflpr_output_tbr_percent = percent; + if (ioctl(s, SIOCSIFLINKPARAMS, &iflpr) < 0 && + errno != ENOENT && errno != ENXIO && errno != ENODEV) { + warn("ioctl (set link params)"); + } else if (errno == ENXIO) { + printf("TBR cannot be set on %s\n", name); + } else if (errno == ENOENT || rate == 0) { + printf("%s: TBR is now disabled\n", name); + } else if (errno == ENODEV) { + printf("%s: requires absolute TBR rate\n", name); + } else if (percent != 0) { + printf("%s: TBR rate set to %u%% of effective link rate\n", + name, percent); + } else { + printf("%s: TBR rate set to %s\n", name, bps_to_str(rate)); + } +} + +static void +setthrottle(const char *val, int dummy __unused, int s, + const struct afswtch *afp) +{ + struct if_throttlereq iftr; + char *cp; + + errno = 0; + bzero(&iftr, sizeof (iftr)); + strncpy(iftr.ifthr_name, name, sizeof (iftr.ifthr_name)); + + iftr.ifthr_level = strtold(val, &cp); + if (val == cp || errno != 0) { + warn("Invalid value '%s'", val); + return; + } + + if (ioctl(s, SIOCSIFTHROTTLE, &iftr) < 0 && errno != ENXIO) { + warn("ioctl (set throttling level)"); + } else if (errno == ENXIO) { + printf("throttling level cannot be set on %s\n", name); + } else { + printf("%s: throttling level set to %d\n", name, + iftr.ifthr_level); + } +} + +static void +setdisableoutput(const char *val, int dummy __unused, int s, + const struct afswtch *afp) +{ + struct ifreq ifr; + char *cp; + errno = 0; + bzero(&ifr, sizeof (ifr)); + strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); + + ifr.ifr_ifru.ifru_disable_output = strtold(val, &cp); + if (val == cp || errno != 0) { + warn("Invalid value '%s'", val); + return; + } + + if (ioctl(s, SIOCSIFDISABLEOUTPUT, &ifr) < 0 && errno != ENXIO) { + warn("ioctl set disable output"); + } else if (errno == ENXIO) { + printf("output thread can not be disabled on %s\n", name); + } else { + printf("output %s on %s\n", + ((ifr.ifr_ifru.ifru_disable_output == 0) ? "enabled" : "disabled"), + name); + } +} + +static void +setlog(const char *val, int dummy __unused, int s, + const struct afswtch *afp) +{ + char *cp; + + errno = 0; + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + + ifr.ifr_log.ifl_level = strtold(val, &cp); + if (val == cp || errno != 0) { + warn("Invalid value '%s'", val); + return; + } + ifr.ifr_log.ifl_flags = (IFRLOGF_DLIL|IFRLOGF_FAMILY|IFRLOGF_DRIVER| + IFRLOGF_FIRMWARE); + + if (ioctl(s, SIOCSIFLOG, &ifr) < 0) + warn("ioctl (set logging parameters)"); +} + +void +setcl2k(const char *vname, int value, int s, const struct afswtch *afp) +{ + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_ifru.ifru_2kcl = value; + + if (ioctl(s, SIOCSIF2KCL, (caddr_t)&ifr) < 0) + Perror(vname); +} + +void +setexpensive(const char *vname, int value, int s, const struct afswtch *afp) +{ + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_ifru.ifru_expensive = value; + + if (ioctl(s, SIOCSIFEXPENSIVE, (caddr_t)&ifr) < 0) + Perror(vname); +} + +void +settimestamp(const char *vname, int value, int s, const struct afswtch *afp) +{ + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + + if (value == 0) { + if (ioctl(s, SIOCSIFTIMESTAMPDISABLE, (caddr_t)&ifr) < 0) + Perror(vname); + } else { + if (ioctl(s, SIOCSIFTIMESTAMPENABLE, (caddr_t)&ifr) < 0) + Perror(vname); + } +} + +void +setecnmode(const char *val, int dummy __unused, int s, + const struct afswtch *afp) +{ + char *cp; + + if (strcmp(val, "default") == 0) + ifr.ifr_ifru.ifru_ecn_mode = IFRTYPE_ECN_DEFAULT; + else if (strcmp(val, "enable") == 0) + ifr.ifr_ifru.ifru_ecn_mode = IFRTYPE_ECN_ENABLE; + else if (strcmp(val, "disable") == 0) + ifr.ifr_ifru.ifru_ecn_mode = IFRTYPE_ECN_DISABLE; + else { + ifr.ifr_ifru.ifru_ecn_mode = strtold(val, &cp); + if (val == cp || errno != 0) { + warn("Invalid ECN mode value '%s'", val); + return; + } + } + + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + + if (ioctl(s, SIOCSECNMODE, (caddr_t)&ifr) < 0) + Perror("ioctl(SIOCSECNMODE)"); +} + +#if defined(SIOCSQOSMARKINGMODE) && defined(SIOCSQOSMARKINGENABLED) + +void +setqosmarking(const char *cmd, const char *arg, int s, const struct afswtch *afp) +{ + u_long ioc; + +#if (DEBUG | DEVELOPMENT) + printf("%s(%s, %s)\n", __func__, cmd, arg); +#endif /* (DEBUG | DEVELOPMENT) */ + + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + + if (strcmp(cmd, "mode") == 0) { + ioc = SIOCSQOSMARKINGMODE; + + if (strcmp(arg, "fastlane") == 0) + ifr.ifr_qosmarking_mode = IFRTYPE_QOSMARKING_FASTLANE; + else if (strcasecmp(arg, "none") == 0 || strcasecmp(arg, "off") == 0) + ifr.ifr_qosmarking_mode = IFRTYPE_QOSMARKING_MODE_NONE; + else + err(EX_USAGE, "bad value for qosmarking mode: %s", arg); + } else if (strcmp(cmd, "enabled") == 0) { + ioc = SIOCSQOSMARKINGENABLED; + if (strcmp(arg, "1") == 0 || strcasecmp(arg, "on") == 0|| + strcasecmp(arg, "yes") == 0 || strcasecmp(arg, "true") == 0) + ifr.ifr_qosmarking_enabled = 1; + else if (strcmp(arg, "0") == 0 || strcasecmp(arg, "off") == 0|| + strcasecmp(arg, "no") == 0 || strcasecmp(arg, "false") == 0) + ifr.ifr_qosmarking_enabled = 0; + else + err(EX_USAGE, "bad value for qosmarking enabled: %s", arg); + } else { + err(EX_USAGE, "qosmarking takes mode or enabled"); + } + + if (ioctl(s, ioc, (caddr_t)&ifr) < 0) + err(EX_OSERR, "ioctl(%s, %s)", cmd, arg); +} + +void +setfastlane(const char *cmd, const char *arg, int s, const struct afswtch *afp) +{ + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + + warnx("### fastlane is obsolete, use qosmarking ###"); + + if (strcmp(cmd, "capable") == 0) { + if (strcmp(arg, "1") == 0 || strcasecmp(arg, "on") == 0|| + strcasecmp(arg, "yes") == 0 || strcasecmp(arg, "true") == 0) + setqosmarking("mode", "fastlane", s, afp); + else if (strcmp(arg, "0") == 0 || strcasecmp(arg, "off") == 0|| + strcasecmp(arg, "no") == 0 || strcasecmp(arg, "false") == 0) + setqosmarking("mode", "off", s, afp); + else + err(EX_USAGE, "bad value for fastlane %s", cmd); + } else if (strcmp(cmd, "enable") == 0) { + if (strcmp(arg, "1") == 0 || strcasecmp(arg, "on") == 0|| + strcasecmp(arg, "yes") == 0 || strcasecmp(arg, "true") == 0) + setqosmarking("enabled", "1", s, afp); + else if (strcmp(arg, "0") == 0 || strcasecmp(arg, "off") == 0|| + strcasecmp(arg, "no") == 0 || strcasecmp(arg, "false") == 0) + setqosmarking("enabled", "0", s, afp); + else + err(EX_USAGE, "bad value for fastlane %s", cmd); + } else { + err(EX_USAGE, "fastlane takes capable or enable"); + } +} + +#else /* defined(SIOCSQOSMARKINGMODE) && defined(SIOCSQOSMARKINGENABLED) */ + +void +setfastlane(const char *cmd, const char *arg, int s, const struct afswtch *afp) +{ + int value; + u_long ioc; + + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + + if (strcmp(cmd, "capable") == 0) + ioc = SIOCSFASTLANECAPABLE; + else if (strcmp(cmd, "enable") == 0) + ioc = SIOCSFASTLEENABLED; + else + err(EX_USAGE, "fastlane takes capable or enabled"); + + if (strcmp(arg, "1") == 0 || strcasecmp(arg, "on") == 0|| + strcasecmp(arg, "yes") == 0 || strcasecmp(arg, "true") == 0) + value = 1; + else if (strcmp(arg, "0") == 0 || strcasecmp(arg, "off") == 0|| + strcasecmp(arg, "no") == 0 || strcasecmp(arg, "false") == 0) + value = 0; + else + err(EX_USAGE, "bad value for fastlane %s", cmd); + + if (ioc == SIOCSFASTLANECAPABLE) + ifr.ifr_fastlane_capable = value; + else + ifr.ifr_fastlane_enabled = value; + + if (ioctl(s, ioc, (caddr_t)&ifr) < 0) + err(EX_OSERR, "ioctl(%s, %s)", cmd, arg); +} + + +void +setqosmarking(const char *cmd, const char *arg, int s, const struct afswtch *afp) +{ + if (strcmp(cmd, "mode") == 0) { + if (strcmp(arg, "fastlane") == 0) + setfastlane("capable", "on", s, afp); + else if (strcmp(arg, "none") == 0) + setfastlane("capable", "off", s, afp); + else + err(EX_USAGE, "bad value for qosmarking mode: %s", arg); + } else if (strcmp(cmd, "enabled") == 0) { + if (strcmp(arg, "1") == 0 || strcasecmp(arg, "on") == 0|| + strcasecmp(arg, "yes") == 0 || strcasecmp(arg, "true") == 0) + setfastlane("enable", "on", s, afp); + else if (strcmp(arg, "0") == 0 || strcasecmp(arg, "off") == 0|| + strcasecmp(arg, "no") == 0 || strcasecmp(arg, "false") == 0) + setfastlane("enable", "off", s, afp); + else + err(EX_USAGE, "bad value for qosmarking enabled: %s", arg); + } else { + err(EX_USAGE, "qosmarking takes mode or enabled"); + } +} + +#endif /* defined(SIOCSQOSMARKINGMODE) && defined(SIOCSQOSMARKINGENABLED) */ + +#define IFFBITS \ +"\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6SMART\7RUNNING" \ +"\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2" \ +"\20MULTICAST" + +#define IFEFBITS \ +"\020\1AUTOCONFIGURING\5FASTLN_CAP\6IPV6_DISABLED\7ACCEPT_RTADV\10TXSTART\11RXPOLL" \ +"\12VLAN\13BOND\14ARPLL\15NOWINDOWSCALE\16NOAUTOIPV6LL\17EXPENSIVE\20ROUTER4" \ +"\21ROUTER6\22LOCALNET_PRIVATE\23ND6ALT\24RESTRICTED_RECV\25AWDL\26NOACKPRI" \ +"\27AWDL_RESTRICTED\30CL2K\31ECN_ENABLE\32ECN_DISABLE\33CHANNEL_DRV\34CA" \ +"\35SENDLIST\36DIRECTLINK\37FASTLN_ON\40UPDOWNCHANGE" + +#define IFCAPBITS \ +"\020\1RXCSUM\2TXCSUM\3VLAN_MTU\4VLAN_HWTAGGING\5JUMBO_MTU" \ +"\6TSO4\7TSO6\10LRO\11AV\12TXSTATUS\13CHANNEL_IO\14HW_TIMESTAMP\15SW_TIMESTAMP" \ +"\16PARTIAL_CSUM\17ZEROINVERT_CSUM" + +#define IFRLOGF_BITS \ +"\020\1DLIL\21FAMILY\31DRIVER\35FIRMWARE" + +/* + * Print the status of the interface. If an address family was + * specified, show only it; otherwise, show them all. + */ +static void +status(const struct afswtch *afp, const struct sockaddr_dl *sdl, + struct ifaddrs *ifa) +{ + struct ifaddrs *ift; + int allfamilies, s; + struct ifstat ifs; + struct if_descreq ifdr; + struct if_linkparamsreq iflpr; + int mib[6]; + struct ifmibdata_supplemental ifmsupp; + size_t miblen = sizeof(struct ifmibdata_supplemental); + u_int64_t eflags = 0; + int curcap = 0; + + if (afp == NULL) { + allfamilies = 1; + afp = af_getbyname("inet"); + } else + allfamilies = 0; + + ifr.ifr_addr.sa_family = afp->af_af == AF_LINK ? AF_INET : afp->af_af; + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + + s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0); + if (s < 0) + err(1, "socket(family %u,SOCK_DGRAM)", ifr.ifr_addr.sa_family); + + printf("%s: ", name); + printb("flags", ifa->ifa_flags, IFFBITS); + if (ioctl(s, SIOCGIFMETRIC, &ifr) != -1) + if (ifr.ifr_metric) + printf(" metric %d", ifr.ifr_metric); + if (ioctl(s, SIOCGIFMTU, &ifr) != -1) + printf(" mtu %d", ifr.ifr_mtu); + if (showrtref && ioctl(s, SIOCGIFGETRTREFCNT, &ifr) != -1) + printf(" rtref %d", ifr.ifr_route_refcnt); + if (verbose) { + unsigned int ifindex = if_nametoindex(ifa->ifa_name); + if (ifindex != 0) + printf(" index %u", ifindex); + } + putchar('\n'); + + if (verbose && ioctl(s, SIOCGIFEFLAGS, (caddr_t)&ifr) != -1 && + (eflags = ifr.ifr_eflags) != 0) { + printb("\teflags", eflags, IFEFBITS); + putchar('\n'); + } + + if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) == 0) { + if (ifr.ifr_curcap != 0) { + curcap = ifr.ifr_curcap; + printb("\toptions", ifr.ifr_curcap, IFCAPBITS); + putchar('\n'); + } + if (supmedia && ifr.ifr_reqcap != 0) { + printb("\tcapabilities", ifr.ifr_reqcap, IFCAPBITS); + putchar('\n'); + } + } + + tunnel_status(s); + + for (ift = ifa; ift != NULL; ift = ift->ifa_next) { + if (ift->ifa_addr == NULL) + continue; + if (strcmp(ifa->ifa_name, ift->ifa_name) != 0) + continue; + if (allfamilies) { + const struct afswtch *p; + p = af_getbyfamily(ift->ifa_addr->sa_family); + if (p != NULL && p->af_status != NULL) + p->af_status(s, ift); + } else if (afp->af_af == ift->ifa_addr->sa_family) + afp->af_status(s, ift); + } +#if 0 + if (allfamilies || afp->af_af == AF_LINK) { + const struct afswtch *lafp; + + /* + * Hack; the link level address is received separately + * from the routing information so any address is not + * handled above. Cobble together an entry and invoke + * the status method specially. + */ + lafp = af_getbyname("lladdr"); + if (lafp != NULL) { + info.rti_info[RTAX_IFA] = (struct sockaddr *)sdl; + lafp->af_status(s, &info); + } + } +#endif + if (allfamilies) + af_other_status(s); + else if (afp->af_other_status != NULL) + afp->af_other_status(s); + + strncpy(ifs.ifs_name, name, sizeof ifs.ifs_name); + if (ioctl(s, SIOCGIFSTATUS, &ifs) == 0) + printf("%s", ifs.ascii); + + /* The rest is for when verbose is set; if not set, we're done */ + if (!verbose) + goto done; + + if (ioctl(s, SIOCGIFTYPE, &ifr) != -1) { + char *c = ift2str(ifr.ifr_type.ift_type, + ifr.ifr_type.ift_family, ifr.ifr_type.ift_subfamily); + if (c != NULL) + printf("\ttype: %s\n", c); + } + + if (verbose > 0) { + struct if_agentidsreq ifar; + memset(&ifar, 0, sizeof(ifar)); + + strlcpy(ifar.ifar_name, name, sizeof(ifar.ifar_name)); + + if (ioctl(s, SIOCGIFAGENTIDS, &ifar) != -1) { + if (ifar.ifar_count != 0) { + ifar.ifar_uuids = calloc(ifar.ifar_count, sizeof(uuid_t)); + if (ifar.ifar_uuids != NULL) { + if (ioctl(s, SIOCGIFAGENTIDS, &ifar) != 1) { + for (int agent_i = 0; agent_i < ifar.ifar_count; agent_i++) { + struct netagent_req nar; + memset(&nar, 0, sizeof(nar)); + + uuid_copy(nar.netagent_uuid, ifar.ifar_uuids[agent_i]); + + if (ioctl(s, SIOCGIFAGENTDATA, &nar) != 1) { + printf("\tagent domain:%s type:%s flags:0x%x desc:\"%s\"\n", + nar.netagent_domain, nar.netagent_type, + nar.netagent_flags, nar.netagent_desc); + } + } + } + free(ifar.ifar_uuids); + } + } + } + } + + if (ioctl(s, SIOCGIFLINKQUALITYMETRIC, &ifr) != -1) { + int lqm = ifr.ifr_link_quality_metric; + if (verbose > 1) { + printf("\tlink quality: %d ", lqm); + if (lqm == IFNET_LQM_THRESH_OFF) + printf("(off)"); + else if (lqm == IFNET_LQM_THRESH_UNKNOWN) + printf("(unknown)"); + else if (lqm > IFNET_LQM_THRESH_UNKNOWN && + lqm <= IFNET_LQM_THRESH_BAD) + printf("(bad)"); + else if (lqm > IFNET_LQM_THRESH_UNKNOWN && + lqm <= IFNET_LQM_THRESH_POOR) + printf("(poor)"); + else if (lqm > IFNET_LQM_THRESH_POOR && + lqm <= IFNET_LQM_THRESH_GOOD) + printf("(good)"); + else + printf("(?)"); + printf("\n"); + } else if (lqm > IFNET_LQM_THRESH_UNKNOWN) { + printf("\tlink quality: %d ", lqm); + if (lqm <= IFNET_LQM_THRESH_BAD) + printf("(bad)"); + else if (lqm <= IFNET_LQM_THRESH_POOR) + printf("(poor)"); + else if (lqm <= IFNET_LQM_THRESH_GOOD) + printf("(good)"); + else + printf("(?)"); + printf("\n"); + } + } + + if (verbose > 0) { + if (ioctl(s, SIOCGIFINTERFACESTATE, &ifr) != -1) { + printf("\tstate"); + if (ifr.ifr_interface_state.valid_bitmask & + IF_INTERFACE_STATE_RRC_STATE_VALID) { + uint8_t rrc_state = ifr.ifr_interface_state.rrc_state; + + printf(" rrc: %u ", rrc_state); + if (rrc_state == IF_INTERFACE_STATE_RRC_STATE_CONNECTED) + printf("(connected)"); + else if (rrc_state == IF_INTERFACE_STATE_RRC_STATE_IDLE) + printf("(idle)"); + else + printf("(?)"); + } + if (ifr.ifr_interface_state.valid_bitmask & + IF_INTERFACE_STATE_INTERFACE_AVAILABILITY_VALID) { + uint8_t ifavail = ifr.ifr_interface_state.interface_availability; + + printf(" availability: %u ", ifavail); + if (ifavail == IF_INTERFACE_STATE_INTERFACE_AVAILABLE) + printf("(true)"); + else if (ifavail == IF_INTERFACE_STATE_INTERFACE_UNAVAILABLE) + printf("(false)"); + else + printf("(?)"); + } else { + printf(" availability: (not valid)"); + } + if (verbose > 1 && + ifr.ifr_interface_state.valid_bitmask & + IF_INTERFACE_STATE_LQM_STATE_VALID) { + int8_t lqm = ifr.ifr_interface_state.lqm_state; + + printf(" lqm: %d", lqm); + + if (lqm == IFNET_LQM_THRESH_OFF) + printf("(off)"); + else if (lqm == IFNET_LQM_THRESH_UNKNOWN) + printf("(unknown)"); + else if (lqm == IFNET_LQM_THRESH_BAD) + printf("(bad)"); + else if (lqm == IFNET_LQM_THRESH_POOR) + printf("(poor)"); + else if (lqm == IFNET_LQM_THRESH_GOOD) + printf("(good)"); + else + printf("(?)"); + } + } + printf("\n"); + } + + bzero(&iflpr, sizeof (iflpr)); + strncpy(iflpr.iflpr_name, name, sizeof (iflpr.iflpr_name)); + if (ioctl(s, SIOCGIFLINKPARAMS, &iflpr) != -1) { + u_int64_t ibw_max = iflpr.iflpr_input_bw.max_bw; + u_int64_t ibw_eff = iflpr.iflpr_input_bw.eff_bw; + u_int64_t obw_max = iflpr.iflpr_output_bw.max_bw; + u_int64_t obw_eff = iflpr.iflpr_output_bw.eff_bw; + u_int64_t obw_tbr = iflpr.iflpr_output_tbr_rate; + u_int32_t obw_pct = iflpr.iflpr_output_tbr_percent; + u_int64_t ilt_max = iflpr.iflpr_input_lt.max_lt; + u_int64_t ilt_eff = iflpr.iflpr_input_lt.eff_lt; + u_int64_t olt_max = iflpr.iflpr_output_lt.max_lt; + u_int64_t olt_eff = iflpr.iflpr_output_lt.eff_lt; + + + if (eflags & IFEF_TXSTART) { + u_int32_t flags = iflpr.iflpr_flags; + u_int32_t sched = iflpr.iflpr_output_sched; + struct if_throttlereq iftr; + + printf("\tscheduler: %s%s ", + (flags & IFLPRF_ALTQ) ? "ALTQ_" : "", + sched2str(sched)); + if (flags & IFLPRF_DRVMANAGED) + printf("(driver managed)"); + printf("\n"); + + bzero(&iftr, sizeof (iftr)); + strncpy(iftr.ifthr_name, name, + sizeof (iftr.ifthr_name)); + if (ioctl(s, SIOCGIFTHROTTLE, &iftr) != -1 && + iftr.ifthr_level != IFNET_THROTTLE_OFF) + printf("\tthrottling: level %d (%s)\n", + iftr.ifthr_level, tl2str(iftr.ifthr_level)); + } + + if (obw_tbr != 0 && obw_eff > obw_tbr) + obw_eff = obw_tbr; + + if (ibw_max != 0 || obw_max != 0) { + if (ibw_max == obw_max && ibw_eff == obw_eff && + ibw_max == ibw_eff && obw_tbr == 0) { + printf("\tlink rate: %s\n", + bps_to_str(ibw_max)); + } else { + printf("\tuplink rate: %s [eff] / ", + bps_to_str(obw_eff)); + if (obw_tbr != 0) { + if (obw_pct == 0) + printf("%s [tbr] / ", + bps_to_str(obw_tbr)); + else + printf("%s [tbr %u%%] / ", + bps_to_str(obw_tbr), + obw_pct); + } + printf("%s", bps_to_str(obw_max)); + if (obw_tbr != 0) + printf(" [max]"); + printf("\n"); + if (ibw_eff == ibw_max) { + printf("\tdownlink rate: %s\n", + bps_to_str(ibw_max)); + } else { + printf("\tdownlink rate: " + "%s [eff] / ", bps_to_str(ibw_eff)); + printf("%s [max]\n", + bps_to_str(ibw_max)); + } + } + } else if (obw_tbr != 0) { + printf("\tuplink rate: %s [tbr]\n", + bps_to_str(obw_tbr)); + } + + if (ilt_max != 0 || olt_max != 0) { + if (ilt_max == olt_max && ilt_eff == olt_eff && + ilt_max == ilt_eff) { + printf("\tlink latency: %s\n", + ns_to_str(ilt_max)); + } else { + if (olt_max != 0 && olt_eff == olt_max) { + printf("\tuplink latency: %s\n", + ns_to_str(olt_max)); + } else if (olt_max != 0) { + printf("\tuplink latency: " + "%s [eff] / ", ns_to_str(olt_eff)); + printf("%s [max]\n", + ns_to_str(olt_max)); + } + if (ilt_max != 0 && ilt_eff == ilt_max) { + printf("\tdownlink latency: %s\n", + ns_to_str(ilt_max)); + } else if (ilt_max != 0) { + printf("\tdownlink latency: " + "%s [eff] / ", ns_to_str(ilt_eff)); + printf("%s [max]\n", + ns_to_str(ilt_max)); + } + } + } + } + + /* Common OID prefix */ + mib[0] = CTL_NET; + mib[1] = PF_LINK; + mib[2] = NETLINK_GENERIC; + mib[3] = IFMIB_IFDATA; + mib[4] = if_nametoindex(name); + mib[5] = IFDATA_SUPPLEMENTAL; + if (sysctl(mib, 6, &ifmsupp, &miblen, (void *)0, 0) == -1) + err(1, "sysctl IFDATA_SUPPLEMENTAL"); + + if (ifmsupp.ifmd_data_extended.ifi_alignerrs != 0) { + printf("\tunaligned pkts: %llu\n", + ifmsupp.ifmd_data_extended.ifi_alignerrs); + } + if (ifmsupp.ifmd_data_extended.ifi_dt_bytes != 0) { + printf("\tdata milestone interval: %s\n", + bytes_to_str(ifmsupp.ifmd_data_extended.ifi_dt_bytes)); + } + + bzero(&ifdr, sizeof (ifdr)); + strncpy(ifdr.ifdr_name, name, sizeof (ifdr.ifdr_name)); + if (ioctl(s, SIOCGIFDESC, &ifdr) != -1 && ifdr.ifdr_len) { + printf("\tdesc: %s\n", ifdr.ifdr_desc); + } + + if (ioctl(s, SIOCGIFLOG, &ifr) != -1 && ifr.ifr_log.ifl_level) { + printf("\tlogging: level %d ", ifr.ifr_log.ifl_level); + printb("facilities", ifr.ifr_log.ifl_flags, IFRLOGF_BITS); + putchar('\n'); + } + + if (ioctl(s, SIOCGIFDELEGATE, &ifr) != -1 && ifr.ifr_delegated) { + char delegatedif[IFNAMSIZ+1]; + if (if_indextoname(ifr.ifr_delegated, delegatedif) != NULL) + printf("\teffective interface: %s\n", delegatedif); + } + + if (ioctl(s, SIOCGSTARTDELAY, &ifr) != -1) { + if (ifr.ifr_start_delay_qlen > 0 && + ifr.ifr_start_delay_timeout > 0) { + printf("\ttxstart qlen: %u packets " + "timeout: %u microseconds\n", + ifr.ifr_start_delay_qlen, + ifr.ifr_start_delay_timeout/1000); + } + } +#if defined(IFCAP_HW_TIMESTAMP) && defined(IFCAP_SW_TIMESTAMP) + if ((curcap & (IFCAP_HW_TIMESTAMP | IFCAP_SW_TIMESTAMP)) && + ioctl(s, SIOCGIFTIMESTAMPENABLED, &ifr) != -1) { + printf("\ttimestamp: %s\n", + (ifr.ifr_intval != 0) ? "enabled" : "disabled"); + } +#endif +#if defined(SIOCGQOSMARKINGENABLED) && defined(SIOCGQOSMARKINGMODE) + if (ioctl(s, SIOCGQOSMARKINGENABLED, &ifr) != -1) { + printf("\tqosmarking enabled: %s mode: ", + ifr.ifr_qosmarking_enabled ? "yes" : "no"); + if (ioctl(s, SIOCGQOSMARKINGMODE, &ifr) != -1) { + switch (ifr.ifr_qosmarking_mode) { + case IFRTYPE_QOSMARKING_FASTLANE: + printf("fastlane\n"); + break; + case IFRTYPE_QOSMARKING_MODE_NONE: + printf("none\n"); + break; + default: + printf("unknown (%u)\n", ifr.ifr_qosmarking_mode); + break; + } + } + } +#endif /* defined(SIOCGQOSMARKINGENABLED) && defined(SIOCGQOSMARKINGMODE) */ +done: + close(s); + return; +} + +#define KILOBYTES 1024 +#define MEGABYTES (KILOBYTES * KILOBYTES) +#define GIGABYTES (KILOBYTES * KILOBYTES * KILOBYTES) + +static char * +bytes_to_str(unsigned long long bytes) +{ + static char buf[32]; + const char *u; + long double n = bytes, t; + + if (bytes >= GIGABYTES) { + t = n / GIGABYTES; + u = "GB"; + } else if (n >= MEGABYTES) { + t = n / MEGABYTES; + u = "MB"; + } else if (n >= KILOBYTES) { + t = n / KILOBYTES; + u = "KB"; + } else { + t = n; + u = "bytes"; + } + + snprintf(buf, sizeof (buf), "%-4.2Lf %s", t, u); + return (buf); +} + +#define GIGABIT_PER_SEC 1000000000 /* gigabit per second */ +#define MEGABIT_PER_SEC 1000000 /* megabit per second */ +#define KILOBIT_PER_SEC 1000 /* kilobit per second */ + +static char * +bps_to_str(unsigned long long rate) +{ + static char buf[32]; + const char *u; + long double n = rate, t; + + if (rate >= GIGABIT_PER_SEC) { + t = n / GIGABIT_PER_SEC; + u = "Gbps"; + } else if (n >= MEGABIT_PER_SEC) { + t = n / MEGABIT_PER_SEC; + u = "Mbps"; + } else if (n >= KILOBIT_PER_SEC) { + t = n / KILOBIT_PER_SEC; + u = "Kbps"; + } else { + t = n; + u = "bps "; + } + + snprintf(buf, sizeof (buf), "%-4.2Lf %4s", t, u); + return (buf); +} + +#define NSEC_PER_SEC 1000000000 /* nanosecond per second */ +#define USEC_PER_SEC 1000000 /* microsecond per second */ +#define MSEC_PER_SEC 1000 /* millisecond per second */ + +static char * +ns_to_str(unsigned long long nsec) +{ + static char buf[32]; + const char *u; + long double n = nsec, t; + + if (nsec >= NSEC_PER_SEC) { + t = n / NSEC_PER_SEC; + u = "sec "; + } else if (n >= USEC_PER_SEC) { + t = n / USEC_PER_SEC; + u = "msec"; + } else if (n >= MSEC_PER_SEC) { + t = n / MSEC_PER_SEC; + u = "usec"; + } else { + t = n; + u = "nsec"; + } + + snprintf(buf, sizeof (buf), "%-4.2Lf %4s", t, u); + return (buf); +} + +static void +tunnel_status(int s) +{ + af_all_tunnel_status(s); +} + +void +Perror(const char *cmd) +{ + switch (errno) { + + case ENXIO: + errx(1, "%s: no such interface", cmd); + break; + + case EPERM: + errx(1, "%s: permission denied", cmd); + break; + + default: + err(1, "%s", cmd); + } +} + +/* + * Print a value a la the %b format of the kernel's printf + */ +void +printb(const char *s, unsigned v, const char *bits) +{ + int i, any = 0; + char c; + + if (bits && *bits == 8) + printf("%s=%o", s, v); + else + printf("%s=%x", s, v); + bits++; + if (bits) { + putchar('<'); + while ((i = *bits++) != '\0') { + if (v & (1 << (i-1))) { + if (any) + putchar(','); + any = 1; + for (; (c = *bits) > 32; bits++) + putchar(c); + } else + for (; *bits > 32; bits++) + ; + } + putchar('>'); + } +} + +#ifndef __APPLE__ +void +ifmaybeload(const char *name) +{ +#define MOD_PREFIX_LEN 3 /* "if_" */ + struct module_stat mstat; + int fileid, modid; + char ifkind[IFNAMSIZ + MOD_PREFIX_LEN], ifname[IFNAMSIZ], *dp; + const char *cp; + + /* loading suppressed by the user */ + if (noload) + return; + + /* trim the interface number off the end */ + strlcpy(ifname, name, sizeof(ifname)); + for (dp = ifname; *dp != 0; dp++) + if (isdigit(*dp)) { + *dp = 0; + break; + } + + /* turn interface and unit into module name */ + strlcpy(ifkind, "if_", sizeof(ifkind)); + strlcpy(ifkind + MOD_PREFIX_LEN, ifname, + sizeof(ifkind) - MOD_PREFIX_LEN); + + /* scan files in kernel */ + mstat.version = sizeof(struct module_stat); + for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) { + /* scan modules in file */ + for (modid = kldfirstmod(fileid); modid > 0; + modid = modfnext(modid)) { + if (modstat(modid, &mstat) < 0) + continue; + /* strip bus name if present */ + if ((cp = strchr(mstat.name, '/')) != NULL) { + cp++; + } else { + cp = mstat.name; + } + /* already loaded? */ + if (strncmp(ifname, cp, strlen(ifname) + 1) == 0 || + strncmp(ifkind, cp, strlen(ifkind) + 1) == 0) + return; + } + } + + /* not present, we should try to load it */ + kldload(ifkind); +} +#endif + +static struct cmd basic_cmds[] = { + DEF_CMD("up", IFF_UP, setifflags), + DEF_CMD("down", -IFF_UP, setifflags), + DEF_CMD("arp", -IFF_NOARP, setifflags), + DEF_CMD("-arp", IFF_NOARP, setifflags), + DEF_CMD("debug", IFF_DEBUG, setifflags), + DEF_CMD("-debug", -IFF_DEBUG, setifflags), +#ifdef IFF_PPROMISC + DEF_CMD("promisc", IFF_PPROMISC, setifflags), + DEF_CMD("-promisc", -IFF_PPROMISC, setifflags), +#endif /* IFF_PPROMISC */ + DEF_CMD("add", IFF_UP, notealias), + DEF_CMD("alias", IFF_UP, notealias), + DEF_CMD("-alias", -IFF_UP, notealias), + DEF_CMD("delete", -IFF_UP, notealias), + DEF_CMD("remove", -IFF_UP, notealias), +#ifdef notdef +#define EN_SWABIPS 0x1000 + DEF_CMD("swabips", EN_SWABIPS, setifflags), + DEF_CMD("-swabips", -EN_SWABIPS, setifflags), +#endif + DEF_CMD_ARG("netmask", setifnetmask), + DEF_CMD_ARG("metric", setifmetric), + DEF_CMD_ARG("broadcast", setifbroadaddr), + DEF_CMD_ARG("ipdst", setifipdst), + DEF_CMD_ARG2("tunnel", settunnel), + DEF_CMD("-tunnel", 0, deletetunnel), + DEF_CMD("deletetunnel", 0, deletetunnel), + DEF_CMD("link0", IFF_LINK0, setifflags), + DEF_CMD("-link0", -IFF_LINK0, setifflags), + DEF_CMD("link1", IFF_LINK1, setifflags), + DEF_CMD("-link1", -IFF_LINK1, setifflags), + DEF_CMD("link2", IFF_LINK2, setifflags), + DEF_CMD("-link2", -IFF_LINK2, setifflags), +#ifdef IFF_MONITOR + DEF_CMD("monitor", IFF_MONITOR:, setifflags), + DEF_CMD("-monitor", -IFF_MONITOR, setifflags), +#endif /* IFF_MONITOR */ +#ifdef IFF_STATICARP + DEF_CMD("staticarp", IFF_STATICARP, setifflags), + DEF_CMD("-staticarp", -IFF_STATICARP, setifflags), +#endif /* IFF_STATICARP */ +#ifdef IFCAP_RXCSUM + DEF_CMD("rxcsum", IFCAP_RXCSUM, setifcap), + DEF_CMD("-rxcsum", -IFCAP_RXCSUM, setifcap), +#endif /* IFCAP_RXCSUM */ +#ifdef IFCAP_TXCSUM + DEF_CMD("txcsum", IFCAP_TXCSUM, setifcap), + DEF_CMD("-txcsum", -IFCAP_TXCSUM, setifcap), +#endif /* IFCAP_TXCSUM */ +#ifdef IFCAP_NETCONS + DEF_CMD("netcons", IFCAP_NETCONS, setifcap), + DEF_CMD("-netcons", -IFCAP_NETCONS, setifcap), +#endif /* IFCAP_NETCONS */ +#ifdef IFCAP_POLLING + DEF_CMD("polling", IFCAP_POLLING, setifcap), + DEF_CMD("-polling", -IFCAP_POLLING, setifcap), +#endif /* IFCAP_POLLING */ +#ifdef IFCAP_TSO + DEF_CMD("tso", IFCAP_TSO, setifcap), + DEF_CMD("-tso", -IFCAP_TSO, setifcap), +#endif /* IFCAP_TSO */ +#ifdef IFCAP_LRO + DEF_CMD("lro", IFCAP_LRO, setifcap), + DEF_CMD("-lro", -IFCAP_LRO, setifcap), +#endif /* IFCAP_LRO */ +#ifdef IFCAP_WOL + DEF_CMD("wol", IFCAP_WOL, setifcap), + DEF_CMD("-wol", -IFCAP_WOL, setifcap), +#endif /* IFCAP_WOL */ +#ifdef IFCAP_WOL_UCAST + DEF_CMD("wol_ucast", IFCAP_WOL_UCAST, setifcap), + DEF_CMD("-wol_ucast", -IFCAP_WOL_UCAST, setifcap), +#endif /* IFCAP_WOL_UCAST */ +#ifdef IFCAP_WOL_MCAST + DEF_CMD("wol_mcast", IFCAP_WOL_MCAST, setifcap), + DEF_CMD("-wol_mcast", -IFCAP_WOL_MCAST, setifcap), +#endif /* IFCAP_WOL_MCAST */ +#ifdef IFCAP_WOL_MAGIC + DEF_CMD("wol_magic", IFCAP_WOL_MAGIC, setifcap), + DEF_CMD("-wol_magic", -IFCAP_WOL_MAGIC, setifcap), +#endif /* IFCAP_WOL_MAGIC */ + DEF_CMD("normal", -IFF_LINK0, setifflags), + DEF_CMD("compress", IFF_LINK0, setifflags), + DEF_CMD("noicmp", IFF_LINK1, setifflags), + DEF_CMD_ARG("mtu", setifmtu), +#ifdef notdef + DEF_CMD_ARG("name", setifname), +#endif /* notdef */ +#ifdef IFCAP_AV + DEF_CMD("av", IFCAP_AV, setifcap), + DEF_CMD("-av", -IFCAP_AV, setifcap), +#endif /* IFCAP_AV */ + DEF_CMD("router", 1, setrouter), + DEF_CMD("-router", 0, setrouter), + DEF_CMD_ARG("desc", setifdesc), + DEF_CMD_ARG("tbr", settbr), + DEF_CMD_ARG("throttle", setthrottle), + DEF_CMD_ARG("log", setlog), + DEF_CMD("cl2k", 1, setcl2k), + DEF_CMD("-cl2k", 0, setcl2k), + DEF_CMD("expensive", 1, setexpensive), + DEF_CMD("-expensive", 0, setexpensive), + DEF_CMD("timestamp", 1, settimestamp), + DEF_CMD("-timestamp", 0, settimestamp), + DEF_CMD_ARG("ecn", setecnmode), + DEF_CMD_ARG2("fastlane", setfastlane), + DEF_CMD_ARG2("qosmarking", setqosmarking), + DEF_CMD_ARG("disable_output", setdisableoutput), +}; + +static __constructor void +ifconfig_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(basic_cmds); i++) + cmd_register(&basic_cmds[i]); +#undef N +} + +static char * +sched2str(unsigned int s) +{ + char *c; + + switch (s) { + case PKTSCHEDT_NONE: + c = "NONE"; + break; + case PKTSCHEDT_TCQ: + c = "TCQ"; + break; + case PKTSCHEDT_QFQ: + c = "QFQ"; + break; + case PKTSCHEDT_FQ_CODEL: + c = "FQ_CODEL"; + break; + default: + c = "UNKNOWN"; + break; + } + + return (c); +} + +static char * +tl2str(unsigned int s) +{ + char *c; + + switch (s) { + case IFNET_THROTTLE_OFF: + c = "off"; + break; + case IFNET_THROTTLE_OPPORTUNISTIC: + c = "opportunistic"; + break; + default: + c = "unknown"; + break; + } + + return (c); +} + +static char * +ift2str(unsigned int t, unsigned int f, unsigned int sf) +{ + static char buf[256]; + char *c = NULL; + + switch (t) { + case IFT_ETHER: + switch (sf) { + case IFRTYPE_SUBFAMILY_USB: + c = "USB Ethernet"; + break; + case IFRTYPE_SUBFAMILY_BLUETOOTH: + c = "Bluetooth PAN"; + break; + case IFRTYPE_SUBFAMILY_WIFI: + c = "Wi-Fi"; + break; + case IFRTYPE_SUBFAMILY_THUNDERBOLT: + c = "IP over Thunderbolt"; + break; + case IFRTYPE_SUBFAMILY_ANY: + default: + c = "Ethernet"; + break; + } + break; + + case IFT_IEEE1394: + c = "IP over FireWire"; + break; + + case IFT_PKTAP: + c = "Packet capture"; + break; + + case IFT_CELLULAR: + c = "Cellular"; + break; + + case IFT_BRIDGE: + case IFT_PFLOG: + case IFT_PFSYNC: + case IFT_OTHER: + case IFT_PPP: + case IFT_LOOP: + case IFT_GIF: + case IFT_STF: + case IFT_L2VLAN: + case IFT_IEEE8023ADLAG: + default: + break; + } + + if (verbose > 1) { + if (c == NULL) { + (void) snprintf(buf, sizeof (buf), + "0x%x family: %u subfamily: %u", + ifr.ifr_type.ift_type, ifr.ifr_type.ift_family, + ifr.ifr_type.ift_subfamily); + } else { + (void) snprintf(buf, sizeof (buf), + "%s (0x%x) family: %u subfamily: %u", c, + ifr.ifr_type.ift_type, ifr.ifr_type.ift_family, + ifr.ifr_type.ift_subfamily); + } + c = buf; + } + + return (c); +} diff --git a/network_cmds-543/ifconfig.tproj/ifconfig.h b/network_cmds-543/ifconfig.tproj/ifconfig.h new file mode 100644 index 00000000..1a89a335 --- /dev/null +++ b/network_cmds-543/ifconfig.tproj/ifconfig.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2009-2011 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* + * Copyright (c) 1997 Peter Wemm. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the FreeBSD Project + * by Peter Wemm. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + * + * so there! + * + * $FreeBSD: src/sbin/ifconfig/ifconfig.h,v 1.21.2.1.2.1 2008/11/25 02:59:29 kensmith Exp $ + */ + +#define __constructor __attribute__((constructor)) + +struct afswtch; +struct cmd; + +typedef void c_func(const char *cmd, int arg, int s, const struct afswtch *afp); +typedef void c_func2(const char *arg1, const char *arg2, int s, const struct afswtch *afp); + +struct cmd { + const char *c_name; + int c_parameter; +#define NEXTARG 0xffffff /* has following arg */ +#define NEXTARG2 0xfffffe /* has 2 following args */ +#define OPTARG 0xfffffd /* has optional following arg */ + union { + c_func *c_func; + c_func2 *c_func2; + } c_u; + int c_iscloneop; + struct cmd *c_next; +}; +void cmd_register(struct cmd *); + +typedef void callback_func(int s, void *); +void callback_register(callback_func *, void *); + +/* + * Macros for declaring command functions and initializing entries. + */ +#define DECL_CMD_FUNC(name, cmd, arg) \ + void name(const char *cmd, int arg, int s, const struct afswtch *afp) +#define DECL_CMD_FUNC2(name, arg1, arg2) \ + void name(const char *arg1, const char *arg2, int s, const struct afswtch *afp) + +#define DEF_CMD(name, param, func) { name, param, { .c_func = func } } +#define DEF_CMD_ARG(name, func) { name, NEXTARG, { .c_func = func } } +#define DEF_CMD_OPTARG(name, func) { name, OPTARG, { .c_func = func } } +#define DEF_CMD_ARG2(name, func) { name, NEXTARG2, { .c_func2 = func } } +#define DEF_CLONE_CMD(name, param, func) { name, param, { .c_func = func }, 1 } +#define DEF_CLONE_CMD_ARG(name, func) { name, NEXTARG, { .c_func = func }, 1 } + +struct ifaddrs; +struct addrinfo; + +enum { + RIDADDR, + ADDR, + MASK, + DSTADDR, +}; + +struct afswtch { + const char *af_name; /* as given on cmd line, e.g. "inet" */ + short af_af; /* AF_* */ + /* + * Status is handled one of two ways; if there is an + * address associated with the interface then the + * associated address family af_status method is invoked + * with the appropriate addressin info. Otherwise, if + * all possible info is to be displayed and af_other_status + * is defined then it is invoked after all address status + * is presented. + */ + void (*af_status)(int, const struct ifaddrs *); + void (*af_other_status)(int); + /* parse address method */ + void (*af_getaddr)(const char *, int); + /* parse prefix method (IPv6) */ + void (*af_getprefix)(const char *, int); + void (*af_postproc)(int s, const struct afswtch *); + u_long af_difaddr; /* set dst if address ioctl */ + u_long af_aifaddr; /* set if address ioctl */ + void *af_ridreq; /* */ + void *af_addreq; /* */ + struct afswtch *af_next; + + /* XXX doesn't fit model */ + void (*af_status_tunnel)(int); + void (*af_settunnel)(int s, struct addrinfo *srcres, + struct addrinfo *dstres); + + void (*af_setrouter)(int, int); +}; +void af_register(struct afswtch *); + +struct option { + const char *opt; + const char *opt_usage; + void (*cb)(const char *arg); + struct option *next; +}; +void opt_register(struct option *); + +extern struct ifreq ifr; +extern char name[IFNAMSIZ]; /* name of interface */ +extern int allmedia; +extern int supmedia; +extern int printkeys; +extern int newaddr; +extern int verbose; +extern int all; + +void setifcap(const char *, int value, int s, const struct afswtch *); + +void Perror(const char *cmd); +void printb(const char *s, unsigned value, const char *bits); + +void ifmaybeload(const char *name); + +typedef void clone_callback_func(int, struct ifreq *); +void clone_setcallback(clone_callback_func *); + +/* + * XXX expose this so modules that neeed to know of any pending + * operations on ifmedia can avoid cmd line ordering confusion. + */ +struct ifmediareq *ifmedia_getstate(int s); diff --git a/network_cmds-543/ifconfig.tproj/iffake.c b/network_cmds-543/ifconfig.tproj/iffake.c new file mode 100644 index 00000000..c8065a28 --- /dev/null +++ b/network_cmds-543/ifconfig.tproj/iffake.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2017 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * iffake.c + * - manage fake interfaces that pretend to be e.g. ethernet + */ + +/* + * Modification History: + * + * January 17, 2017 Dieter Siegmund (dieter@apple.com) + * - created + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ifconfig.h" + +static void +fake_status(int s) +{ + struct ifdrv ifd; + struct if_fake_request iffr; + + bzero((char *)&ifd, sizeof(ifd)); + bzero((char *)&iffr, sizeof(iffr)); + strlcpy(ifd.ifd_name, ifr.ifr_name, sizeof(ifd.ifd_name)); + ifd.ifd_cmd = IF_FAKE_G_CMD_GET_PEER; + ifd.ifd_len = sizeof(iffr); + ifd.ifd_data = &iffr; + if (ioctl(s, SIOCGDRVSPEC, &ifd) < 0) { + return; + } + if (iffr.iffr_peer_name[0] == '\0') { + printf("\tpeer: \n"); + } else { + printf("\tpeer: %s\n", iffr.iffr_peer_name); + } + return; +} + +static void +set_peer(int s, const char * operation, const char * val) +{ + struct ifdrv ifd; + struct if_fake_request iffr; + + bzero((char *)&ifd, sizeof(ifd)); + bzero((char *)&iffr, sizeof(iffr)); + strlcpy(ifd.ifd_name, ifr.ifr_name, sizeof(ifd.ifd_name)); + ifd.ifd_cmd = IF_FAKE_S_CMD_SET_PEER; + ifd.ifd_len = sizeof(iffr); + ifd.ifd_data = &iffr; + if (val != NULL) { + strlcpy(iffr.iffr_peer_name, val, sizeof(iffr.iffr_peer_name)); + } + if (ioctl(s, SIOCSDRVSPEC, &ifd) < 0) { + err(1, "SIOCDRVSPEC %s peer", operation); + } + return; +} + +static +DECL_CMD_FUNC(setpeer, val, d) +{ + set_peer(s, "set", val); + return; +} + +static +DECL_CMD_FUNC(unsetpeer, val, d) +{ + set_peer(s, "unset", NULL); + return; +} + +static struct cmd fake_cmds[] = { + DEF_CLONE_CMD_ARG("peer", setpeer), + DEF_CMD_OPTARG("-peer", unsetpeer), +}; +static struct afswtch af_fake = { + .af_name = "af_fake", + .af_af = AF_UNSPEC, + .af_other_status = fake_status, +}; + +static __constructor void +fake_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(fake_cmds); i++) + cmd_register(&fake_cmds[i]); + af_register(&af_fake); +#undef N +} + diff --git a/network_cmds-543/ifconfig.tproj/ifmedia.c b/network_cmds-543/ifconfig.tproj/ifmedia.c new file mode 100644 index 00000000..5c364062 --- /dev/null +++ b/network_cmds-543/ifconfig.tproj/ifmedia.c @@ -0,0 +1,872 @@ +/* $NetBSD: ifconfig.c,v 1.34 1997/04/21 01:17:58 lukem Exp $ */ +/* $FreeBSD: src/sbin/ifconfig/ifmedia.c,v 1.25.6.1 2008/11/25 02:59:29 kensmith Exp $ */ + +/* + * Copyright (c) 1997 Jason R. Thorpe. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project + * by Jason R. Thorpe. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +/* + * Copyright (c) 1983, 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ifconfig.h" + +static void domediaopt(const char *, int, int); +static int get_media_subtype(int, const char *); +#ifdef notdef +static int get_media_mode(int, const char *); +#endif +static int get_media_options(int, const char *); +static int lookup_media_word(struct ifmedia_description *, const char *); +static void print_media_word(int, int); +static void print_media_word_ifconfig(int); + +static struct ifmedia_description *get_toptype_desc(int); +static struct ifmedia_type_to_subtype *get_toptype_ttos(int); +static struct ifmedia_description *get_subtype_desc(int, + struct ifmedia_type_to_subtype *ttos); + +static void +media_status(int s) +{ + struct ifmediareq ifmr; + int *media_list, i; + + (void) memset(&ifmr, 0, sizeof(ifmr)); + (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name)); + + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { + /* + * Interface doesn't support SIOC{G,S}IFMEDIA. + */ + return; + } + + if (ifmr.ifm_count == 0) { + warnx("%s: no media types?", name); + return; + } + + media_list = (int *)malloc(ifmr.ifm_count * sizeof(int)); + if (media_list == NULL) + err(1, "malloc"); + ifmr.ifm_ulist = media_list; + + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) + err(1, "SIOCGIFMEDIA"); + + printf("\tmedia: "); + print_media_word(ifmr.ifm_current, 1); + if (ifmr.ifm_active != ifmr.ifm_current) { + putchar(' '); + putchar('('); + print_media_word(ifmr.ifm_active, 0); + putchar(')'); + } + + putchar('\n'); + + if (ifmr.ifm_status & IFM_AVALID) { + printf("\tstatus: "); +#ifdef notdef + switch (IFM_TYPE(ifmr.ifm_active)) { + case IFM_ETHER: + case IFM_ATM: + if (ifmr.ifm_status & IFM_ACTIVE) + printf("active"); + else + printf("no carrier"); + break; + + case IFM_FDDI: + case IFM_TOKEN: + if (ifmr.ifm_status & IFM_ACTIVE) + printf("inserted"); + else + printf("no ring"); + break; + + case IFM_IEEE80211: + /* XXX: Different value for adhoc? */ + if (ifmr.ifm_status & IFM_ACTIVE) + printf("associated"); + else + printf("no carrier"); + break; + } +#else + if (ifmr.ifm_status & IFM_ACTIVE) + printf("active"); + else + printf("inactive"); +#endif + putchar('\n'); + } + + if (ifmr.ifm_count > 0 && supmedia) { + printf("\tsupported media:\n"); + for (i = 0; i < ifmr.ifm_count; i++) { + printf("\t\t"); + print_media_word_ifconfig(media_list[i]); + putchar('\n'); + } + } + + free(media_list); +} + +struct ifmediareq * +ifmedia_getstate(int s) +{ + static struct ifmediareq *ifmr = NULL; + int *mwords; + + if (ifmr == NULL) { + ifmr = (struct ifmediareq *)malloc(sizeof(struct ifmediareq)); + if (ifmr == NULL) + err(1, "malloc"); + + (void) memset(ifmr, 0, sizeof(struct ifmediareq)); + (void) strncpy(ifmr->ifm_name, name, + sizeof(ifmr->ifm_name)); + + ifmr->ifm_count = 0; + ifmr->ifm_ulist = NULL; + + /* + * We must go through the motions of reading all + * supported media because we need to know both + * the current media type and the top-level type. + */ + + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)ifmr) < 0) { + err(1, "SIOCGIFMEDIA"); + } + + if (ifmr->ifm_count == 0) + errx(1, "%s: no media types?", name); + + mwords = (int *)malloc(ifmr->ifm_count * sizeof(int)); + if (mwords == NULL) + err(1, "malloc"); + + ifmr->ifm_ulist = mwords; + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)ifmr) < 0) + err(1, "SIOCGIFMEDIA"); + } + + return ifmr; +} + +static void +setifmediacallback(int s, void *arg) +{ + struct ifmediareq *ifmr = (struct ifmediareq *)arg; + static int did_it = 0; + + if (!did_it) { + ifr.ifr_media = ifmr->ifm_current; + if (ioctl(s, SIOCSIFMEDIA, (caddr_t)&ifr) < 0) + err(1, "SIOCSIFMEDIA (media)"); + free(ifmr->ifm_ulist); + free(ifmr); + did_it = 1; + } +} + +static void +setmedia(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifmediareq *ifmr; + int subtype; + + ifmr = ifmedia_getstate(s); + + /* + * We are primarily concerned with the top-level type. + * However, "current" may be only IFM_NONE, so we just look + * for the top-level type in the first "supported type" + * entry. + * + * (I'm assuming that all supported media types for a given + * interface will be the same top-level type..) + */ + subtype = get_media_subtype(IFM_TYPE(ifmr->ifm_ulist[0]), val); + + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_media = (ifmr->ifm_current & ~(IFM_NMASK|IFM_TMASK)) | + IFM_TYPE(ifmr->ifm_ulist[0]) | subtype; + + if ((ifr.ifr_media & IFM_TMASK) == 0) { + ifr.ifr_media &= ~IFM_GMASK; + } + + ifmr->ifm_current = ifr.ifr_media; + callback_register(setifmediacallback, (void *)ifmr); +} + +static void +setmediaopt(const char *val, int d, int s, const struct afswtch *afp) +{ + + domediaopt(val, 0, s); +} + +static void +unsetmediaopt(const char *val, int d, int s, const struct afswtch *afp) +{ + + domediaopt(val, 1, s); +} + +static void +domediaopt(const char *val, int clear, int s) +{ + struct ifmediareq *ifmr; + int options; + + ifmr = ifmedia_getstate(s); + + options = get_media_options(IFM_TYPE(ifmr->ifm_ulist[0]), val); + + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_media = ifmr->ifm_current; + if (clear) + ifr.ifr_media &= ~options; + else { + if (options & IFM_HDX) { + ifr.ifr_media &= ~IFM_FDX; + options &= ~IFM_HDX; + } + ifr.ifr_media |= options; + } + ifmr->ifm_current = ifr.ifr_media; + callback_register(setifmediacallback, (void *)ifmr); +} + +static void +setmediainst(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifmediareq *ifmr; + int inst; + + ifmr = ifmedia_getstate(s); + + inst = atoi(val); + if (inst < 0 || inst > IFM_INST_MAX) + errx(1, "invalid media instance: %s", val); + + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_media = (ifmr->ifm_current & ~IFM_IMASK) | inst << IFM_ISHIFT; + + ifmr->ifm_current = ifr.ifr_media; + callback_register(setifmediacallback, (void *)ifmr); +} + +#ifdef notdef +static void +setmediamode(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifmediareq *ifmr; + int mode; + + ifmr = ifmedia_getstate(s); + + mode = get_media_mode(IFM_TYPE(ifmr->ifm_ulist[0]), val); + + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_media = (ifmr->ifm_current & ~IFM_MMASK) | mode; + + ifmr->ifm_current = ifr.ifr_media; + callback_register(setifmediacallback, (void *)ifmr); +} +#endif + +/********************************************************************** + * A good chunk of this is duplicated from sys/net/ifmedia.c + **********************************************************************/ + +static struct ifmedia_description ifm_type_descriptions[] = + IFM_TYPE_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_ethernet_descriptions[] = + IFM_SUBTYPE_ETHERNET_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_ethernet_aliases[] = + IFM_SUBTYPE_ETHERNET_ALIASES; + +static struct ifmedia_description ifm_subtype_ethernet_option_descriptions[] = + IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_tokenring_descriptions[] = + IFM_SUBTYPE_TOKENRING_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_tokenring_aliases[] = + IFM_SUBTYPE_TOKENRING_ALIASES; + +static struct ifmedia_description ifm_subtype_tokenring_option_descriptions[] = + IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_fddi_descriptions[] = + IFM_SUBTYPE_FDDI_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_fddi_aliases[] = + IFM_SUBTYPE_FDDI_ALIASES; + +static struct ifmedia_description ifm_subtype_fddi_option_descriptions[] = + IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_ieee80211_descriptions[] = + IFM_SUBTYPE_IEEE80211_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_ieee80211_option_descriptions[] = + IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS; + +#ifdef notdef +static struct ifmedia_description ifm_subtype_ieee80211_aliases[] = +IFM_SUBTYPE_IEEE80211_ALIASES; + +struct ifmedia_description ifm_subtype_ieee80211_mode_descriptions[] = + IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_ieee80211_mode_aliases[] = + IFM_SUBTYPE_IEEE80211_MODE_ALIASES; + +static struct ifmedia_description ifm_subtype_atm_descriptions[] = + IFM_SUBTYPE_ATM_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_atm_aliases[] = + IFM_SUBTYPE_ATM_ALIASES; + +static struct ifmedia_description ifm_subtype_atm_option_descriptions[] = + IFM_SUBTYPE_ATM_OPTION_DESCRIPTIONS; +#endif + +static struct ifmedia_description ifm_subtype_shared_descriptions[] = + IFM_SUBTYPE_SHARED_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_shared_aliases[] = + IFM_SUBTYPE_SHARED_ALIASES; + +static struct ifmedia_description ifm_shared_option_descriptions[] = + IFM_SHARED_OPTION_DESCRIPTIONS; + +struct ifmedia_type_to_subtype { + struct { + struct ifmedia_description *desc; + int alias; + } subtypes[5]; + struct { + struct ifmedia_description *desc; + int alias; + } options[3]; + struct { + struct ifmedia_description *desc; + int alias; + } modes[3]; +}; + +/* must be in the same order as IFM_TYPE_DESCRIPTIONS */ +static struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = { + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_ethernet_descriptions[0], 0 }, + { &ifm_subtype_ethernet_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_subtype_ethernet_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_tokenring_descriptions[0], 0 }, + { &ifm_subtype_tokenring_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_subtype_tokenring_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_fddi_descriptions[0], 0 }, + { &ifm_subtype_fddi_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_subtype_fddi_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, +#ifdef __APPLE__ + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_ieee80211_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_subtype_ieee80211_option_descriptions[0], 1 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, +#else /* __APPLE__ */ +#ifdef notdef + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_ieee80211_descriptions[0], 0 }, + { &ifm_subtype_ieee80211_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_subtype_ieee80211_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { &ifm_subtype_ieee80211_mode_descriptions[0], 0 }, + { &ifm_subtype_ieee80211_mode_aliases[0], 0 }, + { NULL, 0 }, + }, + }, + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_atm_descriptions[0], 0 }, + { &ifm_subtype_atm_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_subtype_atm_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, +#endif +#endif /* __APPLE__ */ +}; + +static int +get_media_subtype(int type, const char *val) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int rval, i; + + /* Find the top-level interface type. */ + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (type == desc->ifmt_word) + break; + if (desc->ifmt_string == NULL) + errx(1, "unknown media type 0x%x", type); + + for (i = 0; ttos->subtypes[i].desc != NULL; i++) { + rval = lookup_media_word(ttos->subtypes[i].desc, val); + if (rval != -1) + return (rval); + } + errx(1, "unknown media subtype: %s", val); + /*NOTREACHED*/ +} + +#ifdef notdef +static int +get_media_mode(int type, const char *val) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int rval, i; + + /* Find the top-level interface type. */ + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (type == desc->ifmt_word) + break; + if (desc->ifmt_string == NULL) + errx(1, "unknown media mode 0x%x", type); + + for (i = 0; ttos->modes[i].desc != NULL; i++) { + rval = lookup_media_word(ttos->modes[i].desc, val); + if (rval != -1) + return (rval); + } + return -1; +} +#endif + +static int +get_media_options(int type, const char *val) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + char *optlist, *optptr; + int option = 0, i, rval = 0; + + /* We muck with the string, so copy it. */ + optlist = strdup(val); + if (optlist == NULL) + err(1, "strdup"); + + /* Find the top-level interface type. */ + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (type == desc->ifmt_word) + break; + if (desc->ifmt_string == NULL) + errx(1, "unknown media type 0x%x", type); + + /* + * Look up the options in the user-provided comma-separated + * list. + */ + optptr = optlist; + for (; (optptr = strtok(optptr, ",")) != NULL; optptr = NULL) { + for (i = 0; ttos->options[i].desc != NULL; i++) { + option = lookup_media_word(ttos->options[i].desc, optptr); + if (option != -1) + break; + } + if (option == 0) + errx(1, "unknown option: %s", optptr); + rval |= option; + } + + free(optlist); + return (rval); +} + +static int +lookup_media_word(struct ifmedia_description *desc, const char *val) +{ + + for (; desc->ifmt_string != NULL; desc++) + if (strcasecmp(desc->ifmt_string, val) == 0) + return (desc->ifmt_word); + + return (-1); +} + +static struct ifmedia_description *get_toptype_desc(int ifmw) +{ + struct ifmedia_description *desc; + + for (desc = ifm_type_descriptions; desc->ifmt_string != NULL; desc++) + if (IFM_TYPE(ifmw) == desc->ifmt_word) + break; + + return desc; +} + +static struct ifmedia_type_to_subtype *get_toptype_ttos(int ifmw) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (IFM_TYPE(ifmw) == desc->ifmt_word) + break; + + return ttos; +} + +static struct ifmedia_description *get_subtype_desc(int ifmw, + struct ifmedia_type_to_subtype *ttos) +{ + int i; + struct ifmedia_description *desc; + + for (i = 0; ttos->subtypes[i].desc != NULL; i++) { + if (ttos->subtypes[i].alias) + continue; + for (desc = ttos->subtypes[i].desc; + desc->ifmt_string != NULL; desc++) { + if (IFM_SUBTYPE(ifmw) == desc->ifmt_word) + return desc; + } + } + + return NULL; +} + +#ifdef notdef +static struct ifmedia_description *get_mode_desc(int ifmw, + struct ifmedia_type_to_subtype *ttos) +{ + int i; + struct ifmedia_description *desc; + + for (i = 0; ttos->modes[i].desc != NULL; i++) { + if (ttos->modes[i].alias) + continue; + for (desc = ttos->modes[i].desc; + desc->ifmt_string != NULL; desc++) { + if (IFM_MODE(ifmw) == desc->ifmt_word) + return desc; + } + } + + return NULL; +} +#endif + +static void +print_media_word(int ifmw, int print_toptype) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int seen_option = 0, i; + + /* Find the top-level interface type. */ + desc = get_toptype_desc(ifmw); + ttos = get_toptype_ttos(ifmw); + if (desc->ifmt_string == NULL) { + printf(""); + return; +#ifdef notdef + } else if (print_toptype) { + printf("%s", desc->ifmt_string); +#endif + } + + /* + * Don't print the top-level type; it's not like we can + * change it, or anything. + */ + + /* Find subtype. */ + desc = get_subtype_desc(ifmw, ttos); + if (desc == NULL) { + printf(""); + return; + } + +#ifdef notdef + if (print_toptype) + putchar(' '); +#endif + + printf("%s", desc->ifmt_string); + +#ifdef notdef + if (print_toptype) { + desc = get_mode_desc(ifmw, ttos); + if (desc != NULL && strcasecmp("autoselect", desc->ifmt_string)) + printf(" mode %s", desc->ifmt_string); + } +#endif + /* Find options. */ + for (i = 0; ttos->options[i].desc != NULL; i++) { + if (ttos->options[i].alias) + continue; + for (desc = ttos->options[i].desc; + desc->ifmt_string != NULL; desc++) { + if (ifmw & desc->ifmt_word) { + if (seen_option == 0) + printf(" <"); + printf("%s%s", seen_option++ ? "," : "", + desc->ifmt_string); + } + } + } + printf("%s", seen_option ? ">" : ""); + +#ifdef notdef + if (print_toptype && IFM_INST(ifmw) != 0) + printf(" instance %d", IFM_INST(ifmw)); +#endif +} + +static void +print_media_word_ifconfig(int ifmw) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int i; + + /* Find the top-level interface type. */ + desc = get_toptype_desc(ifmw); + ttos = get_toptype_ttos(ifmw); + if (desc->ifmt_string == NULL) { + printf(""); + return; + } + + /* + * Don't print the top-level type; it's not like we can + * change it, or anything. + */ + + /* Find subtype. */ + desc = get_subtype_desc(ifmw, ttos); + if (desc == NULL) { + printf(""); + return; + } + + printf("media %s", desc->ifmt_string); + +#ifdef notdef + desc = get_mode_desc(ifmw, ttos); + if (desc != NULL) + printf(" mode %s", desc->ifmt_string); +#endif + + /* Find options. */ + for (i = 0; ttos->options[i].desc != NULL; i++) { + if (ttos->options[i].alias) + continue; + for (desc = ttos->options[i].desc; + desc->ifmt_string != NULL; desc++) { + if (ifmw & desc->ifmt_word) { + printf(" mediaopt %s", desc->ifmt_string); + } + } + } + + if (IFM_INST(ifmw) != 0) + printf(" instance %d", IFM_INST(ifmw)); +} + +/********************************************************************** + * ...until here. + **********************************************************************/ + +static struct cmd media_cmds[] = { + DEF_CMD_ARG("media", setmedia), +#ifdef notdef + DEF_CMD_ARG("mode", setmediamode), +#endif + DEF_CMD_ARG("mediaopt", setmediaopt), + DEF_CMD_ARG("-mediaopt",unsetmediaopt), + DEF_CMD_ARG("inst", setmediainst), + DEF_CMD_ARG("instance", setmediainst), +}; +static struct afswtch af_media = { + .af_name = "af_media", + .af_af = AF_UNSPEC, + .af_other_status = media_status, +}; + +static __constructor void +ifmedia_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(media_cmds); i++) + cmd_register(&media_cmds[i]); + af_register(&af_media); +#undef N +} diff --git a/network_cmds-543/ifconfig.tproj/ifvlan.c b/network_cmds-543/ifconfig.tproj/ifvlan.c new file mode 100644 index 00000000..9ec0a0ae --- /dev/null +++ b/network_cmds-543/ifconfig.tproj/ifvlan.c @@ -0,0 +1,207 @@ +/* + * Copyright (c) 1999 + * Bill Paul . 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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 +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ifconfig.h" + +#include + +#define NOTAG ((u_short) -1) + +static struct vlanreq params = { + .vlr_tag = NOTAG, +}; + +static int +getvlan(int s, struct ifreq *ifr, struct vlanreq *vreq) +{ + bzero((char *)vreq, sizeof(*vreq)); + ifr->ifr_data = (caddr_t)vreq; + + return ioctl(s, SIOCGETVLAN, (caddr_t)ifr); +} + +static void +vlan_status(int s) +{ + struct vlanreq vreq; + + if (getvlan(s, &ifr, &vreq) != -1) + printf("\tvlan: %d parent interface: %s\n", + vreq.vlr_tag, vreq.vlr_parent[0] == '\0' ? + "" : vreq.vlr_parent); +} + +static void +vlan_create(int s, struct ifreq *ifr) +{ + if (params.vlr_tag != NOTAG || params.vlr_parent[0] != '\0') { + /* + * One or both parameters were specified, make sure both. + */ + if (params.vlr_tag == NOTAG) + errx(1, "must specify a tag for vlan create"); + if (params.vlr_parent[0] == '\0') + errx(1, "must specify a parent device for vlan create"); + ifr->ifr_data = (caddr_t) ¶ms; + } +#ifdef SIOCIFCREATE2 + if (ioctl(s, SIOCIFCREATE2, ifr) < 0) + err(1, "SIOCIFCREATE2"); +#else + if (ioctl(s, SIOCIFCREATE, ifr) < 0) + err(1, "SIOCIFCREATE"); +#endif +} + +static void +vlan_cb(int s, void *arg) +{ + if ((params.vlr_tag != NOTAG) ^ (params.vlr_parent[0] != '\0')) + errx(1, "both vlan and vlandev must be specified"); +} + +static void +vlan_set(int s, struct ifreq *ifr) +{ + if (params.vlr_tag != NOTAG && params.vlr_parent[0] != '\0') { + ifr->ifr_data = (caddr_t) ¶ms; + if (ioctl(s, SIOCSETVLAN, (caddr_t)ifr) == -1) + err(1, "SIOCSETVLAN"); + } +} + +static +DECL_CMD_FUNC(setvlantag, val, d) +{ + struct vlanreq vreq; + u_long ul; + char *endp; + + ul = strtoul(val, &endp, 0); + if (*endp != '\0') + errx(1, "invalid value for vlan"); + params.vlr_tag = ul; + /* check if the value can be represented in vlr_tag */ + if (params.vlr_tag != ul) + errx(1, "value for vlan out of range"); + + if (getvlan(s, &ifr, &vreq) != -1) + vlan_set(s, &ifr); + else + clone_setcallback(vlan_create); +} + +static +DECL_CMD_FUNC(setvlandev, val, d) +{ + struct vlanreq vreq; + + strlcpy(params.vlr_parent, val, sizeof(params.vlr_parent)); + + if (getvlan(s, &ifr, &vreq) != -1) + vlan_set(s, &ifr); + else + clone_setcallback(vlan_create); +} + +static +DECL_CMD_FUNC(unsetvlandev, val, d) +{ + struct vlanreq vreq; + + bzero((char *)&vreq, sizeof(struct vlanreq)); + ifr.ifr_data = (caddr_t)&vreq; + + if (ioctl(s, SIOCGETVLAN, (caddr_t)&ifr) == -1) + err(1, "SIOCGETVLAN"); + + bzero((char *)&vreq.vlr_parent, sizeof(vreq.vlr_parent)); + vreq.vlr_tag = 0; + + if (ioctl(s, SIOCSETVLAN, (caddr_t)&ifr) == -1) + err(1, "SIOCSETVLAN"); +} + +static struct cmd vlan_cmds[] = { + DEF_CLONE_CMD_ARG("vlan", setvlantag), + DEF_CLONE_CMD_ARG("vlandev", setvlandev), + /* XXX For compatibility. Should become DEF_CMD() some day. */ + DEF_CMD_OPTARG("-vlandev", unsetvlandev), +#ifdef IFCAP_VLAN_MTU + DEF_CMD("vlanmtu", IFCAP_VLAN_MTU, setifcap), + DEF_CMD("-vlanmtu", -IFCAP_VLAN_MTU, setifcap), +#endif /* IFCAP_VLAN_MTU */ +#ifdef IFCAP_VLAN_HWTAGGING + DEF_CMD("vlanhwtag", IFCAP_VLAN_HWTAGGING, setifcap), + DEF_CMD("-vlanhwtag", -IFCAP_VLAN_HWTAGGING, setifcap), +#endif /* IFCAP_VLAN_HWTAGGING */ +}; +static struct afswtch af_vlan = { + .af_name = "af_vlan", + .af_af = AF_UNSPEC, + .af_other_status = vlan_status, +}; + +static __constructor void +vlan_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(vlan_cmds); i++) + cmd_register(&vlan_cmds[i]); + af_register(&af_vlan); + callback_register(vlan_cb, NULL); +#undef N +} diff --git a/network_cmds-543/ifconfig.tproj/nexus.c b/network_cmds-543/ifconfig.tproj/nexus.c new file mode 100644 index 00000000..692e2111 --- /dev/null +++ b/network_cmds-543/ifconfig.tproj/nexus.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2017 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * nexus.c + * - report information about attached nexus + */ + +/* + * Modification History: + * + * April 10, 2017 Dieter Siegmund (dieter@apple.com) + * - created + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ifconfig.h" + +static void +nexus_status(int s) +{ + struct if_nexusreq ifnr; + uuid_string_t multistack; + uuid_string_t netif; + + if (!verbose) { + return; + } + bzero((char *)&ifnr, sizeof(ifnr)); + strlcpy(ifnr.ifnr_name, ifr.ifr_name, sizeof(ifnr.ifnr_name)); + if (ioctl(s, SIOCGIFNEXUS, &ifnr) < 0) { + return; + } + if (uuid_is_null(ifnr.ifnr_netif)) { + /* technically, this shouldn't happen */ + return; + } + uuid_unparse_upper(ifnr.ifnr_netif, netif); + printf("\tnetif: %s\n", netif); + if (uuid_is_null(ifnr.ifnr_multistack) == 0) { + uuid_unparse_upper(ifnr.ifnr_multistack, multistack); + printf("\tmultistack: %s\n", multistack); + } + return; +} + +static struct afswtch af_fake = { + .af_name = "af_fake", + .af_af = AF_UNSPEC, + .af_other_status = nexus_status, +}; + +static __constructor void +fake_ctor(void) +{ + af_register(&af_fake); +} + diff --git a/network_cmds-543/ip6addrctl.tproj/ip6addrctl.8 b/network_cmds-543/ip6addrctl.tproj/ip6addrctl.8 new file mode 100644 index 00000000..59154d44 --- /dev/null +++ b/network_cmds-543/ip6addrctl.tproj/ip6addrctl.8 @@ -0,0 +1,126 @@ +.\" $KAME: ip6addrctl.8,v 1.3 2003/03/22 05:56:41 jinmei Exp $ +.\" +.\" Copyright (C) 2001 WIDE Project. +.\" 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. +.\" 3. Neither the name of the project 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 PROJECT 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 PROJECT 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. +.\" +.\" $FreeBSD$ +.\" +.Dd September 25, 2001 +.Dt IP6ADDRCTL 8 +.Os +.\" +.Sh NAME +.Nm ip6addrctl +.Nd configure address selection policy for IPv6 and IPv4 +.\" +.Sh SYNOPSIS +.Nm +.Op Cm show +.Nm +.Cm add +.Ar prefix precedence label +.Nm +.Cm delete +.Ar prefix +.Nm +.Cm flush +.Nm +.Cm install +.Ar configfile +.\" +.Sh DESCRIPTION +The +.Nm +utility manages the policy table of source and destination address +selection for outgoing IPv4 and IPv6 packets. +When +.Nm +is invoked without an argument or with a single argument +.Cm show , +it prints the content of the policy table currently installed in the +kernel. +.Pp +To modify the table, the following operations are available: +.Bl -tag -width indent +.It Cm add Ar prefix precedence label +Add a policy entry. +The +.Ar prefix +argument +is an IPv6 prefix, which is a key for the entry. +An IPv4 prefix should be specified with an IPv6 prefix using an +IPv4-mapped IPv6 address. +The +.Ar precedence +and +.Ar label +arguments +are decimal numbers, which specify the precedence and label values +for the entry, respectively. +This operation should be performed without an existing entry for the +prefix. +.It Cm delete Ar prefix +Delete a policy entry specified by +.Ar prefix , +which should be an IPv6 prefix. +A corresponding entry for the prefix should have already been +installed. +.It Cm flush +Delete all existing policy entries in the kernel. +.It Cm install Ar configfile +Install policy entries from a configuration file named +.Ar configfile . +The configuration file should contain a set of policy entries. +Each entry is specified in a single line which contains an IPv6 prefix, +a decimal precedence value, and a decimal label value, separated with +white space or tab characters. +In the configuration file, lines beginning with the pound-sign +.Pq Ql # +are +comments and are ignored. +.El +.\" +.Sh EXIT STATUS +.Ex -std +.\" +.Sh SEE ALSO +.Rs +.%A "Richard Draves" +.%T "Default Address Selection for IPv6" +.%N RFC 3484 +.Re +.\" +.Sh HISTORY +The +.Nm +utility first appeared in the KAME IPv6 protocol stack kit. +The original command name was +.Nm addrselect , +but it was then renamed to the current one so that the name would +describe its function well. +.\" .Sh BUGS +.\" (to be written) diff --git a/network_cmds-543/ip6addrctl.tproj/ip6addrctl.c b/network_cmds-543/ip6addrctl.tproj/ip6addrctl.c new file mode 100644 index 00000000..400412a6 --- /dev/null +++ b/network_cmds-543/ip6addrctl.tproj/ip6addrctl.c @@ -0,0 +1,467 @@ +/* $KAME: ip6addrctl.c,v 1.3 2003/12/16 08:14:28 suz Exp $ */ + +/* + * Copyright (C) 2001 WIDE Project. + * 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. + * 3. Neither the name of the project 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 PROJECT 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 PROJECT 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static char *configfile; + +struct policyqueue { + TAILQ_ENTRY(policyqueue) pc_entry; + struct in6_addrpolicy pc_policy; +}; +TAILQ_HEAD(policyhead, policyqueue); +struct policyhead policyhead; + +static void usage __P((void)); +static void get_policy __P((void)); +static void dump_policy __P((void)); +static int mask2plen __P((struct sockaddr_in6 *)); +static int parse_prefix __P((const char *, struct in6_addrpolicy *)); +static void make_policy_fromfile __P((char *)); +static void plen2mask __P((struct sockaddr_in6 *, int)); +static void set_policy __P((void)); +static void add_policy __P((char *, char *, char *)); +static void delete_policy __P((char *)); +static void flush_policy __P(()); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + TAILQ_INIT(&policyhead); + + if (argc == 1 || strcasecmp(argv[1], "show") == 0) { + get_policy(); + dump_policy(); + } else if (strcasecmp(argv[1], "add") == 0) { + if (argc < 5) + usage(); + add_policy(argv[2], argv[3], argv[4]); + } else if (strcasecmp(argv[1], "delete") == 0) { + if (argc < 3) + usage(); + delete_policy(argv[2]); + } else if (strcasecmp(argv[1], "flush") == 0) { + get_policy(); + flush_policy(); + } else if (strcasecmp(argv[1], "install") == 0) { + if (argc < 3) + usage(); + configfile = argv[2]; + make_policy_fromfile(configfile); + set_policy(); + } else + usage(); + + exit(0); +} + +static void +get_policy() +{ + int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_ADDRCTLPOLICY }; + size_t l; + char *buf; + struct in6_addrpolicy *pol, *ep; + + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) { + err(1, "sysctl(IPV6CTL_ADDRCTLPOLICY)"); + /* NOTREACHED */ + } + if (l == 0) { + printf("no source-address-selection policy is installed\n"); + return; + } + if ((buf = malloc(l)) == NULL) { + errx(1, "malloc failed"); + /* NOTREACHED */ + } + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) { + err(1, "sysctl(IPV6CTL_ADDRCTLPOLICY)"); + /* NOTREACHED */ + } + + ep = (struct in6_addrpolicy *)(buf + l); + for (pol = (struct in6_addrpolicy *)buf; pol + 1 <= ep; pol++) { + struct policyqueue *new; + + if ((new = malloc(sizeof(*new))) == NULL) + errx(1, "malloc failed\n"); + new->pc_policy = *pol; + TAILQ_INSERT_TAIL(&policyhead, new, pc_entry); + } + + free(buf); +} + +static void +dump_policy() +{ + size_t addrlen; + char addrbuf[NI_MAXHOST]; + struct in6_addrpolicy *pol; + struct policyqueue *ent; + int plen, first = 1; + + for (ent = TAILQ_FIRST(&policyhead); ent; + ent = TAILQ_NEXT(ent, pc_entry)) { + pol = &ent->pc_policy; + if (first) { + printf("%-30s %5s %5s %8s\n", + "Prefix", "Prec", "Label", "Use"); + first = 0; + } + + if ((getnameinfo((struct sockaddr *)&pol->addr, + sizeof(pol->addr), addrbuf, sizeof(addrbuf), + NULL, 0, NI_NUMERICHOST))) { + warnx("getnameinfo for prefix address failed"); + continue; + } + if ((plen = mask2plen(&pol->addrmask)) < 0) { + warnx("invalid address mask"); + continue; + } + addrlen = strlen(addrbuf); + if (addrlen + sizeof("/128") < sizeof(addrbuf)) { + snprintf(&addrbuf[addrlen], + sizeof(addrbuf) - addrlen - 1, + "/%d", plen); + printf("%-30s", addrbuf); + } else /* XXX */ + printf("%s/%d", addrbuf, plen); + printf(" %5d %5d %8llu\n", pol->preced, pol->label, + (unsigned long long)pol->use); + } +} + +#define SKIP_WHITE(p, emptyok) \ + do { \ + while((*(p) == ' ' || *(p) == '\t')) \ + (p)++; \ + if ((*(p) == '\0' || (*(p) == '\n')) && !(emptyok)) \ + goto bad; \ + } while (0); +#define SKIP_WORD(p) \ + do { \ + while(*(p) != ' ' && *(p) != '\t') \ + (p)++; \ + if (*(p) == '\0' || *(p) == '\n') \ + goto bad; \ + } while (0); + +static void +make_policy_fromfile(conf) + char *conf; +{ + char line[_POSIX2_LINE_MAX], *cp; + char *addrstr; + FILE *fp; + int count = 0; + struct in6_addrpolicy pol0; + struct policyqueue *new; + + if ((fp = fopen(conf, "r")) == NULL) + err(1, "fopen: %s", conf); + + while(fgets(line, sizeof(line), fp)) { + count++; + cp = line; + + memset(&pol0, 0, sizeof(pol0)); + + /* get prefix */ + SKIP_WHITE(cp, 1); + if (*cp == '\n') /* empty line */ + continue; + if (*cp == '#') + continue; + addrstr = cp; + if (parse_prefix((const char *)addrstr, &pol0)) + goto bad; + + /* get precedence value */ + SKIP_WORD(cp); + SKIP_WHITE(cp, 0); + pol0.preced = atoi(cp); + + /* get label */ + SKIP_WORD(cp); + SKIP_WHITE(cp, 0); + pol0.label = atoi(cp); + + /* parse succeeded. make a control buffer entry. */ + if ((new = malloc(sizeof(*new))) == NULL) + errx(1, "malloc failed\n"); + memset(new, 0, sizeof(*new)); + new->pc_policy = pol0; + TAILQ_INSERT_TAIL(&policyhead, new, pc_entry); + } + + fclose(fp); + return; + + bad: + errx(1, "parse failed at line %d", count); + /* NOTREACHED */ +} + +static int +parse_prefix(prefix0, pol) + const char *prefix0; + struct in6_addrpolicy *pol; +{ + int e = 0, plen; + char *prefix, *plenstr; + struct addrinfo hints, *res; + + if ((prefix = strdup(prefix0)) == NULL) + errx(1, "strdup failed"); + + if ((plenstr = strchr(prefix, '/')) == NULL) { + e = -1; + goto end; + } + *plenstr = '\0'; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = AF_INET6; + + if ((e = getaddrinfo(prefix, NULL, &hints, &res)) != 0) { + warnx("getaddrinfo failed for %s: %s", prefix, + gai_strerror(e)); + goto end; + } + memcpy(&pol->addr, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + plen = atoi(plenstr + 1); + if (plen < 0 || plen > 128) { + warnx("invalid prefix length: %d", plen); + e = -1; + goto end; + } + plen2mask(&pol->addrmask, plen); + + end: + free(prefix); + return(e); +} + +static void +plen2mask(mask, plen) + struct sockaddr_in6 *mask; + int plen; +{ + u_char *cp = (unsigned char *)&mask->sin6_addr; + + memset(mask, 0, sizeof(*mask)); + mask->sin6_family = AF_INET6; /* just in case */ + mask->sin6_len = sizeof(*mask); + + for(; plen >= 8; plen -= 8) + *cp++ = 0xff; + if (plen > 0) + *cp = (0xff << (8 - plen)); +} + +static void +set_policy() +{ + struct policyqueue *ent; + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) + err(1, "socket(UDP)"); + + for (ent = TAILQ_FIRST(&policyhead); ent; + ent = TAILQ_NEXT(ent, pc_entry)) { + if (ioctl(s, SIOCAADDRCTL_POLICY, &ent->pc_policy)) + warn("ioctl(SIOCAADDRCTL_POLICY)"); + } + + close(s); +} + +static int +mask2plen(mask) + struct sockaddr_in6 *mask; +{ + int masklen, final = 0; + u_char *p, *lim; + + masklen = 0; + lim = (u_char *)(mask + 1); + for (p = (u_char *)(&mask->sin6_addr); p < lim; p++) { + if (final && *p) { + goto bad; + } + + switch (*p & 0xff) { + case 0xff: + masklen += 8; + break; + case 0xfe: + masklen += 7; + final++; + break; + case 0xfc: + masklen += 6; + final++; + break; + case 0xf8: + masklen += 5; + final++; + break; + case 0xf0: + masklen += 4; + final++; + break; + case 0xe0: + masklen += 3; + final++; + break; + case 0xc0: + masklen += 2; + final++; + break; + case 0x80: + masklen += 1; + final++; + break; + case 0x00: + final++; + break; + default: + goto bad; + break; + } + } + return(masklen); + + bad: + return(-1); +} + +static void +add_policy(prefix, prec, label) + char *prefix, *prec, *label; +{ + struct in6_addrpolicy p; + int s; + + memset(&p, 0, sizeof(p)); + + if (parse_prefix((const char *)prefix, &p)) + errx(1, "bad prefix: %s", prefix); + p.preced = atoi(prec); + p.label = atoi(label); + + if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) + err(1, "socket(UDP)"); + if (ioctl(s, SIOCAADDRCTL_POLICY, &p)) + err(1, "ioctl(SIOCAADDRCTL_POLICY)"); + + close(s); +} + +static void +delete_policy(prefix) + char *prefix; +{ + struct in6_addrpolicy p; + int s; + + memset(&p, 0, sizeof(p)); + + if (parse_prefix((const char *)prefix, &p)) + errx(1, "bad prefix: %s", prefix); + + if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) + err(1, "socket(UDP)"); + if (ioctl(s, SIOCDADDRCTL_POLICY, &p)) + err(1, "ioctl(SIOCDADDRCTL_POLICY)"); + + close(s); +} + +static void +flush_policy() +{ + struct policyqueue *ent; + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) + err(1, "socket(UDP)"); + + for (ent = TAILQ_FIRST(&policyhead); ent; + ent = TAILQ_NEXT(ent, pc_entry)) { + if (ioctl(s, SIOCDADDRCTL_POLICY, &ent->pc_policy)) + warn("ioctl(SIOCDADDRCTL_POLICY)"); + } + + close(s); +} + +static void +usage() +{ + fprintf(stderr, "usage: ip6addrctl [show]\n"); + fprintf(stderr, " ip6addrctl add " + "